From d53b3545daef21705369c780948ae1223e104f44 Mon Sep 17 00:00:00 2001 From: IvanHoncharenko Date: Tue, 18 Jul 2023 15:19:49 +0300 Subject: [PATCH 1/2] Basic datepicker implemented --- package-lock.json | 30 ++ package.json | 1 + src/components/DatePicker/DatePicker.tsx | 52 +++ .../DatePicker/interfaces/IDatePicker.ts | 29 ++ .../DatePicker/sass/DatePicker.module.scss | 370 ++++++++++++++++++ .../sass/DropdownSelectItem.module.scss | 2 +- src/sass/colors.scss | 5 +- src/stories/DatePicker.stories.tsx | 15 + 8 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 src/components/DatePicker/DatePicker.tsx create mode 100644 src/components/DatePicker/interfaces/IDatePicker.ts create mode 100644 src/components/DatePicker/sass/DatePicker.module.scss create mode 100644 src/stories/DatePicker.stories.tsx diff --git a/package-lock.json b/package-lock.json index d63b2ac..54abfad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "i18next": "^22.4.11", "i18next-browser-languagedetector": "^7.0.1", "react": "^18.2.0", + "react-day-picker": "^8.7.1", "react-dom": "^18.2.0", "react-hook-form": "^7.43.7", "react-i18next": "^12.2.0", @@ -15550,6 +15551,22 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -28813,6 +28830,19 @@ "node": ">=14" } }, + "node_modules/react-day-picker": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.7.1.tgz", + "integrity": "sha512-Gv426AW8b151CZfh3aP5RUGztLwHB/EyJgWZ5iMgtzbFBkjHfG6Y66CIQFMWGLnYjsQ9DYSJRmJ5S0Pg5HWKjA==", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "date-fns": "^2.28.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", diff --git a/package.json b/package.json index 5b4978d..f359cc7 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "i18next": "^22.4.11", "i18next-browser-languagedetector": "^7.0.1", "react": "^18.2.0", + "react-day-picker": "^8.7.1", "react-dom": "^18.2.0", "react-hook-form": "^7.43.7", "react-i18next": "^12.2.0", diff --git a/src/components/DatePicker/DatePicker.tsx b/src/components/DatePicker/DatePicker.tsx new file mode 100644 index 0000000..337608e --- /dev/null +++ b/src/components/DatePicker/DatePicker.tsx @@ -0,0 +1,52 @@ +import React, { FC } from 'react'; +import { DayPicker } from 'react-day-picker'; +import 'react-day-picker/dist/style.css'; +import styles from './sass/DatePicker.module.scss'; +import { IDatePickerProps } from './interfaces/IDatePicker'; + +const DatePicker: FC = ({ + className = '', + selected, + numberOfMonths = 1, + fromDate, + toDate, + onDayClick, + onMonthChange = () => null, + modifiersDays, + disabledDays, + month, + footer, +}) => { + const modifiers = { ...modifiersDays }; + const modifiersClassNames = { + holidays: styles.holidays, + pending: styles.pending, + approved: styles.approved, + expired: styles.expired, + start: styles.day_range_start, + end: styles.day_range_end, + }; + + return ( + onMonthChange(date)} + month={month} + disabled={disabledDays} + footer={footer} + /> + ); +}; + +export default DatePicker; diff --git a/src/components/DatePicker/interfaces/IDatePicker.ts b/src/components/DatePicker/interfaces/IDatePicker.ts new file mode 100644 index 0000000..32dbbef --- /dev/null +++ b/src/components/DatePicker/interfaces/IDatePicker.ts @@ -0,0 +1,29 @@ +import { DayClickEventHandler, Matcher } from 'react-day-picker'; + +interface IModifiersDay { + from: Date + to: Date +} + +interface IModifiersDays { + pending: IModifiersDay[] +} + +export interface IDatePickerProps { + className?: string, + selected: Matcher, + numberOfMonths?: number, + onDayClick?: DayClickEventHandler, + onMonthChange?: (month: Date) => void, + month?: Date, + modifiersDays?: IModifiersDays + approved: IModifiersDay[], + start: Date, + end: Date, + expired: Date, + disabledDays?: Date, + fromDate?: Date, + toDate?: Date, + footer?: string, + holidays?: Date[] +} diff --git a/src/components/DatePicker/sass/DatePicker.module.scss b/src/components/DatePicker/sass/DatePicker.module.scss new file mode 100644 index 0000000..ad1926b --- /dev/null +++ b/src/components/DatePicker/sass/DatePicker.module.scss @@ -0,0 +1,370 @@ +@import "../../../sass/colors"; + +.calendar { + &.absolute { + padding: 20px 12px 24px; + background: $white; + box-shadow: 0 4px 22px $shadow; + border-radius: 4px; + position: absolute; + z-index: 11; + } + + .reset { + padding-left: 10px; + margin-top: 10px; + + button { + color: $primary; + + &:hover { + text-decoration: underline; + } + } + } +} + +.datepicker { + background: $white; + box-shadow: 2px 4px 48px 0 $dark-shadow; + border-radius: 4px; + width: fit-content; + padding: 24px; + + .months { + display: flex; + + .month { + margin: 0 1em; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } + + .row { + height: 100%; + vertical-align: middle; + } + + .caption { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 2px 0 10px; + text-align: left; + margin-bottom: 24px; + + .caption_label { + font-size: 14px; + font-weight: bold; + color: #363A42; + } + } + + .head_cell { + vertical-align: middle; + text-transform: uppercase; + text-align: center; + font-weight: 600; + font-size: 10px; + height: 12px; + color: $blue-gray; + padding: 0 0 8px; + } + + .cell { + width: 34px; + height: 34px; + padding: 0; + text-align: center; + } + + .day { + display: flex; + overflow: hidden; + align-items: center; + justify-content: center; + box-sizing: border-box; + width: 34px; + max-width: 34px; + height: 34px; + margin: 0; + border: 2px solid transparent; + font-size: 13px; + + &:hover:not(:disabled) { + background-color: $light-blue; + color: $dark-gray; + } + } + + .button { + border: 2px solid transparent; + + &:not(:disabled) { + cursor: pointer; + } + + &:disabled { + color: $blue-gray; + background-color: $white; + opacity: 0.5; + } + + &:not(.day_range_start) { + &.holidays { + color: $red; + background-color: $white; + } + } + + + &.pending { + background-color: $light-warning; + } + + &.approved { + background-color: $light-warning; + } + + &.expired { + display: flex; + flex-direction: column; + border: 2px solid $success; + border-radius: 4px; + color: $success; + + span:last-of-type { + font-weight: 600; + font-size: 7px; + line-height: 6px; + text-align: center; + color: $success; + } + + &.day_range_start, + &.day_range_end { + border-color: transparent; + + span { + color: $white; + } + } + } + } + + .nav_button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + padding: 0.25em; + border-radius: 4px; + + &:hover:not(:disabled) { + background-color: $light-gray; + } + } + + .tfoot { + width: 100%; + color: $warning; + text-align: center; + font-size: 14px; + } + } + } + + &:not(.multiple_months) { + .months { + .month { + .caption { + justify-content: center; + + .nav { + position: absolute; + left: 3px; + right: 1px; + display: flex; + justify-content: space-between; + } + + & button:disabled { + opacity: 0.5; + } + } + } + } + } + + &.multiple_months { + .month { + .caption { + padding: 0; + justify-content: center; + + .nav { + position: absolute; + } + } + + &.caption_end { + .caption { + .nav { + right: 2px; + } + } + } + + &.caption_start { + .caption { + .nav { + left: 2px; + } + } + } + } + } + + .day_selected { + &:not(:disabled):not(.day_range_start):not(.day_range_end), + &:active:not(:disabled), + &:hover:not(:disabled) { + background-color: $light-blue; + } + } + + .pending { + background-color: $light-warning + } + + .approved { + background-color: $light-success; + } + + .day_range_start, + .day_range_end { + background-color: $primary; + color: $white; + } + + .day_range_start { + &:not(.day_range_end) { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + } + } + + .day_range_end { + &:not(.day_range_start) { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + } + } + + .day_range_start.day_range_end { + color: #FFFFFF; + background-color: #0072FF; + border-radius: 4px; + } +} + +.bottom { + display: flex; + justify-content: flex-end; + margin-top: 24px; + padding: 0 12px; + + button { + padding: 9px 12px; + font-weight: 600; + font-size: 12px; + + &[aria-label^='cancel_'] { + margin-right: 8px; + border: 1px solid $light-gray; + } + } +} + +.top { + display: flex; + justify-content: space-between; + margin-bottom: 28px; + padding: 0 12px; + gap: 20px; + + > div { + display: flex; + align-items: center; + } + + .separator { + font-size: 14px; + background: $dark-gray; + display: block; + width: 7px; + height: 1px; + margin: 0 8px; + } + + input { + width: 105px; + border: 1px solid $primary-border; + border-radius: 4px; + padding: 8px 12px; + font-size: 13px; + color: #363A42; + + &:focus { + color: $dark-gray; + caret-color: $primary; + border: 1px solid $primary; + + &::-webkit-input-placeholder { + color: transparent; + } + } + } + + .lastWeek, .lastMonth { + background: $light-blue; + border-radius: 4px; + color: $primary; + font-size: 13px; + width: 111px; + height: 32px; + } +} + +.btn { + width: 100%; + justify-content: flex-start; + gap: 10px; + height: 40px; + border-radius: 4px; + border: 1px solid $primary-border; + padding: 12px 16px; + + .icon { + position: absolute; + right: 16px; + color: $gray; + } + + &.open { + background-color: $outlined-hover; + border-color: $outlined-hover; + + .icon { + color: $primary; + width: 16px; + } + } +} diff --git a/src/components/DropdownSelect/sass/DropdownSelectItem.module.scss b/src/components/DropdownSelect/sass/DropdownSelectItem.module.scss index 11670a9..68689bb 100644 --- a/src/components/DropdownSelect/sass/DropdownSelectItem.module.scss +++ b/src/components/DropdownSelect/sass/DropdownSelectItem.module.scss @@ -4,6 +4,6 @@ padding: 6px 16px; &:hover { - background-color: $lightest-blue; + background-color: $outlined-hover; } } diff --git a/src/sass/colors.scss b/src/sass/colors.scss index ad261eb..e9975b1 100644 --- a/src/sass/colors.scss +++ b/src/sass/colors.scss @@ -2,10 +2,11 @@ $white: #FFF; $red: #FF2E2E; $primary: #0072FF; $primary-hover: #2988FF; +$blue-gray: #616F8A; $outlined-hover: #F2F8FF; $light-blue: #E5F0FF; -$lightest-blue: #F2F8FF; $success: #36B37E; +$light-success: #D7F7D7; $error: #B44747; $error-background: #FAC3C3; $warning: #FFA90B; @@ -18,3 +19,5 @@ $light-gray: #EBEEF2; $light-gray-2: #C4C4C4; $light-yellow: #FDFAEB; $transparent: #FFFFFF00; +$shadow: #2B2C3028; +$dark-shadow: #00000028; \ No newline at end of file diff --git a/src/stories/DatePicker.stories.tsx b/src/stories/DatePicker.stories.tsx new file mode 100644 index 0000000..ae94ce6 --- /dev/null +++ b/src/stories/DatePicker.stories.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import DatePicker from '../components/DatePicker/DatePicker'; + +export default { + title: 'Example/DatePicker', + component: DatePicker, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + +}; From a2354f8b3d40de7e470403523773bf56bc24f353 Mon Sep 17 00:00:00 2001 From: IvanHoncharenko Date: Tue, 18 Jul 2023 15:21:11 +0300 Subject: [PATCH 2/2] Basic datepicker implemented --- src/components/DropdownSelect/sass/DropdownSelect.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DropdownSelect/sass/DropdownSelect.module.scss b/src/components/DropdownSelect/sass/DropdownSelect.module.scss index f6873c0..53c3009 100644 --- a/src/components/DropdownSelect/sass/DropdownSelect.module.scss +++ b/src/components/DropdownSelect/sass/DropdownSelect.module.scss @@ -9,7 +9,7 @@ padding: 6px 16px; &:hover { - background-color: $lightest-blue; + background-color: $light-blue; } } }