diff --git a/packages/ui-toolkit/package.json b/packages/ui-toolkit/package.json index 0f2a4f2f..05a0f43e 100644 --- a/packages/ui-toolkit/package.json +++ b/packages/ui-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "@groww-tech/ui-toolkit", - "version": "0.4.5", + "version": "0.4.6", "description": "A lightning nature UI", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", diff --git a/packages/ui-toolkit/src/components/molecules/Calendar/Calendar.tsx b/packages/ui-toolkit/src/components/molecules/Calendar/Calendar.tsx index 87a085b7..91884086 100644 --- a/packages/ui-toolkit/src/components/molecules/Calendar/Calendar.tsx +++ b/packages/ui-toolkit/src/components/molecules/Calendar/Calendar.tsx @@ -2,6 +2,7 @@ import React from 'react'; import MonthCalendar from './MonthCalendar'; import DateCalendar from './DateCalendar'; +import { CalendarProps } from './calendar.types'; export const CALENDAR_TYPE = { MONTH: 'MONTH', @@ -22,34 +23,22 @@ const Calendar = (props:Props) => { const emptyFunction = () => void 0; - if (type === CALENDAR_TYPE.MONTH) { - - return ( - - ); - - } else if (type === CALENDAR_TYPE.DATE) { - return ( - - ); - - } - - return
; + const CalendarComponent = type === CALENDAR_TYPE.MONTH ? MonthCalendar : DateCalendar; + + return ( + + ); }; export type Props = { - type: ValueOf; -} & Partial> -& Partial>; + type: keyof typeof CALENDAR_TYPE; +} & CalendarProps; export default Calendar; diff --git a/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/dateCalendar.css b/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/dateCalendar.css index 060ecfea..364ccb7b 100644 --- a/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/dateCalendar.css +++ b/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/dateCalendar.css @@ -5,12 +5,7 @@ cursor: auto; } -.cc12YearBox { - justify-content: space-between; -} - .cc12WeekNameBox { - justify-content: space-between; margin-top: 14px; text-align: center; } @@ -32,14 +27,15 @@ .cc12DateNotSelected {} -.cc12DateSelected { - background: var(--green500); -} - +/* NOTE: Here below order matters as active, hover state color override */ .cc12DateNotSelected:hover { background: var(--overlay30); } +.cc12DateCurrent { + background: var(--green300); +} + .cc12DateRow { justify-content: space-between; margin-top: 8px; @@ -52,4 +48,5 @@ .cc12DisableDate { opacity: 0.2; + cursor: default; } diff --git a/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/index.tsx b/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/index.tsx index 0f04686d..d0a9d769 100644 --- a/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/index.tsx +++ b/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/index.tsx @@ -7,7 +7,8 @@ import { KeyboardDoubleArrowRight } from '@groww-tech/icon-store/mi'; -import { getDatesArray, getMonthAbbrByIndex } from './dateCalendarUtils'; +import { compareDate, getDatesArray, getMonthAbbrByIndex } from '../calendarUtils'; +import { CalendarProps as Props } from '../calendar.types'; import './dateCalendar.css'; @@ -16,7 +17,7 @@ const WEEK_DAYS = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]; class DateCalendar extends React.PureComponent { static defaultProps = { - highlightCurrentDate: false, + highlightCurrentDate: true, minDate: null, maxDate: null }; @@ -24,12 +25,14 @@ class DateCalendar extends React.PureComponent { state:State = { dateToShow: this.props.currentDate, - dates: getDatesArray(this.props.currentDate) + dates: getDatesArray(this.props.currentDate), + selectedDate: null }; componentDidUpdate(prevProps: Props) { - if (prevProps.currentDate.getDate() != this.props.currentDate.getDate()) { + + if (prevProps.currentDate.getDate() !== this.props.currentDate.getDate()) { this.setState({ dateToShow: this.props.currentDate, dates: getDatesArray(this.props.currentDate) @@ -43,15 +46,9 @@ class DateCalendar extends React.PureComponent { return (
-
- {this.getYearUI()} -
-
- {this.getMonthUI()} -
-
- {this.getDatesUI()} -
+ {this.getYearUI()} + {this.getMonthUI()} + {this.getDatesUI()}
); @@ -60,35 +57,59 @@ class DateCalendar extends React.PureComponent { getYearUI = () => { const { dateToShow } = this.state; + const maxDate = this.props.maxDate ? new Date(this.props.maxDate) : null; + const minDate = this.props.minDate ? new Date(this.props.minDate) : null; + + const hasNextYear = !(maxDate && dateToShow.getFullYear() === maxDate.getFullYear()); + const hasPrevYear = !(minDate && dateToShow.getFullYear() === minDate.getFullYear()); + const hasNextMonth = !(maxDate && dateToShow.getMonth() === maxDate.getMonth() && dateToShow.getFullYear() === maxDate.getFullYear()); + const hasPrevMonth = !(minDate && dateToShow.getMonth() === minDate.getMonth() && dateToShow.getFullYear() === minDate.getFullYear()); + + const handlers = { + 'PREVIOUS_YEAR': () => { + if (hasPrevYear) this.goToPreviousYear(); + }, + 'PREVIOUS_MONTH': () => { + if (hasPrevMonth) this.goToPreviousMonth(); + }, + 'NEXT_MONTH': () => { + if (hasNextMonth) this.goToNextMonth(); + }, + 'NEXT_YEAR': () => { + if (hasNextYear) this.goToNextYear(); + } + }; return ( -
-
-
- -
-
- -
-
-
{getMonthAbbrByIndex(dateToShow.getMonth() + 1)} {dateToShow.getFullYear()}
-
-
- -
-
- -
+
+
+ +
+
+ +
+
+ {getMonthAbbrByIndex(dateToShow.getMonth() + 1)} {dateToShow.getFullYear()} +
+
+ +
+
+
); @@ -97,74 +118,83 @@ class DateCalendar extends React.PureComponent { getMonthUI = () => { return ( - (
-
- { - WEEK_DAYS.map(day => ( -
-
- {day} -
-
- )) - } -
-
) +
+ { + WEEK_DAYS.map((day) => ( + + {day} + + )) + } +
); } getDatesUI = () => { - const { dates, dateToShow } = this.state; + const { dates, dateToShow, selectedDate } = this.state; const { minDate, maxDate } = this.props; return (
-
- { - dates.map((dateArr, datesArrIndex) => { - return ( -
- { - dateArr.map((date) => { - if (date !== null) { - const newDate = new Date(dateToShow); - - newDate.setDate(date); - - const dateSelected = this.isDateSelected(date); - - return ( - (
-
this.onDateClick(date)} - > - {date} -
-
) - ); - } + { + dates.map((dateArr, datesArrIndex) => { + + return ( +
+ { + dateArr.map((date, index) => { + if (date !== null) { + const newDate = new Date(dateToShow); + + newDate.setDate(date); + + const dateSelected = newDate.getTime() === selectedDate?.getTime(); + const isDisabled = (minDate && compareDate(minDate, newDate)) || (maxDate && compareDate(newDate, maxDate)); + + + const onClickHandler = () => { + if (!isDisabled) this.onDateClick(date); + }; return ( -
 
+
+ + {date} + +
); - }) - } -
- ); - }) - } -
+ } + + // NOTE: Renders the empty date block or date offsets + return ( +
 
+ ); + }) + } +
+ ); + }) + }
); } @@ -215,46 +245,27 @@ class DateCalendar extends React.PureComponent { onDateClick = (date: number) => { - const { minDate, maxDate, onDateChange } = this.props; + const { onDateChange } = this.props; const { dateToShow } = this.state; const newDate = new Date(dateToShow); newDate.setDate(date); - if (minDate && (this.compareDate(minDate, newDate))) { - return; - } - - if (maxDate && (this.compareDate(newDate, maxDate))) { - return; - } - onDateChange(newDate); - } - /* check date1 is greater than date2 */ - compareDate = (date1: Date, date2: Date) => { - const d1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate(), 0, 0, 0); - const d2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate(), 0, 0, 0); - - return d1.getTime() > d2.getTime(); + this.setState({ + dateToShow: newDate, + selectedDate: newDate + }); } } -type Props = { - currentDate: Date; - onDateChange: (date:Date)=>void; - highlightCurrentDate: boolean; - minDate: Date | null; - maxDate: Date | null; -}; - - type State = { dateToShow: Date; dates: (number | null)[][]; + selectedDate: Date | null; } diff --git a/packages/ui-toolkit/src/components/molecules/Calendar/MonthCalendar/index.tsx b/packages/ui-toolkit/src/components/molecules/Calendar/MonthCalendar/index.tsx index 6911a423..01479958 100644 --- a/packages/ui-toolkit/src/components/molecules/Calendar/MonthCalendar/index.tsx +++ b/packages/ui-toolkit/src/components/molecules/Calendar/MonthCalendar/index.tsx @@ -3,61 +3,96 @@ import cn from 'classnames'; import { KeyboardArrowLeft, KeyboardArrowRight } from '@groww-tech/icon-store/mi'; import './monthCalendar.css'; +import { compareDate } from '../calendarUtils'; +import { CalendarProps as Props } from '../calendar.types'; const MONTHS = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]; class MonthCalendar extends React.PureComponent { + static defaultProps = { + highlightCurrentDate: true, + minDate: null, + maxDate: null + }; + state:State = { - dateToShow: this.props.currentDate + dateToShow: this.props.currentDate, + selectedDate: null }; render() { - const { dateToShow } = this.state; - const { currentDate } = this.props; - - const currentMonthIndex = currentDate.getMonth(); - const presentDate = new Date(); + const { dateToShow, selectedDate } = this.state; + const maxDate = this.props.maxDate ? new Date(this.props.maxDate) : null; + const minDate = this.props.minDate ? new Date(this.props.minDate) : null; + + const hasNextYear = !(maxDate && dateToShow.getFullYear() === maxDate.getFullYear()); + const hasPrevYear = !(minDate && dateToShow.getFullYear() === minDate.getFullYear()); + + const handlers = { + 'PREVIOUS_MONTH': () => { + if (hasPrevYear) this.goToPreviousYear(); + }, + 'NEXT_MONTH': () => { + if (hasNextYear) this.goToNextYear(); + } + }; return (
-
-
- +
+
+
-
{dateToShow.getFullYear()}
-
- + {dateToShow.getFullYear()} +
+
-
+
{ - MONTHS.map((month, index) => ( -
-
presentDate.getMonth()) && (presentDate.getFullYear() === dateToShow.getFullYear())) - }) - } - onClick={() => this.onMonthClick(index)} + MONTHS.map((month, index) => { + const newDate = new Date(dateToShow); + + newDate.setMonth(index); + const monthSelected = newDate.getTime() === selectedDate?.getTime(); + const isDisabled = (minDate && compareDate(minDate, newDate)) || (maxDate && compareDate(newDate, maxDate)); + + + const onClickHandler = () => { + if (!isDisabled) this.onMonthClick(index); + }; + + return ( +
- {month} +
+ {month} +
-
- )) + ); + }) }
@@ -66,57 +101,53 @@ class MonthCalendar extends React.PureComponent { } - onMonthClick = (index:number) => { - const dateToShow = this.state.dateToShow; - const newDate = new Date(dateToShow.toString()); - const presentDate = new Date(); - const presentMonth = presentDate.getMonth(); + isMonthSelected = (month: number) => { + const { currentDate } = this.props; + const { dateToShow } = this.state; + const currentMonthIndex = currentDate.getMonth(); - if ((index > presentMonth) && (presentDate.getFullYear() === newDate.getFullYear())) { - return; - } + return month === currentMonthIndex && currentDate.getFullYear() === dateToShow.getFullYear(); + } + + + onMonthClick = (index:number) => { + const { onDateChange } = this.props; + const { dateToShow } = this.state; + const newDate = new Date(dateToShow); - newDate.setDate(1); newDate.setMonth(index); - this.props.onDateChange(newDate); + onDateChange(newDate); this.setState({ - dateToShow: newDate + dateToShow: newDate, + selectedDate: newDate }); } - handlePrevYearClick = () => { - const dateToShow = this.state.dateToShow; - const newDate = new Date(dateToShow.toString()); + goToPreviousYear = () => { + const { dateToShow } = this.state; + const newDate = new Date(dateToShow); newDate.setFullYear(dateToShow.getFullYear() - 1); this.setState({ dateToShow: newDate }); - } - + }; - handleForwardYearClick = () => { - const dateToShow = this.state.dateToShow; - if (dateToShow.getFullYear() !== new Date().getFullYear()) { - const newDate = new Date(dateToShow.toString()); + goToNextYear = () => { + const { dateToShow } = this.state; + const newDate = new Date(dateToShow); - newDate.setFullYear(dateToShow.getFullYear() + 1); - this.setState({ dateToShow: newDate }); - } - } + newDate.setFullYear(dateToShow.getFullYear() + 1); + this.setState({ dateToShow: newDate }); + }; } -type Props = { - currentDate: Date; - onDateChange: (date:Date) => void; -}; - - type State = { dateToShow: Date; + selectedDate: Date | null; } export default MonthCalendar; diff --git a/packages/ui-toolkit/src/components/molecules/Calendar/MonthCalendar/monthCalendar.css b/packages/ui-toolkit/src/components/molecules/Calendar/MonthCalendar/monthCalendar.css index 3b3f8444..2f31e09e 100644 --- a/packages/ui-toolkit/src/components/molecules/Calendar/MonthCalendar/monthCalendar.css +++ b/packages/ui-toolkit/src/components/molecules/Calendar/MonthCalendar/monthCalendar.css @@ -3,10 +3,6 @@ width: 220px; } -.mn12YearRow { - justify-content: space-between; -} - .mn12Month { text-align: center; min-width: 33%; @@ -20,20 +16,25 @@ margin: 3px auto; } +/* NOTE: Here below order matters as active, hover state color override */ .mn12MonthBack:hover { background-color: var(--overlay30); } -.mn12MonthTextSelected { - background-color: var(--green500); +.mn12MonthCurrent { + background-color: var(--green300); } .mn12MonthBox { flex-wrap: wrap; - justify-content: space-between; margin-top: 12px; } .mn12YearIcon { font-size: 22px !important; } + +.cc12DisableDate { + opacity: 0.2; + cursor: default; +} \ No newline at end of file diff --git a/packages/ui-toolkit/src/components/molecules/Calendar/calendar.types.ts b/packages/ui-toolkit/src/components/molecules/Calendar/calendar.types.ts new file mode 100644 index 00000000..cca56198 --- /dev/null +++ b/packages/ui-toolkit/src/components/molecules/Calendar/calendar.types.ts @@ -0,0 +1,7 @@ +export type CalendarProps = { + currentDate: Date; + onDateChange: (date:Date)=>void; + highlightCurrentDate?: boolean; + minDate?: Date | null; + maxDate?: Date | null; +}; diff --git a/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/dateCalendarUtils.ts b/packages/ui-toolkit/src/components/molecules/Calendar/calendarUtils.ts similarity index 72% rename from packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/dateCalendarUtils.ts rename to packages/ui-toolkit/src/components/molecules/Calendar/calendarUtils.ts index 9a6ae793..1f9e3631 100644 --- a/packages/ui-toolkit/src/components/molecules/Calendar/DateCalendar/dateCalendarUtils.ts +++ b/packages/ui-toolkit/src/components/molecules/Calendar/calendarUtils.ts @@ -47,3 +47,15 @@ export function getMonthAbbrByIndex(monthNumber: number): string { return monthNames[ monthNumber - 1 ]; } + + + /* check date1 is greater than date2 */ +export function compareDate(date1: Date, date2: Date) { + // This conversion is required because there is no validation below and .get methods only exists on Date instance unix timestamp will fail + date1 = new Date(date1); + date2 = new Date(date2); + const d1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate(), 0, 0, 0); + const d2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate(), 0, 0, 0); + + return d1.getTime() > d2.getTime(); +} diff --git a/packages/ui-toolkit/stories/Calander.stories.tsx b/packages/ui-toolkit/stories/Calander.stories.tsx index 4d797ceb..e699f50a 100644 --- a/packages/ui-toolkit/stories/Calander.stories.tsx +++ b/packages/ui-toolkit/stories/Calander.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { Story } from '@storybook/react'; import { Calendar } from '../src/components/molecules'; -import { Props as CalendarProps, CALENDAR_TYPE } from '../src/components/molecules/Calendar/Calendar'; +import { Props, CALENDAR_TYPE } from '../src/components/molecules/Calendar/Calendar'; export default { title: 'Calendar', @@ -11,7 +11,7 @@ export default { }; -const Template: Story = (args) => ; +const Template: Story = (args) => ; export const Month = Template.bind({});