From 44c9b5c96d30e7ef3b28e0a8f9c5ded626fb7f58 Mon Sep 17 00:00:00 2001
From: "Massimiliano D." <126668030+mdeliatf@users.noreply.github.com>
Date: Fri, 9 Aug 2024 11:14:03 +0200
Subject: [PATCH] feat: add datetime picker
---
.../DateTimePicker/DateTimePicker.stories.tsx | 53 ++++
components/DateTimePicker/DateTimePicker.tsx | 268 ++++++++++++++++++
components/DateTimePicker/index.ts | 1 +
index.ts | 1 +
package.json | 1 +
vite.config.ts | 2 +-
yarn.lock | 81 ++++++
7 files changed, 406 insertions(+), 1 deletion(-)
create mode 100644 components/DateTimePicker/DateTimePicker.stories.tsx
create mode 100644 components/DateTimePicker/DateTimePicker.tsx
create mode 100644 components/DateTimePicker/index.ts
diff --git a/components/DateTimePicker/DateTimePicker.stories.tsx b/components/DateTimePicker/DateTimePicker.stories.tsx
new file mode 100644
index 00000000..230746d3
--- /dev/null
+++ b/components/DateTimePicker/DateTimePicker.stories.tsx
@@ -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 => (
+
+);
+
+const DateTimePickerForStory = modifyVariantsForStory(
+ BaseDateTimePicker,
+);
+
+const Component: Meta = {
+ title: 'Components/DateTimePicker',
+ component: DateTimePickerForStory,
+};
+
+const Template: StoryFn = (args) => {
+ const [date, setDate] = useState(new Date());
+
+ return (
+
+ );
+};
+
+export const Basic: StoryFn = Template.bind({});
+
+export default Component;
diff --git a/components/DateTimePicker/DateTimePicker.tsx b/components/DateTimePicker/DateTimePicker.tsx
new file mode 100644
index 00000000..97d57b2d
--- /dev/null
+++ b/components/DateTimePicker/DateTimePicker.tsx
@@ -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;
+
+export const DateTimePicker = React.forwardRef<
+ React.ElementRef,
+ DateTimePickerProps
+>(({ css, onChange, selected, showIcon, ...props }, fowardedRef) => {
+ const datePickerRef = useRef(null);
+
+ const [selectedDate, setSelectedDate] = useState(selected || new Date());
+
+ const CalendarContainerWrapper = ({ className, children }) => {
+ return (
+
+
+ {children}
+
+
+
+
+
+
+
+ );
+ };
+
+ useEffect(() => {
+ if (onChange) onChange(selectedDate as Date & [Date | null, Date | null] & Date[]);
+ }, [onChange, selectedDate]);
+
+ return (
+
+ : undefined} />}
+ ref={datePickerRef}
+ showTimeSelect
+ {...props}
+ selected={selectedDate}
+ onChange={(date: any) => setSelectedDate(date)}
+ />
+
+ );
+});
diff --git a/components/DateTimePicker/index.ts b/components/DateTimePicker/index.ts
new file mode 100644
index 00000000..2ed355b3
--- /dev/null
+++ b/components/DateTimePicker/index.ts
@@ -0,0 +1 @@
+export * from './DateTimePicker';
diff --git a/index.ts b/index.ts
index ab55bec4..4aaa7d4e 100644
--- a/index.ts
+++ b/index.ts
@@ -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,
diff --git a/package.json b/package.json
index b2d39514..1a5788d8 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/vite.config.ts b/vite.config.ts
index 6566ad1b..ed1f779d 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -3,7 +3,7 @@ import { defineConfig } from 'vite';
export default defineConfig({
optimizeDeps: {
- exclude: ['storybook'],
+ exclude: ['react-datepicker', 'storybook'],
},
plugins: [react()],
});
diff --git a/yarn.lock b/yarn.lock
index 6146c1e8..ce575f6a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3095,6 +3095,32 @@ __metadata:
languageName: node
linkType: hard
+"@floating-ui/react-dom@npm:^2.1.1":
+ version: 2.1.1
+ resolution: "@floating-ui/react-dom@npm:2.1.1"
+ dependencies:
+ "@floating-ui/dom": "npm:^1.0.0"
+ peerDependencies:
+ react: ">=16.8.0"
+ react-dom: ">=16.8.0"
+ checksum: 10c0/732ab64600c511ceb0563b87bc557aa61789fec4f416a3f092bab89e508fa1d3ee5ade0f42051cc56eb5e4db867b87ab7fd48ce82db9fd4c01d94ffa08f60115
+ languageName: node
+ linkType: hard
+
+"@floating-ui/react@npm:^0.26.2":
+ version: 0.26.20
+ resolution: "@floating-ui/react@npm:0.26.20"
+ dependencies:
+ "@floating-ui/react-dom": "npm:^2.1.1"
+ "@floating-ui/utils": "npm:^0.2.5"
+ tabbable: "npm:^6.0.0"
+ peerDependencies:
+ react: ">=16.8.0"
+ react-dom: ">=16.8.0"
+ checksum: 10c0/2fe96b8123734a9facb560b235bbb9a21849890f7a84a48c4e1d496dde045aafecb4575c3e035413f2d88a8324651c85b018bfbf8d57eb1fb9e469cdd211ca86
+ languageName: node
+ linkType: hard
+
"@floating-ui/utils@npm:^0.2.0":
version: 0.2.2
resolution: "@floating-ui/utils@npm:0.2.2"
@@ -3102,6 +3128,13 @@ __metadata:
languageName: node
linkType: hard
+"@floating-ui/utils@npm:^0.2.5":
+ version: 0.2.5
+ resolution: "@floating-ui/utils@npm:0.2.5"
+ checksum: 10c0/9e1c7330433c3a8f226c5a44ed1dcdda13b313c4126ce3281f970d1e471b1c9fd9e1559cc76a0592af25d55a3f81afe1a5778aa7b80e51c9fa01930cd1d5557e
+ languageName: node
+ linkType: hard
+
"@gar/promisify@npm:^1.1.3":
version: 1.1.3
resolution: "@gar/promisify@npm:1.1.3"
@@ -6216,6 +6249,7 @@ __metadata:
patch-package: "npm:^8.0.0"
prettier: "npm:^3.3.3"
react: "npm:18.2.0"
+ react-datepicker: "npm:^7.3.0"
react-dom: "npm:18.2.0"
rollup: "npm:^2.70.1"
rollup-plugin-typescript2: "npm:^0.36.0"
@@ -8388,6 +8422,13 @@ __metadata:
languageName: node
linkType: hard
+"clsx@npm:^2.1.0":
+ version: 2.1.1
+ resolution: "clsx@npm:2.1.1"
+ checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839
+ languageName: node
+ linkType: hard
+
"cmd-shim@npm:^5.0.0":
version: 5.0.0
resolution: "cmd-shim@npm:5.0.0"
@@ -8876,6 +8917,13 @@ __metadata:
languageName: node
linkType: hard
+"date-fns@npm:^3.3.1":
+ version: 3.6.0
+ resolution: "date-fns@npm:3.6.0"
+ checksum: 10c0/0b5fb981590ef2f8e5a3ba6cd6d77faece0ea7f7158948f2eaae7bbb7c80a8f63ae30b01236c2923cf89bb3719c33aeb150c715ea4fe4e86e37dcf06bed42fb6
+ languageName: node
+ linkType: hard
+
"dateformat@npm:^3.0.0":
version: 3.0.3
resolution: "dateformat@npm:3.0.3"
@@ -15937,6 +15985,22 @@ __metadata:
languageName: node
linkType: hard
+"react-datepicker@npm:^7.3.0":
+ version: 7.3.0
+ resolution: "react-datepicker@npm:7.3.0"
+ dependencies:
+ "@floating-ui/react": "npm:^0.26.2"
+ clsx: "npm:^2.1.0"
+ date-fns: "npm:^3.3.1"
+ prop-types: "npm:^15.7.2"
+ react-onclickoutside: "npm:^6.13.0"
+ peerDependencies:
+ react: ^16.9.0 || ^17 || ^18
+ react-dom: ^16.9.0 || ^17 || ^18
+ checksum: 10c0/a926c896fbd827bd6195e5fdbf98bf48fc86a076112bcff38dcbd580da24a98c0c8f5ed1a1dfb2d91076134e41b209e118ccd41cbc3b2036e94c54af25e1788c
+ languageName: node
+ linkType: hard
+
"react-docgen-typescript@npm:^2.2.2":
version: 2.2.2
resolution: "react-docgen-typescript@npm:2.2.2"
@@ -16030,6 +16094,16 @@ __metadata:
languageName: node
linkType: hard
+"react-onclickoutside@npm:^6.13.0":
+ version: 6.13.1
+ resolution: "react-onclickoutside@npm:6.13.1"
+ peerDependencies:
+ react: ^15.5.x || ^16.x || ^17.x || ^18.x
+ react-dom: ^15.5.x || ^16.x || ^17.x || ^18.x
+ checksum: 10c0/93b02c493332ca7f8b480a1e185386c4af55d1daf33af2f68ddf440e4fc9b22ad13f59d7e204694aa31c1c2e332f0766c7ee302fe3b0cecbd0e0b45d55905c96
+ languageName: node
+ linkType: hard
+
"react-refresh@npm:^0.14.2":
version: 0.14.2
resolution: "react-refresh@npm:0.14.2"
@@ -17762,6 +17836,13 @@ __metadata:
languageName: node
linkType: hard
+"tabbable@npm:^6.0.0":
+ version: 6.2.0
+ resolution: "tabbable@npm:6.2.0"
+ checksum: 10c0/ced8b38f05f2de62cd46836d77c2646c42b8c9713f5bd265daf0e78ff5ac73d3ba48a7ca45f348bafeef29b23da7187c72250742d37627883ef89cbd7fa76898
+ languageName: node
+ linkType: hard
+
"tar@npm:^6.1.0, tar@npm:^6.1.11, tar@npm:^6.1.2, tar@npm:^6.2.0, tar@npm:^6.2.1":
version: 6.2.1
resolution: "tar@npm:6.2.1"