Date picker for Date and DateTime field input (#4981)
- Implemented correct mask for Date and DateTime field in InternalDatePicker - Use only keyDown event and click outside in InternalDatePicker and DateInput - Refactored InternalDatePicker UI to have month and year displayed - Fixed bug and synchronized date value between the different inputs that can change it --------- Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com> Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Matheus <matheus_benini@hotmail.com>
This commit is contained in:
@ -144,7 +144,7 @@
|
|||||||
"qs": "^6.11.2",
|
"qs": "^6.11.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-data-grid": "7.0.0-beta.13",
|
"react-data-grid": "7.0.0-beta.13",
|
||||||
"react-datepicker": "^4.11.0",
|
"react-datepicker": "^6.7.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-error-boundary": "^4.0.11",
|
"react-error-boundary": "^4.0.11",
|
||||||
@ -252,7 +252,7 @@
|
|||||||
"@types/passport-google-oauth20": "^2.0.11",
|
"@types/passport-google-oauth20": "^2.0.11",
|
||||||
"@types/passport-jwt": "^3.0.8",
|
"@types/passport-jwt": "^3.0.8",
|
||||||
"@types/react": "^18.2.39",
|
"@types/react": "^18.2.39",
|
||||||
"@types/react-datepicker": "^4.11.2",
|
"@types/react-datepicker": "^6.2.0",
|
||||||
"@types/react-dom": "^18.2.15",
|
"@types/react-dom": "^18.2.15",
|
||||||
"@types/scroll-into-view": "^1.16.0",
|
"@types/scroll-into-view": "^1.16.0",
|
||||||
"@types/supertest": "^2.0.11",
|
"@types/supertest": "^2.0.11",
|
||||||
|
|||||||
@ -106,12 +106,14 @@ export const FieldInput = ({
|
|||||||
onEnter={onEnter}
|
onEnter={onEnter}
|
||||||
onEscape={onEscape}
|
onEscape={onEscape}
|
||||||
onClickOutside={onClickOutside}
|
onClickOutside={onClickOutside}
|
||||||
|
onClear={onSubmit}
|
||||||
/>
|
/>
|
||||||
) : isFieldDate(fieldDefinition) ? (
|
) : isFieldDate(fieldDefinition) ? (
|
||||||
<DateFieldInput
|
<DateFieldInput
|
||||||
onEnter={onEnter}
|
onEnter={onEnter}
|
||||||
onEscape={onEscape}
|
onEscape={onEscape}
|
||||||
onClickOutside={onClickOutside}
|
onClickOutside={onClickOutside}
|
||||||
|
onClear={onSubmit}
|
||||||
/>
|
/>
|
||||||
) : isFieldNumber(fieldDefinition) ? (
|
) : isFieldNumber(fieldDefinition) ? (
|
||||||
<NumberFieldInput
|
<NumberFieldInput
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
import { isFieldDateTime } from '@/object-record/record-field/types/guards/isFieldDateTime';
|
|
||||||
|
|
||||||
import { FieldContext } from '../contexts/FieldContext';
|
import { FieldContext } from '../contexts/FieldContext';
|
||||||
|
|
||||||
// TODO: have a better clearable settings in metadata ?
|
// TODO: have a better clearable settings in metadata ?
|
||||||
@ -9,13 +7,9 @@ import { FieldContext } from '../contexts/FieldContext';
|
|||||||
// Instead of passing it in the context
|
// Instead of passing it in the context
|
||||||
// See: https://github.com/twentyhq/twenty/issues/4403
|
// See: https://github.com/twentyhq/twenty/issues/4403
|
||||||
export const useIsFieldClearable = (): boolean => {
|
export const useIsFieldClearable = (): boolean => {
|
||||||
const { clearable, isLabelIdentifier, fieldDefinition } =
|
const { clearable, isLabelIdentifier } = useContext(FieldContext);
|
||||||
useContext(FieldContext);
|
|
||||||
|
|
||||||
const isDateField = isFieldDateTime(fieldDefinition);
|
const fieldCanBeCleared = !isLabelIdentifier && clearable !== false;
|
||||||
|
|
||||||
const fieldCanBeCleared =
|
|
||||||
!isLabelIdentifier && !isDateField && clearable !== false;
|
|
||||||
|
|
||||||
return fieldCanBeCleared;
|
return fieldCanBeCleared;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { DateDisplay } from '@/ui/field/display/components/DateDisplay';
|
import { DateTimeDisplay } from '@/ui/field/display/components/DateTimeDisplay';
|
||||||
|
|
||||||
import { useDateTimeField } from '../../hooks/useDateTimeField';
|
import { useDateTimeField } from '../../hooks/useDateTimeField';
|
||||||
|
|
||||||
export const DateTimeFieldDisplay = () => {
|
export const DateTimeFieldDisplay = () => {
|
||||||
const { fieldValue } = useDateTimeField();
|
const { fieldValue } = useDateTimeField();
|
||||||
|
|
||||||
return <DateDisplay value={fieldValue} />;
|
return <DateTimeDisplay value={fieldValue} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Nullable } from 'twenty-ui';
|
|||||||
|
|
||||||
import { useDateField } from '@/object-record/record-field/meta-types/hooks/useDateField';
|
import { useDateField } from '@/object-record/record-field/meta-types/hooks/useDateField';
|
||||||
import { DateInput } from '@/ui/field/input/components/DateInput';
|
import { DateInput } from '@/ui/field/input/components/DateInput';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
import { usePersistField } from '../../../hooks/usePersistField';
|
import { usePersistField } from '../../../hooks/usePersistField';
|
||||||
|
|
||||||
@ -11,19 +12,21 @@ export type DateFieldInputProps = {
|
|||||||
onClickOutside?: FieldInputEvent;
|
onClickOutside?: FieldInputEvent;
|
||||||
onEnter?: FieldInputEvent;
|
onEnter?: FieldInputEvent;
|
||||||
onEscape?: FieldInputEvent;
|
onEscape?: FieldInputEvent;
|
||||||
|
onClear?: FieldInputEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateFieldInput = ({
|
export const DateFieldInput = ({
|
||||||
onEnter,
|
onEnter,
|
||||||
onEscape,
|
onEscape,
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
|
onClear,
|
||||||
}: DateFieldInputProps) => {
|
}: DateFieldInputProps) => {
|
||||||
const { fieldValue, hotkeyScope, setDraftValue } = useDateField();
|
const { fieldValue, setDraftValue } = useDateField();
|
||||||
|
|
||||||
const persistField = usePersistField();
|
const persistField = usePersistField();
|
||||||
|
|
||||||
const persistDate = (newDate: Nullable<Date>) => {
|
const persistDate = (newDate: Nullable<Date>) => {
|
||||||
if (!newDate) {
|
if (!isDefined(newDate)) {
|
||||||
persistField(null);
|
persistField(null);
|
||||||
} else {
|
} else {
|
||||||
const newDateISO = newDate?.toISOString();
|
const newDateISO = newDate?.toISOString();
|
||||||
@ -51,17 +54,21 @@ export const DateFieldInput = ({
|
|||||||
setDraftValue(newDate?.toDateString() ?? '');
|
setDraftValue(newDate?.toDateString() ?? '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClear = () => {
|
||||||
|
onClear?.(() => persistDate(null));
|
||||||
|
};
|
||||||
|
|
||||||
const dateValue = fieldValue ? new Date(fieldValue) : null;
|
const dateValue = fieldValue ? new Date(fieldValue) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DateInput
|
<DateInput
|
||||||
hotkeyScope={hotkeyScope}
|
|
||||||
onClickOutside={handleClickOutside}
|
onClickOutside={handleClickOutside}
|
||||||
onEnter={handleEnter}
|
onEnter={handleEnter}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
value={dateValue}
|
value={dateValue}
|
||||||
clearable
|
clearable
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onClear={handleClear}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,15 +11,16 @@ export type DateTimeFieldInputProps = {
|
|||||||
onClickOutside?: FieldInputEvent;
|
onClickOutside?: FieldInputEvent;
|
||||||
onEnter?: FieldInputEvent;
|
onEnter?: FieldInputEvent;
|
||||||
onEscape?: FieldInputEvent;
|
onEscape?: FieldInputEvent;
|
||||||
|
onClear?: FieldInputEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateTimeFieldInput = ({
|
export const DateTimeFieldInput = ({
|
||||||
onEnter,
|
onEnter,
|
||||||
onEscape,
|
onEscape,
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
|
onClear,
|
||||||
}: DateTimeFieldInputProps) => {
|
}: DateTimeFieldInputProps) => {
|
||||||
const { fieldValue, hotkeyScope, clearable, setDraftValue } =
|
const { fieldValue, setDraftValue } = useDateTimeField();
|
||||||
useDateTimeField();
|
|
||||||
|
|
||||||
const persistField = usePersistField();
|
const persistField = usePersistField();
|
||||||
|
|
||||||
@ -52,18 +53,22 @@ export const DateTimeFieldInput = ({
|
|||||||
setDraftValue(newDate?.toDateString() ?? '');
|
setDraftValue(newDate?.toDateString() ?? '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClear = () => {
|
||||||
|
onClear?.(() => persistDate(null));
|
||||||
|
};
|
||||||
|
|
||||||
const dateValue = fieldValue ? new Date(fieldValue) : null;
|
const dateValue = fieldValue ? new Date(fieldValue) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DateInput
|
<DateInput
|
||||||
hotkeyScope={hotkeyScope}
|
|
||||||
onClickOutside={handleClickOutside}
|
onClickOutside={handleClickOutside}
|
||||||
onEnter={handleEnter}
|
onEnter={handleEnter}
|
||||||
onEscape={handleEscape}
|
onEscape={handleEscape}
|
||||||
value={dateValue}
|
value={dateValue}
|
||||||
clearable={clearable}
|
clearable
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
isDateTimeInput
|
isDateTimeInput
|
||||||
|
onClear={handleClear}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { formatToHumanReadableDateTime } from '~/utils';
|
||||||
|
|
||||||
|
import { EllipsisDisplay } from './EllipsisDisplay';
|
||||||
|
|
||||||
|
type DateTimeDisplayProps = {
|
||||||
|
value: Date | string | null | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DateTimeDisplay = ({ value }: DateTimeDisplayProps) => (
|
||||||
|
<EllipsisDisplay>
|
||||||
|
{value && formatToHumanReadableDateTime(value)}
|
||||||
|
</EllipsisDisplay>
|
||||||
|
);
|
||||||
@ -4,9 +4,15 @@ import styled from '@emotion/styled';
|
|||||||
import { flip, offset, useFloating } from '@floating-ui/react';
|
import { flip, offset, useFloating } from '@floating-ui/react';
|
||||||
import { Nullable } from 'twenty-ui';
|
import { Nullable } from 'twenty-ui';
|
||||||
|
|
||||||
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
|
|
||||||
import { DateDisplay } from '@/ui/field/display/components/DateDisplay';
|
import { DateDisplay } from '@/ui/field/display/components/DateDisplay';
|
||||||
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
|
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
|
||||||
|
import {
|
||||||
|
MONTH_AND_YEAR_DROPDOWN_ID,
|
||||||
|
MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID,
|
||||||
|
MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID,
|
||||||
|
} from '@/ui/input/components/internal/date/components/MonthAndYearDropdown';
|
||||||
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
|
import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
|
||||||
|
|
||||||
const StyledCalendarContainer = styled.div`
|
const StyledCalendarContainer = styled.div`
|
||||||
background: ${({ theme }) => theme.background.secondary};
|
background: ${({ theme }) => theme.background.secondary};
|
||||||
@ -33,21 +39,21 @@ export type DateInputProps = {
|
|||||||
event: MouseEvent | TouchEvent,
|
event: MouseEvent | TouchEvent,
|
||||||
newDate: Nullable<Date>,
|
newDate: Nullable<Date>,
|
||||||
) => void;
|
) => void;
|
||||||
hotkeyScope: string;
|
|
||||||
clearable?: boolean;
|
clearable?: boolean;
|
||||||
onChange?: (newDate: Nullable<Date>) => void;
|
onChange?: (newDate: Nullable<Date>) => void;
|
||||||
isDateTimeInput?: boolean;
|
isDateTimeInput?: boolean;
|
||||||
|
onClear?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateInput = ({
|
export const DateInput = ({
|
||||||
value,
|
value,
|
||||||
onEnter,
|
onEnter,
|
||||||
onEscape,
|
onEscape,
|
||||||
hotkeyScope,
|
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
clearable,
|
clearable,
|
||||||
onChange,
|
onChange,
|
||||||
isDateTimeInput,
|
isDateTimeInput,
|
||||||
|
onClear,
|
||||||
}: DateInputProps) => {
|
}: DateInputProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@ -70,13 +76,30 @@ export const DateInput = ({
|
|||||||
onChange?.(newDate);
|
onChange?.(newDate);
|
||||||
};
|
};
|
||||||
|
|
||||||
useRegisterInputEvents({
|
const handleClear = () => {
|
||||||
inputRef: wrapperRef,
|
setInternalValue(null);
|
||||||
inputValue: internalValue,
|
onClear?.();
|
||||||
onEnter,
|
};
|
||||||
onEscape,
|
|
||||||
onClickOutside,
|
const { closeDropdown } = useDropdown(MONTH_AND_YEAR_DROPDOWN_ID);
|
||||||
hotkeyScope,
|
const { closeDropdown: closeDropdownMonthSelect } = useDropdown(
|
||||||
|
MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID,
|
||||||
|
);
|
||||||
|
const { closeDropdown: closeDropdownYearSelect } = useDropdown(
|
||||||
|
MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID,
|
||||||
|
);
|
||||||
|
|
||||||
|
useListenClickOutsideV2({
|
||||||
|
refs: [wrapperRef],
|
||||||
|
listenerId: 'DateInput',
|
||||||
|
callback: (event) => {
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
|
closeDropdownYearSelect();
|
||||||
|
closeDropdownMonthSelect();
|
||||||
|
closeDropdown();
|
||||||
|
onClickOutside(event, internalValue);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -96,7 +119,9 @@ export const DateInput = ({
|
|||||||
}}
|
}}
|
||||||
clearable={clearable ? clearable : false}
|
clearable={clearable ? clearable : false}
|
||||||
isDateTimeInput={isDateTimeInput}
|
isDateTimeInput={isDateTimeInput}
|
||||||
onClickOutside={onClickOutside}
|
onEnter={onEnter}
|
||||||
|
onEscape={onEscape}
|
||||||
|
onClear={handleClear}
|
||||||
/>
|
/>
|
||||||
</StyledCalendarContainer>
|
</StyledCalendarContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -16,6 +16,7 @@ export type LightIconButtonGroupProps = Pick<
|
|||||||
iconButtons: {
|
iconButtons: {
|
||||||
Icon: IconComponent;
|
Icon: IconComponent;
|
||||||
onClick?: (event: MouseEvent<any>) => void;
|
onClick?: (event: MouseEvent<any>) => void;
|
||||||
|
disabled?: boolean;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useIMask } from 'react-imask';
|
import { useIMask } from 'react-imask';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
import { DATE_BLOCKS } from '@/ui/input/components/internal/date/constants/DateBlocks';
|
||||||
|
import { DATE_MASK } from '@/ui/input/components/internal/date/constants/DateMask';
|
||||||
import { DATE_TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/DateTimeBlocks';
|
import { DATE_TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/DateTimeBlocks';
|
||||||
import { DATE_TIME_MASK } from '@/ui/input/components/internal/date/constants/DateTimeMask';
|
import { DATE_TIME_MASK } from '@/ui/input/components/internal/date/constants/DateTimeMask';
|
||||||
|
import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate';
|
||||||
|
import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate';
|
||||||
|
|
||||||
const StyledInputContainer = styled.div`
|
const StyledInputContainer = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -37,20 +41,27 @@ export const DateTimeInput = ({
|
|||||||
onChange,
|
onChange,
|
||||||
isDateTimeInput,
|
isDateTimeInput,
|
||||||
}: DateTimeInputProps) => {
|
}: DateTimeInputProps) => {
|
||||||
|
const parsingFormat = isDateTimeInput ? 'MM/dd/yyyy HH:mm' : 'MM/dd/yyyy';
|
||||||
|
|
||||||
const [hasError, setHasError] = useState(false);
|
const [hasError, setHasError] = useState(false);
|
||||||
|
|
||||||
const parseDateToString = (date: any) => {
|
const parseDateToString = useCallback(
|
||||||
const dateParsed = DateTime.fromJSDate(date);
|
(date: any) => {
|
||||||
|
const dateParsed = DateTime.fromJSDate(date);
|
||||||
|
|
||||||
const formattedDate = dateParsed.toFormat('MM/dd/yyyy HH:mm');
|
const formattedDate = dateParsed.toFormat(parsingFormat);
|
||||||
|
|
||||||
return formattedDate;
|
return formattedDate;
|
||||||
};
|
},
|
||||||
|
[parsingFormat],
|
||||||
|
);
|
||||||
|
|
||||||
const parseStringToDate = (str: string) => {
|
const parseStringToDate = (str: string) => {
|
||||||
setHasError(false);
|
setHasError(false);
|
||||||
|
|
||||||
const parsedDate = DateTime.fromFormat(str, 'MM/dd/yyyy HH:mm');
|
const parsedDate = isDateTimeInput
|
||||||
|
? DateTime.fromFormat(str, parsingFormat)
|
||||||
|
: DateTime.fromFormat(str, parsingFormat, { zone: 'utc' });
|
||||||
|
|
||||||
const isValid = parsedDate.isValid;
|
const isValid = parsedDate.isValid;
|
||||||
|
|
||||||
@ -65,13 +76,16 @@ export const DateTimeInput = ({
|
|||||||
return jsDate;
|
return jsDate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pattern = isDateTimeInput ? DATE_TIME_MASK : DATE_MASK;
|
||||||
|
const blocks = isDateTimeInput ? DATE_TIME_BLOCKS : DATE_BLOCKS;
|
||||||
|
|
||||||
const { ref, setValue, value } = useIMask(
|
const { ref, setValue, value } = useIMask(
|
||||||
{
|
{
|
||||||
mask: Date,
|
mask: Date,
|
||||||
pattern: DATE_TIME_MASK,
|
pattern,
|
||||||
blocks: DATE_TIME_BLOCKS,
|
blocks,
|
||||||
min: new Date(1970, 0, 1),
|
min: MIN_DATE,
|
||||||
max: new Date(2100, 0, 1),
|
max: MAX_DATE,
|
||||||
format: parseDateToString,
|
format: parseDateToString,
|
||||||
parse: parseStringToDate,
|
parse: parseStringToDate,
|
||||||
lazy: false,
|
lazy: false,
|
||||||
@ -91,7 +105,7 @@ export const DateTimeInput = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValue(parseDateToString(date));
|
setValue(parseDateToString(date));
|
||||||
}, [date, setValue]);
|
}, [date, setValue, parseDateToString]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledInputContainer>
|
<StyledInputContainer>
|
||||||
@ -102,6 +116,7 @@ export const DateTimeInput = ({
|
|||||||
isDateTimeInput ? ' and time' : ' (mm/dd/yyyy)'
|
isDateTimeInput ? ' and time' : ' (mm/dd/yyyy)'
|
||||||
}`}
|
}`}
|
||||||
value={value}
|
value={value}
|
||||||
|
onChange={() => {}} // Prevent React warning
|
||||||
hasError={hasError}
|
hasError={hasError}
|
||||||
/>
|
/>
|
||||||
</StyledInputContainer>
|
</StyledInputContainer>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import ReactDatePicker from 'react-datepicker';
|
import ReactDatePicker from 'react-datepicker';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import { Key } from 'ts-key-enum';
|
||||||
import { IconCalendarX, IconChevronLeft, IconChevronRight } from 'twenty-ui';
|
import { IconCalendarX, IconChevronLeft, IconChevronRight } from 'twenty-ui';
|
||||||
|
|
||||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||||
@ -16,6 +17,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
|||||||
import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent';
|
import { MenuItemLeftContent } from '@/ui/navigation/menu-item/internals/components/MenuItemLeftContent';
|
||||||
import { StyledHoverableMenuItemBase } from '@/ui/navigation/menu-item/internals/components/StyledMenuItemBase';
|
import { StyledHoverableMenuItemBase } from '@/ui/navigation/menu-item/internals/components/StyledMenuItemBase';
|
||||||
import { OVERLAY_BACKGROUND } from '@/ui/theme/constants/OverlayBackground';
|
import { OVERLAY_BACKGROUND } from '@/ui/theme/constants/OverlayBackground';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
|
|
||||||
@ -37,6 +39,10 @@ const StyledContainer = styled.div`
|
|||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .react-datepicker__triangle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
& .react-datepicker__triangle::after {
|
& .react-datepicker__triangle::after {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -259,7 +265,7 @@ const StyledButton = styled(MenuItemLeftContent)`
|
|||||||
const StyledCustomDatePickerHeader = styled.div`
|
const StyledCustomDatePickerHeader = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: flex-end;
|
||||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||||
@ -267,29 +273,40 @@ const StyledCustomDatePickerHeader = styled.div`
|
|||||||
gap: ${({ theme }) => theme.spacing(1)};
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledMonthText = styled.div`
|
||||||
|
color: ${({ theme }) => theme.font.color.secondary};
|
||||||
|
font-family: ${({ theme }) => theme.font.family};
|
||||||
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
export type InternalDatePickerProps = {
|
export type InternalDatePickerProps = {
|
||||||
date: Date;
|
date: Date;
|
||||||
onMouseSelect?: (date: Date | null) => void;
|
onMouseSelect?: (date: Date | null) => void;
|
||||||
onChange?: (date: Date | null) => void;
|
onChange?: (date: Date | null) => void;
|
||||||
clearable?: boolean;
|
clearable?: boolean;
|
||||||
isDateTimeInput?: boolean;
|
isDateTimeInput?: boolean;
|
||||||
onClickOutside?: (event: MouseEvent | TouchEvent, date: Date | null) => void;
|
onEnter?: (date: Date | null) => void;
|
||||||
|
onEscape?: (date: Date | null) => void;
|
||||||
|
keyboardEventsDisabled?: boolean;
|
||||||
|
onClear?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PICKER_DATE_FORMAT = 'MM/dd/yyyy';
|
|
||||||
|
|
||||||
export const InternalDatePicker = ({
|
export const InternalDatePicker = ({
|
||||||
date,
|
date,
|
||||||
onChange,
|
onChange,
|
||||||
onMouseSelect,
|
onMouseSelect,
|
||||||
|
onEnter,
|
||||||
|
onEscape,
|
||||||
clearable = true,
|
clearable = true,
|
||||||
isDateTimeInput,
|
isDateTimeInput,
|
||||||
onClickOutside,
|
keyboardEventsDisabled,
|
||||||
|
onClear,
|
||||||
}: InternalDatePickerProps) => {
|
}: InternalDatePickerProps) => {
|
||||||
const internalDate = date ?? new Date();
|
const internalDate = date ?? new Date();
|
||||||
|
|
||||||
const dateFormatted =
|
const monthLabel = DateTime.fromJSDate(internalDate).toFormat('LLLL');
|
||||||
DateTime.fromJSDate(internalDate).toFormat(PICKER_DATE_FORMAT);
|
const yearLabel = DateTime.fromJSDate(internalDate).toFormat('yyyy');
|
||||||
|
|
||||||
const { closeDropdown } = useDropdown(MONTH_AND_YEAR_DROPDOWN_ID);
|
const { closeDropdown } = useDropdown(MONTH_AND_YEAR_DROPDOWN_ID);
|
||||||
const { closeDropdown: closeDropdownMonthSelect } = useDropdown(
|
const { closeDropdown: closeDropdownMonthSelect } = useDropdown(
|
||||||
@ -301,7 +318,7 @@ export const InternalDatePicker = ({
|
|||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
closeDropdowns();
|
closeDropdowns();
|
||||||
onMouseSelect?.(null);
|
onClear?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeDropdowns = () => {
|
const closeDropdowns = () => {
|
||||||
@ -310,28 +327,59 @@ export const InternalDatePicker = ({
|
|||||||
closeDropdown();
|
closeDropdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickOutside = (event: any) => {
|
|
||||||
closeDropdowns();
|
|
||||||
onClickOutside?.(event, internalDate);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseSelect = (newDate: Date) => {
|
const handleMouseSelect = (newDate: Date) => {
|
||||||
closeDropdowns();
|
closeDropdowns();
|
||||||
onMouseSelect?.(newDate);
|
onMouseSelect?.(newDate);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: implement keyboard events here
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (isDefined(keyboardEventsDisabled) && keyboardEventsDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event.key) {
|
||||||
|
case Key.Enter: {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
closeDropdowns();
|
||||||
|
onEnter?.(internalDate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Key.Escape: {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
closeDropdowns();
|
||||||
|
onEscape?.(internalDate);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer onKeyDown={handleKeyDown}>
|
||||||
<div className={clearable ? 'clearable ' : ''}>
|
<div className={clearable ? 'clearable ' : ''}>
|
||||||
<ReactDatePicker
|
<ReactDatePicker
|
||||||
open={true}
|
open={true}
|
||||||
selected={internalDate}
|
selected={internalDate}
|
||||||
value={dateFormatted}
|
openToDate={internalDate}
|
||||||
onChange={(newDate) => {
|
onChange={(newDate) => {
|
||||||
onChange?.(newDate);
|
onChange?.(newDate);
|
||||||
}}
|
}}
|
||||||
|
customInput={
|
||||||
|
<DateTimeInput
|
||||||
|
date={internalDate}
|
||||||
|
isDateTimeInput={isDateTimeInput}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onMonthChange={(newDate) => {
|
||||||
|
onChange?.(newDate);
|
||||||
|
}}
|
||||||
|
onYearChange={(newDate) => {
|
||||||
|
onChange?.(newDate);
|
||||||
|
}}
|
||||||
renderCustomHeader={({
|
renderCustomHeader={({
|
||||||
decreaseMonth,
|
decreaseMonth,
|
||||||
increaseMonth,
|
increaseMonth,
|
||||||
@ -345,7 +393,9 @@ export const InternalDatePicker = ({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
<StyledCustomDatePickerHeader>
|
<StyledCustomDatePickerHeader>
|
||||||
<TimeInput date={internalDate} onChange={onChange} />
|
{isDateTimeInput && (
|
||||||
|
<TimeInput date={internalDate} onChange={onChange} />
|
||||||
|
)}
|
||||||
<MonthAndYearDropdown date={internalDate} onChange={onChange} />
|
<MonthAndYearDropdown date={internalDate} onChange={onChange} />
|
||||||
<LightIconButton
|
<LightIconButton
|
||||||
Icon={IconChevronLeft}
|
Icon={IconChevronLeft}
|
||||||
@ -360,9 +410,11 @@ export const InternalDatePicker = ({
|
|||||||
disabled={nextMonthButtonDisabled}
|
disabled={nextMonthButtonDisabled}
|
||||||
/>
|
/>
|
||||||
</StyledCustomDatePickerHeader>
|
</StyledCustomDatePickerHeader>
|
||||||
|
<StyledMonthText>
|
||||||
|
{monthLabel} - {yearLabel}
|
||||||
|
</StyledMonthText>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
customInput={<></>}
|
|
||||||
onSelect={(date: Date, event) => {
|
onSelect={(date: Date, event) => {
|
||||||
const dateUTC = DateTime.fromJSDate(date, {
|
const dateUTC = DateTime.fromJSDate(date, {
|
||||||
zone: 'utc',
|
zone: 'utc',
|
||||||
@ -374,8 +426,7 @@ export const InternalDatePicker = ({
|
|||||||
onChange?.(dateUTC);
|
onChange?.(dateUTC);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onClickOutside={handleClickOutside}
|
/>
|
||||||
></ReactDatePicker>
|
|
||||||
</div>
|
</div>
|
||||||
{clearable && (
|
{clearable && (
|
||||||
<StyledButtonContainer onClick={handleClear} isMenuOpen={false}>
|
<StyledButtonContainer onClick={handleClear} isMenuOpen={false}>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { IconCalendarDue } from 'twenty-ui';
|
import { IconCalendarDue } from 'twenty-ui';
|
||||||
|
|
||||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
|
||||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||||
import { Select } from '@/ui/input/components/Select';
|
import { Select } from '@/ui/input/components/Select';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
@ -57,7 +56,7 @@ export const MonthAndYearDropdown = ({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={MONTH_AND_YEAR_DROPDOWN_ID}
|
dropdownId={MONTH_AND_YEAR_DROPDOWN_ID}
|
||||||
dropdownHotkeyScope={{
|
dropdownHotkeyScope={{
|
||||||
scope: TableHotkeyScope.CellEditMode,
|
scope: MONTH_AND_YEAR_DROPDOWN_ID,
|
||||||
}}
|
}}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { IMask } from 'react-imask';
|
||||||
|
|
||||||
|
import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate';
|
||||||
|
import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate';
|
||||||
|
|
||||||
|
export const DATE_BLOCKS = {
|
||||||
|
YYYY: {
|
||||||
|
mask: IMask.MaskedRange,
|
||||||
|
from: MIN_DATE.getFullYear(),
|
||||||
|
to: MAX_DATE.getFullYear(),
|
||||||
|
},
|
||||||
|
MM: {
|
||||||
|
mask: IMask.MaskedRange,
|
||||||
|
from: 1,
|
||||||
|
to: 12,
|
||||||
|
},
|
||||||
|
DD: {
|
||||||
|
mask: IMask.MaskedRange,
|
||||||
|
from: 1,
|
||||||
|
to: 31,
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const DATE_MASK = 'm`/d`/Y`'; // See https://imask.js.org/guide.html#masked-date
|
||||||
@ -1,22 +1,7 @@
|
|||||||
import { IMask } from 'react-imask';
|
import { DATE_BLOCKS } from '@/ui/input/components/internal/date/constants/DateBlocks';
|
||||||
|
|
||||||
import { TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/TimeBlocks';
|
import { TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/TimeBlocks';
|
||||||
|
|
||||||
export const DATE_TIME_BLOCKS = {
|
export const DATE_TIME_BLOCKS = {
|
||||||
YYYY: {
|
...DATE_BLOCKS,
|
||||||
mask: IMask.MaskedRange,
|
|
||||||
from: 1970,
|
|
||||||
to: 2100,
|
|
||||||
},
|
|
||||||
MM: {
|
|
||||||
mask: IMask.MaskedRange,
|
|
||||||
from: 1,
|
|
||||||
to: 12,
|
|
||||||
},
|
|
||||||
DD: {
|
|
||||||
mask: IMask.MaskedRange,
|
|
||||||
from: 1,
|
|
||||||
to: 31,
|
|
||||||
},
|
|
||||||
...TIME_BLOCKS,
|
...TIME_BLOCKS,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
import { TIME_MASK } from '@/ui/input/components/internal/date/constants/TimeMask';
|
import { TIME_MASK } from '@/ui/input/components/internal/date/constants/TimeMask';
|
||||||
|
|
||||||
export const DATE_TIME_MASK = `MM/DD/YYYY ${TIME_MASK}`;
|
export const DATE_TIME_MASK = `m\`/d\`/Y\` ${TIME_MASK}`;
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export const MAX_DATE = new Date(2100, 11, 31);
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const MIN_DATE = new Date(1900, 0, 1);
|
||||||
@ -9,6 +9,6 @@ export const TIME_BLOCKS = {
|
|||||||
mm: {
|
mm: {
|
||||||
mask: IMask.MaskedRange, // Use MaskedRange for valid minute range (0-59)
|
mask: IMask.MaskedRange, // Use MaskedRange for valid minute range (0-59)
|
||||||
from: 0,
|
from: 0,
|
||||||
to: 61,
|
to: 59,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export const TIME_MASK = 'HH:mm'; // Define blocks for hours and minutes
|
export const TIME_MASK = 'HH`:mm`'; // Define blocks for hours and minutes
|
||||||
|
|||||||
@ -10,6 +10,18 @@ export const formatToHumanReadableDate = (date: Date | string) => {
|
|||||||
}).format(parsedJSDate);
|
}).format(parsedJSDate);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatToHumanReadableDateTime = (date: Date | string) => {
|
||||||
|
const parsedJSDate = parseDate(date).toJSDate();
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat(undefined, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
}).format(parsedJSDate);
|
||||||
|
};
|
||||||
|
|
||||||
export const sanitizeURL = (link: string | null | undefined) => {
|
export const sanitizeURL = (link: string | null | undefined) => {
|
||||||
return link
|
return link
|
||||||
? link.replace(/(https?:\/\/)|(www\.)/g, '').replace(/\/$/, '')
|
? link.replace(/(https?:\/\/)|(www\.)/g, '').replace(/\/$/, '')
|
||||||
|
|||||||
81
yarn.lock
81
yarn.lock
@ -5533,6 +5533,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@floating-ui/react@npm:^0.26.2":
|
||||||
|
version: 0.26.12
|
||||||
|
resolution: "@floating-ui/react@npm:0.26.12"
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/react-dom": "npm:^2.0.0"
|
||||||
|
"@floating-ui/utils": "npm:^0.2.0"
|
||||||
|
tabbable: "npm:^6.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">=16.8.0"
|
||||||
|
react-dom: ">=16.8.0"
|
||||||
|
checksum: f47e133b9fe5dd404886c4e69e760dc0d6ab30774de82b5547cba83d0cd6e7a3bb4c221587e115a3c0c466ac344578c3db78b5808b5acd70ae5d43d587de9fd1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@floating-ui/react@npm:^0.26.4":
|
"@floating-ui/react@npm:^0.26.4":
|
||||||
version: 0.26.8
|
version: 0.26.8
|
||||||
resolution: "@floating-ui/react@npm:0.26.8"
|
resolution: "@floating-ui/react@npm:0.26.8"
|
||||||
@ -5554,7 +5568,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@floating-ui/utils@npm:^0.2.1":
|
"@floating-ui/utils@npm:^0.2.0, @floating-ui/utils@npm:^0.2.1":
|
||||||
version: 0.2.1
|
version: 0.2.1
|
||||||
resolution: "@floating-ui/utils@npm:0.2.1"
|
resolution: "@floating-ui/utils@npm:0.2.1"
|
||||||
checksum: ee77756712cf5b000c6bacf11992ffb364f3ea2d0d51cc45197a7e646a17aeb86ea4b192c0b42f3fbb29487aee918a565e84f710b8c3645827767f406a6b4cc9
|
checksum: ee77756712cf5b000c6bacf11992ffb364f3ea2d0d51cc45197a7e646a17aeb86ea4b192c0b42f3fbb29487aee918a565e84f710b8c3645827767f406a6b4cc9
|
||||||
@ -9791,7 +9805,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@popperjs/core@npm:^2.11.8, @popperjs/core@npm:^2.9.0, @popperjs/core@npm:^2.9.2":
|
"@popperjs/core@npm:^2.9.0":
|
||||||
version: 2.11.8
|
version: 2.11.8
|
||||||
resolution: "@popperjs/core@npm:2.11.8"
|
resolution: "@popperjs/core@npm:2.11.8"
|
||||||
checksum: 4681e682abc006d25eb380d0cf3efc7557043f53b6aea7a5057d0d1e7df849a00e281cd8ea79c902a35a414d7919621fc2ba293ecec05f413598e0b23d5a1e63
|
checksum: 4681e682abc006d25eb380d0cf3efc7557043f53b6aea7a5057d0d1e7df849a00e281cd8ea79c902a35a414d7919621fc2ba293ecec05f413598e0b23d5a1e63
|
||||||
@ -16975,15 +16989,14 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/react-datepicker@npm:^4.11.2":
|
"@types/react-datepicker@npm:^6.2.0":
|
||||||
version: 4.19.5
|
version: 6.2.0
|
||||||
resolution: "@types/react-datepicker@npm:4.19.5"
|
resolution: "@types/react-datepicker@npm:6.2.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@popperjs/core": "npm:^2.9.2"
|
"@floating-ui/react": "npm:^0.26.2"
|
||||||
"@types/react": "npm:*"
|
"@types/react": "npm:*"
|
||||||
date-fns: "npm:^2.0.1"
|
date-fns: "npm:^3.3.1"
|
||||||
react-popper: "npm:^2.2.5"
|
checksum: 40342f0a5e15bac6f2b35072a2f7041db20b924692123aface78f2cbf0e896c0c02c299a9173583e01e5b6dad38e3da86ad0e56e943e74e0c0dc7d5c60129bdf
|
||||||
checksum: 8b3402338e842a2d5c4d4c0ceefc9fc280607662d9073db860b59e31a3b3977eec6ec3d36b388ec5a3a3874adec00ab0f6a9405cd0de1f8dcf75de3328a84eb8
|
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -22376,7 +22389,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"clsx@npm:^2.0.0":
|
"clsx@npm:^2.0.0, clsx@npm:^2.1.0":
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
resolution: "clsx@npm:2.1.0"
|
resolution: "clsx@npm:2.1.0"
|
||||||
checksum: c09c00ad14f638366ca814097e6cab533dfa1972a358da5b557be487168acbb25b4c1395e89ffa842a8a61ba87a462d2b4885bc9d4f8410b598f3cb339599cdb
|
checksum: c09c00ad14f638366ca814097e6cab533dfa1972a358da5b557be487168acbb25b4c1395e89ffa842a8a61ba87a462d2b4885bc9d4f8410b598f3cb339599cdb
|
||||||
@ -23986,7 +23999,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"date-fns@npm:^2.0.1, date-fns@npm:^2.30.0":
|
"date-fns@npm:^2.30.0":
|
||||||
version: 2.30.0
|
version: 2.30.0
|
||||||
resolution: "date-fns@npm:2.30.0"
|
resolution: "date-fns@npm:2.30.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -23995,6 +24008,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"date-fns@npm:^3.3.1":
|
||||||
|
version: 3.6.0
|
||||||
|
resolution: "date-fns@npm:3.6.0"
|
||||||
|
checksum: 0b5fb981590ef2f8e5a3ba6cd6d77faece0ea7f7158948f2eaae7bbb7c80a8f63ae30b01236c2923cf89bb3719c33aeb150c715ea4fe4e86e37dcf06bed42fb6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"dateformat@npm:^4.5.0":
|
"dateformat@npm:^4.5.0":
|
||||||
version: 4.6.3
|
version: 4.6.3
|
||||||
resolution: "dateformat@npm:4.6.3"
|
resolution: "dateformat@npm:4.6.3"
|
||||||
@ -40815,20 +40835,19 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-datepicker@npm:^4.11.0":
|
"react-datepicker@npm:^6.7.1":
|
||||||
version: 4.25.0
|
version: 6.7.1
|
||||||
resolution: "react-datepicker@npm:4.25.0"
|
resolution: "react-datepicker@npm:6.7.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@popperjs/core": "npm:^2.11.8"
|
"@floating-ui/react": "npm:^0.26.2"
|
||||||
classnames: "npm:^2.2.6"
|
clsx: "npm:^2.1.0"
|
||||||
date-fns: "npm:^2.30.0"
|
date-fns: "npm:^3.3.1"
|
||||||
prop-types: "npm:^15.7.2"
|
prop-types: "npm:^15.7.2"
|
||||||
react-onclickoutside: "npm:^6.13.0"
|
react-onclickoutside: "npm:^6.13.0"
|
||||||
react-popper: "npm:^2.3.0"
|
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.9.0 || ^17 || ^18
|
react: ^16.9.0 || ^17 || ^18
|
||||||
react-dom: ^16.9.0 || ^17 || ^18
|
react-dom: ^16.9.0 || ^17 || ^18
|
||||||
checksum: 714fc6cffdf9fa76d1a74581375acb4c86ac81e6c2cde372448ab447370b7e9ffb1840cf388020485028e7b0f43edf82c1cfd2dab7d2c6b964cdd6f4dd560e92
|
checksum: 9029c358254ef011e3a6b82fd542a87efa30a67b7aa2814178d28354e2b18f69b863861fb852f236a91be57a0b684455c9127c87232b7ed2f4fae6737e3248e6
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -40957,7 +40976,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-fast-compare@npm:3.2.2, react-fast-compare@npm:^3.0.1, react-fast-compare@npm:^3.2.0, react-fast-compare@npm:^3.2.2":
|
"react-fast-compare@npm:3.2.2, react-fast-compare@npm:^3.2.0, react-fast-compare@npm:^3.2.2":
|
||||||
version: 3.2.2
|
version: 3.2.2
|
||||||
resolution: "react-fast-compare@npm:3.2.2"
|
resolution: "react-fast-compare@npm:3.2.2"
|
||||||
checksum: 0bbd2f3eb41ab2ff7380daaa55105db698d965c396df73e6874831dbafec8c4b5b08ba36ff09df01526caa3c61595247e3269558c284e37646241cba2b90a367
|
checksum: 0bbd2f3eb41ab2ff7380daaa55105db698d965c396df73e6874831dbafec8c4b5b08ba36ff09df01526caa3c61595247e3269558c284e37646241cba2b90a367
|
||||||
@ -41157,20 +41176,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"react-popper@npm:^2.2.5, react-popper@npm:^2.3.0":
|
|
||||||
version: 2.3.0
|
|
||||||
resolution: "react-popper@npm:2.3.0"
|
|
||||||
dependencies:
|
|
||||||
react-fast-compare: "npm:^3.0.1"
|
|
||||||
warning: "npm:^4.0.2"
|
|
||||||
peerDependencies:
|
|
||||||
"@popperjs/core": ^2.0.0
|
|
||||||
react: ^16.8.0 || ^17 || ^18
|
|
||||||
react-dom: ^16.8.0 || ^17 || ^18
|
|
||||||
checksum: 23f93540537ca4c035425bb8d5e51b11131fbc921d7ac1d041d0ae557feac8c877f3a012d36b94df8787803f52ed81e6df9257ac9e58719875f7805518d6db3f
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"react-query@npm:^3.34.19":
|
"react-query@npm:^3.34.19":
|
||||||
version: 3.39.3
|
version: 3.39.3
|
||||||
resolution: "react-query@npm:3.39.3"
|
resolution: "react-query@npm:3.39.3"
|
||||||
@ -45046,7 +45051,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"tabbable@npm:^6.0.1, tabbable@npm:^6.2.0":
|
"tabbable@npm:^6.0.0, tabbable@npm:^6.0.1, tabbable@npm:^6.2.0":
|
||||||
version: 6.2.0
|
version: 6.2.0
|
||||||
resolution: "tabbable@npm:6.2.0"
|
resolution: "tabbable@npm:6.2.0"
|
||||||
checksum: ced8b38f05f2de62cd46836d77c2646c42b8c9713f5bd265daf0e78ff5ac73d3ba48a7ca45f348bafeef29b23da7187c72250742d37627883ef89cbd7fa76898
|
checksum: ced8b38f05f2de62cd46836d77c2646c42b8c9713f5bd265daf0e78ff5ac73d3ba48a7ca45f348bafeef29b23da7187c72250742d37627883ef89cbd7fa76898
|
||||||
@ -46222,7 +46227,7 @@ __metadata:
|
|||||||
"@types/passport-google-oauth20": "npm:^2.0.11"
|
"@types/passport-google-oauth20": "npm:^2.0.11"
|
||||||
"@types/passport-jwt": "npm:^3.0.8"
|
"@types/passport-jwt": "npm:^3.0.8"
|
||||||
"@types/react": "npm:^18.2.39"
|
"@types/react": "npm:^18.2.39"
|
||||||
"@types/react-datepicker": "npm:^4.11.2"
|
"@types/react-datepicker": "npm:^6.2.0"
|
||||||
"@types/react-dom": "npm:^18.2.15"
|
"@types/react-dom": "npm:^18.2.15"
|
||||||
"@types/scroll-into-view": "npm:^1.16.0"
|
"@types/scroll-into-view": "npm:^1.16.0"
|
||||||
"@types/supertest": "npm:^2.0.11"
|
"@types/supertest": "npm:^2.0.11"
|
||||||
@ -46346,7 +46351,7 @@ __metadata:
|
|||||||
raw-loader: "npm:^4.0.2"
|
raw-loader: "npm:^4.0.2"
|
||||||
react: "npm:^18.2.0"
|
react: "npm:^18.2.0"
|
||||||
react-data-grid: "npm:7.0.0-beta.13"
|
react-data-grid: "npm:7.0.0-beta.13"
|
||||||
react-datepicker: "npm:^4.11.0"
|
react-datepicker: "npm:^6.7.1"
|
||||||
react-dom: "npm:^18.2.0"
|
react-dom: "npm:^18.2.0"
|
||||||
react-dropzone: "npm:^14.2.3"
|
react-dropzone: "npm:^14.2.3"
|
||||||
react-error-boundary: "npm:^4.0.11"
|
react-error-boundary: "npm:^4.0.11"
|
||||||
@ -48240,7 +48245,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"warning@npm:^4.0.2, warning@npm:^4.0.3":
|
"warning@npm:^4.0.3":
|
||||||
version: 4.0.3
|
version: 4.0.3
|
||||||
resolution: "warning@npm:4.0.3"
|
resolution: "warning@npm:4.0.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user