Skip to content

Commit

Permalink
New Datetime field picker (twentyhq#4907)
Browse files Browse the repository at this point in the history
### Description
New Datetime field picker

### Refs
twentyhq#4376

### Demo


https://github.com/twentyhq/twenty/assets/140154534/32656323-972c-413a-9986-a78efffae1b4


Fixes twentyhq#4376

---------

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>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
  • Loading branch information
6 people authored Apr 13, 2024
1 parent 464a2d5 commit efcb5dc
Show file tree
Hide file tree
Showing 15 changed files with 469 additions and 99 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"react-hook-form": "^7.45.1",
"react-hotkeys-hook": "^4.4.4",
"react-icons": "^4.12.0",
"react-imask": "^7.6.0",
"react-intersection-observer": "^9.5.2",
"react-loading-skeleton": "^3.3.1",
"react-phone-number-input": "^3.3.4",
Expand Down
2 changes: 0 additions & 2 deletions packages/twenty-docs/src/theme/DocSidebarItem/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import SearchBar from '@theme-original/SearchBar';
const CustomComponents = {
'search-bar': () => {
const openSearchModal = () => {
console.log('yo');

const searchInput = document.querySelector('#search-bar');
if (searchInput) {
searchInput.focus();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IconBell } from "@tabler/icons-react";
import { MenuItemToggle } from "@/ui/navigation/menu-item/components/MenuItemToggle";
import { IconBell } from '@tabler/icons-react';

import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle';

export const MyComponent = () => {
const handleToggleChange = (toggled) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
IconCalendarEvent,
IconCalendarTime,
IconCheck,
IconCoins,
IconComponent,
Expand Down Expand Up @@ -67,8 +68,8 @@ export const SETTINGS_FIELD_TYPE_CONFIGS: Record<
defaultValue: true,
},
[FieldMetadataType.DateTime]: {
label: 'Date & Time',
Icon: IconCalendarEvent,
label: 'Date and Time',
Icon: IconCalendarTime,
defaultValue: DEFAULT_DATE_VALUE.toISOString(),
},
[FieldMetadataType.Date]: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ export type DateInputProps = {

export const DateInput = ({
value,
hotkeyScope,
onEnter,
onEscape,
hotkeyScope,
onClickOutside,
clearable,
onChange,
Expand All @@ -65,7 +65,7 @@ export const DateInput = ({
],
});

const handleChange = (newDate: Date) => {
const handleChange = (newDate: Date | null) => {
setInternalValue(newDate);
onChange?.(newDate);
};
Expand Down Expand Up @@ -96,6 +96,7 @@ export const DateInput = ({
}}
clearable={clearable ? clearable : false}
isDateTimeInput={isDateTimeInput}
onClickOutside={onClickOutside}
/>
</StyledCalendarContainer>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { useEffect, useState } from 'react';
import { useIMask } from 'react-imask';
import styled from '@emotion/styled';
import { DateTime } from 'luxon';

import { DATE_TIME_BLOCKS } from '@/ui/input/components/internal/date/constants/DateTimeBlocks';
import { DATE_TIME_MASK } from '@/ui/input/components/internal/date/constants/DateTimeMask';

const StyledInputContainer = styled.div`
width: 100%;
display: flex;
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
height: ${({ theme }) => theme.spacing(8)};
`;

const StyledInput = styled.input<{ hasError?: boolean }>`
background: ${({ theme }) => theme.background.secondary};
border: none;
color: ${({ theme }) => theme.font.color.primary};
outline: none;
padding: 8px;
font-weight: 500;
font-size: ${({ theme }) => theme.font.size.md};
width: 100%;
color: ${({ hasError, theme }) => (hasError ? theme.color.red : 'inherit')};
`;

type DateTimeInputProps = {
onChange?: (date: Date | null) => void;
date: Date | null;
isDateTimeInput?: boolean;
onError?: (error: Error) => void;
};

export const DateTimeInput = ({
date,
onChange,
isDateTimeInput,
}: DateTimeInputProps) => {
const [hasError, setHasError] = useState(false);

const parseDateToString = (date: any) => {
const dateParsed = DateTime.fromJSDate(date);

const formattedDate = dateParsed.toFormat('MM/dd/yyyy HH:mm');

return formattedDate;
};

const parseStringToDate = (str: string) => {
setHasError(false);

const parsedDate = DateTime.fromFormat(str, 'MM/dd/yyyy HH:mm');

const isValid = parsedDate.isValid;

if (!isValid) {
setHasError(true);

return null;
}

const jsDate = parsedDate.toJSDate();

return jsDate;
};

const { ref, setValue, value } = useIMask(
{
mask: Date,
pattern: DATE_TIME_MASK,
blocks: DATE_TIME_BLOCKS,
min: new Date(1970, 0, 1),
max: new Date(2100, 0, 1),
format: parseDateToString,
parse: parseStringToDate,
lazy: false,
autofix: true,
},
{
onComplete: (value) => {
const parsedDate = parseStringToDate(value);

onChange?.(parsedDate);
},
onAccept: () => {
setHasError(false);
},
},
);

useEffect(() => {
setValue(parseDateToString(date));
}, [date, setValue]);

return (
<StyledInputContainer>
<StyledInput
type="text"
ref={ref as any}
placeholder={`Type date${
isDateTimeInput ? ' and time' : ' (mm/dd/yyyy)'
}`}
value={value}
hasError={hasError}
/>
</StyledInputContainer>
);
};
Loading

0 comments on commit efcb5dc

Please sign in to comment.