Skip to content

Commit

Permalink
feat: add datetime picker
Browse files Browse the repository at this point in the history
  • Loading branch information
mdeliatf authored Aug 9, 2024
1 parent ea60ab3 commit 44c9b5c
Show file tree
Hide file tree
Showing 7 changed files with 406 additions and 1 deletion.
53 changes: 53 additions & 0 deletions components/DateTimePicker/DateTimePicker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Meta, StoryFn } from '@storybook/react';
import React, { useState } from 'react';

import { modifyVariantsForStory } from '../../utils/modifyVariantsForStory';
import { Box } from '../Box';
import { Flex } from '../Flex';
import { Label } from '../Label';
import { Text } from '../Text';
import { DateTimePicker, DateTimePickerProps, DateTimePickerVariants } from './DateTimePicker';

const BaseDateTimePicker = (props: DateTimePickerProps): JSX.Element => (
<DateTimePicker {...props} />
);

const DateTimePickerForStory = modifyVariantsForStory<DateTimePickerVariants, DateTimePickerProps>(
BaseDateTimePicker,
);

const Component: Meta<typeof DateTimePickerForStory> = {
title: 'Components/DateTimePicker',
component: DateTimePickerForStory,
};

const Template: StoryFn<typeof DateTimePickerForStory> = (args) => {
const [date, setDate] = useState(new Date());

return (
<form>
<Flex direction="column" gap={3}>
<Box>
<Label htmlFor="expiration-input">Expiration</Label>
<DateTimePickerForStory
{...args}
id="expiration-input"
dateFormat="MMMM d, yyyy h:mm aa"
onChange={(date) => setDate(date)}
popperPlacement="bottom-start"
selected={date}
showIcon
/>
</Box>
<Flex direction="column" css={{ gap: '2px' }}>
<Label htmlFor="expiration-date">Expiration date</Label>
<Text id="expiration-date">{date.toISOString()}</Text>
</Flex>
</Flex>
</form>
);
};

export const Basic: StoryFn<typeof DateTimePickerForStory> = Template.bind({});

export default Component;
268 changes: 268 additions & 0 deletions components/DateTimePicker/DateTimePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
import 'react-datepicker/dist/react-datepicker.css';

import { CalendarIcon } from '@radix-ui/react-icons';
import { addMonths, addYears } from 'date-fns';
import React, { useEffect, useRef, useState } from 'react';
import DatePicker, { CalendarContainer, DatePickerProps } from 'react-datepicker';

import { CSS, styled, VariantProps } from '../../stitches.config';
import { Button } from '../Button';
import { Card } from '../Card';
import { Flex } from '../Flex';
import { Input } from '../Input';

const StyledWrapper = styled('div', {
display: 'flex',
width: '100%',

// Reset
outline: 'none',
lineHeight: 0,

position: 'relative',
backgroundColor: '$inputBg',
color: '$inputPlaceholder',

'&::before': {
boxSizing: 'border-box',
content: '""',
position: 'absolute',
inset: 0,
pointerEvents: 'none',
},
'&::after': {
boxSizing: 'border-box',
content: '""',
position: 'absolute',
inset: 0,
pointerEvents: 'none',
},

'&:focus-visible': {
'&::before': {
backgroundColor: '$inputFocusBg',
},
'&::after': {
backgroundColor: '$primary',
opacity: 0.15,
},
},

'@hover': {
'&:hover': {
'&::before': {
backgroundColor: '$inputHoverBg',
},
'&::after': {
backgroundColor: '$primary',
opacity: 0.05,
},
},
},

'> .react-datepicker-wrapper': {
width: '100%',
},

'.react-datepicker': {
backgroundColor: 'transparent',
border: '2px solid $colors$buttonSecondaryBorder',
borderRadius: '$3',
// border: 'none',
// boxShadow: 'inset 0 0 0 2px $colors$buttonSecondaryBorder',
color: '$textDefault',
fontFamily: '$rubik',

'.react-datepicker__header': {
backgroundColor: 'transparent',

'&::before': {
boxSizing: 'border-box',
content: '""',
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
borderRadius: '$3',
backgroundColor: '$navButtonActiveBg',
},

'&::after': {
boxSizing: 'border-box',
content: '""',
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
borderRadius: '$3',
backgroundColor: '$navButtonActiveBg2',
opacity: 0.05,
},

'.react-datepicker__current-month, .react-datepicker-time__header': {
color: '$textDefault',
},
},

'.react-datepicker__navigation': {
'.react-datepicker__navigation-icon::before': {
borderColor: '$gray11',
},
},

'.react-datepicker__month-container': {
'.react-datepicker__day': {
border: '1px solid transparent',
borderRadius: '$3',
boxSizing: 'border-box',
color: '$textDefault',

'&--outside-month': {
color: '$textSubtle',
},

'&--today': {
border: '1px solid $buttonSecondaryBorder',
borderRadius: '$3',
fontWeight: 'normal',
},

'&:hover': {
backgroundColor: '$gray6',
},

'&--keyboard-selected': {
backgroundColor: 'inherit',
border: '1px solid $primary',
borderRadius: '$3',
},

'&--selected': {
backgroundColor: '$primary',
borderRadius: '$3',
color: '$buttonPrimaryText',

'&:hover': {
backgroundColor: '$primary',
},
},
},

'.react-datepicker__day-name': {
color: '$textDefault',
},
},

'.react-datepicker__time-container': {
'.react-datepicker__time': {
backgroundColor: 'transparent',
color: '$textDefault',

'.react-datepicker__time-box': {
scrollbarColor: 'var(--colors-gray7) var(--colors-gray2)',
},

'.react-datepicker__time-list .react-datepicker__time-list-item': {
'&:hover': {
backgroundColor: '$gray6',
},

'&.react-datepicker__time-list-item--selected, &.react-datepicker__time-list-item--selected:hover':
{
backgroundColor: '$primary',
color: '$buttonPrimaryText',
fontWeight: 'normal',
},
},
},
},
},

'.react-datepicker-popper[data-placement] .react-datepicker__triangle': {
left: '50% !important',
transform: 'rotate(180deg) translateY(-1px) translateX(50%) !important',
color: '$01dp',
fill: '$01dp',
stroke: '$01dp',
},
});

export type DateTimePickerProps = DatePickerProps & {
css?: CSS;
};

export type DateTimePickerVariants = VariantProps<typeof DateTimePicker>;

export const DateTimePicker = React.forwardRef<
React.ElementRef<typeof StyledWrapper>,
DateTimePickerProps
>(({ css, onChange, selected, showIcon, ...props }, fowardedRef) => {
const datePickerRef = useRef<DatePicker | null>(null);

const [selectedDate, setSelectedDate] = useState(selected || new Date());

const CalendarContainerWrapper = ({ className, children }) => {
return (
<Card css={{ display: 'flex', flexDirection: 'column', gap: '$2' }}>
<CalendarContainer className={className}>
<div style={{ position: 'relative' }}>{children}</div>
</CalendarContainer>
<Flex gap={2}>
<Button
type="button"
variant="secondary"
onClick={() => {
setSelectedDate(addMonths(new Date(), 1));
datePickerRef?.current?.setOpen(false);
}}
css={{ flex: '1 1 0px' }}
>
Now + 1 M
</Button>
<Button
type="button"
variant="secondary"
onClick={() => {
setSelectedDate(addMonths(new Date(), 3));
datePickerRef?.current?.setOpen(false);
}}
css={{ flex: '1 1 0px' }}
>
Now + 3 M
</Button>
<Button
type="button"
variant="secondary"
onClick={() => {
setSelectedDate(addYears(new Date(), 1));
datePickerRef?.current?.setOpen(false);
}}
css={{ flex: '1 1 0px' }}
>
Now + 1 Y
</Button>
</Flex>
</Card>
);
};

useEffect(() => {
if (onChange) onChange(selectedDate as Date & [Date | null, Date | null] & Date[]);
}, [onChange, selectedDate]);

return (
<StyledWrapper css={css} ref={fowardedRef}>
<DatePicker
calendarContainer={CalendarContainerWrapper}
customInput={<Input startAdornment={showIcon ? <CalendarIcon /> : undefined} />}
ref={datePickerRef}
showTimeSelect
{...props}
selected={selectedDate}
onChange={(date: any) => setSelectedDate(date)}
/>
</StyledWrapper>
);
});
1 change: 1 addition & 0 deletions components/DateTimePicker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './DateTimePicker';
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { Checkbox } from './components/Checkbox';
export { Container } from './components/Container';
export { Elevation, elevationVariants } from './components/Elevation';
export { FaencyProvider } from './components/FaencyProvider';
export { DateTimePicker } from './components/DateTimePicker';
export {
Dialog,
DialogCloseIconButton,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
"patch-package": "^8.0.0",
"prettier": "^3.3.3",
"react": "18.2.0",
"react-datepicker": "^7.3.0",
"react-dom": "18.2.0",
"rollup": "^2.70.1",
"rollup-plugin-typescript2": "^0.36.0",
Expand Down
2 changes: 1 addition & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineConfig } from 'vite';

export default defineConfig({
optimizeDeps: {
exclude: ['storybook'],
exclude: ['react-datepicker', 'storybook'],
},
plugins: [react()],
});
Loading

0 comments on commit 44c9b5c

Please sign in to comment.