Skip to content

Commit

Permalink
EPMRPP-97286 || fix datapicker (#64)
Browse files Browse the repository at this point in the history
* EPMRPP-97286 || fix datapicker

* fix resolve

* remove picker from vite config
BlazarQSO authored Jan 29, 2025
1 parent 3a05483 commit e91dec3
Showing 20 changed files with 1,331 additions and 0 deletions.
34 changes: 34 additions & 0 deletions src/components/dataPicker2/dataPicker2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## **DatePicker**

width - 380px. Max height - flexible.

### Props:

- **disabled** : _bool_, optional, default = false
- **endDate** : _undefined_,_Date_, optional, default = undefined
- **startDate** : _undefined_,_Date_, optional, default = undefined
- **customClassName** : _string_, optional, default : ""
- **customTimeInput** : _ReactElement_, optional, default = undefined
- **shouldCloseOnSelect** : _bool_, optional, default = false
- **showPopperArrow** : _bool_, optional, default=false
- **popperClassName** : _string_, optional, default = ""
- **calendarClassName** : _string_, optional, default = ""
- **fixedHeight** : _bool_, optional, default = false
- **headerNodes** : _node_, optional, default = null
- **value** : _string_, optional, default = new Date()
- **language** : _string_ or _Locale_, optional, default = 'en'
- **yearsOptions** : _number[]_, optional, default = 'en'
- **placeholder** : _string_, optional, default = 'MM-DD-YYYY'
- **dateFormat** : _string_, optional, default = 'MM-dd-yyyy'
- **selects** : _string('start' | 'end')_, optional, default = ''
- **value** : _Date_, optional, default = null

### Events:

- **onChange**
- **onBlur**
- **onFocus**


### Setup:
- **registerDatePickerLocale**
262 changes: 262 additions & 0 deletions src/components/dataPicker2/dataPicker2/datePicker.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
@import 'src/assets/styles/variables/typography';
@import 'src/assets/styles/mixins/font-scale';

$DAY_OF_THE_WEEK_Z_INDEX: 3;
$DAY_OF_THE_WEEK_HOVERED_Z_INDEX: 2;
$POPOVER_Z_INDEX: 10;

@mixin setMonthContainerProperties {
.react-datepicker__month-container {
width: 100%;
height: 100%;
float: none;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
@include setCustomHeaderProperties;
@include setMothProperties;
}
}

@mixin setCustomHeaderProperties {
.react-datepicker__header.react-datepicker__header--custom {
width: 100%;
background-color: var(--rp-ui-base-bg-000);
border-bottom: none;
padding: 0;
@include setDaysNameOfTheWeekProperties;
}
}

@mixin setMothProperties {
.react-datepicker__month {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
row-gap: 8px;
@include setDaysDigitsContainerProperties;
}
}

@mixin setDaysDigitsContainerProperties {
.react-datepicker__week {
height: 32px;
display: flex;
align-items: center;
justify-content: space-between;
font-family: var(--rp-ui-base-font-family);
color: var(--rp-ui-base-dark-e-500);
@include font-scale();
.react-datepicker__day--range-end:first-child:before {
display: none;
}
}

.react-datepicker__day {
cursor: pointer;

&.react-datepicker__day--in-selecting-range {
&:not(.react-datepicker__day--selecting-range-end, .react-datepicker__day--selecting-range-start, .react-datepicker__day--range-start, .react-datepicker__day--range-end) {
background-color: var(--rp-ui-base-bg-200);
height: 32px;
line-height: 32px;
position: relative;
}
}

&.react-datepicker__day--selected {
position: relative;
border-radius: 50%;
background-color: var(--rp-ui-base-topaz);
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
color: var(--rp-ui-base-bg-000);
}
}

.react-datepicker__day--disabled {
cursor: default;

&:hover {
border: none !important;
line-height: 40px !important;
}
}
}

@mixin setDaysNameOfTheWeekProperties {
.react-datepicker__day-names {
display: flex;
height: 40px;
justify-content: space-between;
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
@include font-scale();
color: var(--rp-ui-base-dark-e-500);
vertical-align: middle;

.react-datepicker__day-name {
width: 40px;
text-align: center;
}
}
}

@mixin removeAriaLive {
.react-datepicker__aria-live {
display: none;
}
}

@mixin setHoverState($backgroundColor, $textColor: inherit) {
border-radius: 50%;
border: 1px solid var(--rp-ui-base-topaz);
background-color: $backgroundColor;
line-height: 38px;
color: $textColor;
}

@mixin removeOutline {
&:focus-visible {
outline: none;
}
}

@mixin verticalAlign($height) {
height: $height;
line-height: $height;
}

@mixin drawBeforePseudoElement {
position: absolute;
content: '';
height: 32px;
background-color: var(--rp-ui-base-bg-200);
width: 16px;
top: 0;
left: -9px;
z-index: 1;
}

@mixin drawAfterPseudoElement($displayValue, $borderWidth) {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 40px;
height: 40px;
border: $borderWidth solid var(--rp-ui-base-topaz);
border-radius: 50%;
display: $displayValue;
z-index: $DAY_OF_THE_WEEK_HOVERED_Z_INDEX;
box-sizing: border-box;
}

:global {
@include removeAriaLive;
}

.calendar {
box-sizing: border-box;
background-color: var(--rp-ui-base-bg-000);
width: 344px;
padding: 30px 32px 32px;
border-radius: 8px;
box-shadow: 0px 8px 40px var(--rp-ui-base-dark-bg-light);
border: none;
margin-top: 4px;

:global {
@include setMonthContainerProperties;
}

.current-date,
.date {
width: 40px;
margin: 0;
box-sizing: border-box;
@include verticalAlign(40px);
@include removeOutline;
}

.date {
background-color: transparent;
border-radius: unset;
color: inherit;
text-align: center;
}

.current-date,
.current-date:hover {
position: relative;
z-index: $DAY_OF_THE_WEEK_Z_INDEX;
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
@include setHoverState(var(--rp-ui-base-topaz), var(--rp-ui-base-bg-000));
}

.date:hover:not(.current-date):not(.selected-range):not(.end-date) {
@include setHoverState(transparent);
}

.end-date {
position: relative;
border-radius: 50%;
background-color: var(--rp-ui-base-topaz);
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
color: var(--rp-ui-base-bg-000);
}

.end-date::after {
@include drawAfterPseudoElement(block, 10px);
}

.end-date::before {
@include drawBeforePseudoElement;
top: 4px;
}

.selected-range {
background-color: var(--rp-ui-base-bg-200);
border-radius: 8px;
@include verticalAlign(32px);
position: relative;
&:hover {
@include verticalAlign(40px);
border-radius: 50%;
background: var(--rp-ui-base-bg-200);
&::after {
display: block;
}
&:not(:first-child)::before {
top: 4px;
}
}
}

.selected-range::after {
@include drawAfterPseudoElement(none, 1px);
}

.selected-range:not(:first-child)::before {
@include drawBeforePseudoElement;
}

.disabled {
color: var(--rp-ui-base-e-400);
background-color: transparent;
@include removeOutline;
}
}
.popper {
z-index: $POPOVER_Z_INDEX;
}

.input {
width: 100%;
min-width: 138px;
}
121 changes: 121 additions & 0 deletions src/components/dataPicker2/dataPicker2/datePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import ReactDatePicker, { ReactDatePickerCustomHeaderProps } from 'react-datepicker';
import classNames from 'classnames/bind';
import { FC, ReactNode, useRef, ReactElement } from 'react';
import { FieldText } from '@components/fieldText';
import { CalendarIcon } from '@components/icons';
import { DatePickerHeader } from './header/datePickerHeader';
import styles from './datePicker.module.scss';

const cx = classNames.bind(styles);

const DEFAULT_LANGUAGE = 'en';
const DEFAULT_DATE_FORMAT = 'MM-dd-yyyy';

interface DatePickerProps {
onChange?: (date: Date | any) => void;

Check warning on line 15 in src/components/dataPicker2/dataPicker2/datePicker.tsx

GitHub Actions / build

Unexpected any. Specify a different type
onBlur?: () => void;
onFocus?: () => void;
headerNodes?: ReactNode;
disabled?: boolean;
shouldCloseOnSelect?: boolean;
fixedHeight?: boolean;
startDate?: Date | undefined;
endDate?: Date | undefined;
customClassName?: string;
popperClassName?: string;
calendarClassName?: string;
customTimeInput?: ReactElement;
language?: string;
yearsOptions?: number[];
placeholder?: string;
dateFormat?: string;
selects?: 'start' | 'end' | 'none';
value?: Date | null;
}

export const DatePicker2: FC<DatePickerProps> = ({
onChange = () => {},
disabled = false,
onBlur = () => {},
onFocus = () => {},
endDate = undefined,
startDate = undefined,
headerNodes = null,
customClassName = '',
customTimeInput = undefined,
shouldCloseOnSelect = true,
popperClassName = '',
calendarClassName = '',
fixedHeight = false,
language = DEFAULT_LANGUAGE,
yearsOptions = [],
placeholder = DEFAULT_DATE_FORMAT.toUpperCase(),
dateFormat = DEFAULT_DATE_FORMAT,
selects = 'start',
value = null,
}) => {
const inputRef = useRef(null);
const startDateString = startDate?.toDateString();
const endDateString = endDate?.toDateString();
const isValidEndDate = endDate && startDate && endDate > startDate;

const getDayClassName = (displayedDates: Date) => {
const displayedDateString = displayedDates.toDateString();
const isCurrentDate = displayedDateString === startDateString;
const isEndDate = isValidEndDate && displayedDateString === endDateString;

const isInsideSelectedRange =
startDate && endDate && displayedDates > startDate && displayedDates < endDate;

return cx('date', {
'current-date': isCurrentDate,
'selected-range': isInsideSelectedRange && !isEndDate,
'end-date': isEndDate && isValidEndDate,
disabled,
});
};

return (
<ReactDatePicker
customInput={
<FieldText
className={cx('input')}
defaultWidth={false}
endIcon={<CalendarIcon />}
ref={inputRef}
/>
}
placeholderText={placeholder}
selected={value}
startDate={startDate}
endDate={endDate}
minDate={selects === 'end' ? startDate : undefined}
disabled={disabled}
shouldCloseOnSelect={shouldCloseOnSelect}
fixedHeight={fixedHeight}
locale={language}
showPopperArrow={false}
dayClassName={getDayClassName}
calendarClassName={cx(calendarClassName, 'calendar')}
renderCustomHeader={(customHeaderProps: ReactDatePickerCustomHeaderProps) => (
<DatePickerHeader
{...customHeaderProps}
headerNodes={headerNodes}
customClassName={customClassName}
yearsOptions={yearsOptions}
locale={language}
/>
)}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
customTimeInput={customTimeInput}
showTimeInput={Boolean(customTimeInput)}
popperClassName={cx(popperClassName, 'popper')}
dateFormat={dateFormat}
selectsStart={selects === 'start'}
selectsEnd={selects === 'end'}
className={cx('datepicker')}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@import 'src/assets/styles/variables/typography';

@mixin arrowHoverState($arrow-color) {
&:hover:not(.disabled) {
cursor: pointer;
svg > path {
fill: $arrow-color;
}
}
}

@mixin disabledState {
opacity: 0.3;
pointer-events: none;
}

@mixin setArrowDefaultProps {
align-self: center;
width: 16px;
height: 16px;
svg {
width: 16px;
height: 16px;
}
}

.header {
display: flex;
align-items: center;
justify-content: space-between;
background-color: var(--rp-ui-base-bg-000);
padding-bottom: 18px;

.dropdowns-wrapper {
display: flex;
align-items: center;
column-gap: 8px;
}

.button-prev,
.button-next {
all: unset;

@include setArrowDefaultProps();
@include arrowHoverState(var(--rp-ui-base-e-400));
&.disabled {
@include disabledState;
}
}

.button-next {
transform: rotate(180deg);
}

.dropdown {
width: auto;

&.month-dropdown {
width: 117px;
}

.toggle-button > span {
color: var(--rp-ui-base-topaz);
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
}

.toggle-button:hover > span {
color: var(-rp-ui-base-topaz-hover);
}
}
}
117 changes: 117 additions & 0 deletions src/components/dataPicker2/dataPicker2/header/datePickerHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { FC, ReactNode, useMemo } from 'react';
import classNames from 'classnames/bind';
import { Dropdown } from '@components/dropdown';
import { CalendarArrowIcon } from '@components/icons';
import { DropdownOptionType, DropdownValue } from '@components/dropdown/types';
import { getYearsFrom } from '../utils';
import styles from './datePickerHeader.module.scss';

const cx = classNames.bind(styles);

export interface DatePickerHeaderProps {
changeYear: (year: number) => void;
changeMonth: (month: number) => void;
decreaseMonth: () => void;
increaseMonth: () => void;
headerNodes: ReactNode;
date: Date;
prevMonthButtonDisabled: boolean;
nextMonthButtonDisabled: boolean;
customClassName: string;
yearsOptions: number[];
locale: string;
}

export const DatePickerHeader: FC<DatePickerHeaderProps> = ({
date = new Date(),
changeYear = () => {},
changeMonth = () => {},
decreaseMonth = () => {},
increaseMonth = () => {},
prevMonthButtonDisabled = false,
nextMonthButtonDisabled = false,
headerNodes = null,
customClassName = '',
yearsOptions = [],
locale,
}) => {
const year = date.getFullYear();
const month = date.getMonth();

const monthDropdownOptions = useMemo(() => {
const monthList = Array(12).keys();
const formatter = new Intl.DateTimeFormat(locale, {
month: 'long',
});
const getMonthName = (monthIndex: number) => formatter.format(new Date(year, monthIndex));
const months = Array.from(monthList, getMonthName);

return months.reduce((acc: DropdownOptionType[], monthValue, monthNumber) => {
return acc.concat({
value: monthNumber,
label: monthValue,
});
}, []);
}, []);

Check warning on line 55 in src/components/dataPicker2/dataPicker2/header/datePickerHeader.tsx

GitHub Actions / build

React Hook useMemo has missing dependencies: 'locale' and 'year'. Either include them or remove the dependency array

const yearDropdownOptions: DropdownOptionType[] = useMemo(() => {
const yearValues = yearsOptions.length > 0 ? yearsOptions : getYearsFrom(year);
return yearValues.reduce(
(acc: DropdownOptionType[], yearValue) =>
acc.concat({ value: yearValue, label: `${yearValue}` }),
[],
);
}, [yearsOptions]);

Check warning on line 64 in src/components/dataPicker2/dataPicker2/header/datePickerHeader.tsx

GitHub Actions / build

React Hook useMemo has a missing dependency: 'year'. Either include it or remove the dependency array

const onMonthChange = (changedMonth: DropdownValue) => {
const numberMonth: number = changedMonth as number;
changeMonth(numberMonth);
};

const onYearChange = (changedYear: DropdownValue) => {
const numberYear: number = changedYear as number;
changeYear(numberYear);
};

return (
<>
{headerNodes && <div className={cx(customClassName)}>{headerNodes}</div>}
<div className={cx('header')}>
<button
aria-label="Previous Months"
disabled={prevMonthButtonDisabled}
onClick={decreaseMonth}
className={cx('button-prev', { disabled: prevMonthButtonDisabled })}
>
<CalendarArrowIcon />
</button>
<div className={cx('dropdowns-wrapper')}>
<Dropdown
options={monthDropdownOptions}
value={month}
onChange={onMonthChange}
transparentBackground
className={cx('dropdown', 'month-dropdown')}
toggleButtonClassName={cx('toggle-button')}
/>
<Dropdown
options={yearDropdownOptions}
value={year}
onChange={onYearChange}
transparentBackground
className={cx('dropdown')}
toggleButtonClassName={cx('toggle-button')}
/>
</div>
<button
aria-label="Next Months"
disabled={nextMonthButtonDisabled}
onClick={increaseMonth}
className={cx('button-next', { disabled: nextMonthButtonDisabled })}
>
<CalendarArrowIcon />
</button>
</div>
</>
);
};
5 changes: 5 additions & 0 deletions src/components/dataPicker2/dataPicker2/header/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DatePickerHeader } from './datePickerHeader';

export { DatePickerHeader };

export default DatePickerHeader;
5 changes: 5 additions & 0 deletions src/components/dataPicker2/dataPicker2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DatePicker2 } from './datePicker';

export { DatePicker2 };

export default DatePicker2;
11 changes: 11 additions & 0 deletions src/components/dataPicker2/dataPicker2/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Locale } from 'date-fns';
import { registerLocale } from 'react-datepicker';

export const registerDatePickerLocale = (language: string, locale: Locale) => {
registerLocale(language, locale);
};

export const getYearsFrom = (start: number, amountYearsToGenerate = 20) => {
const yearsFromCurrent = start + amountYearsToGenerate;
return new Array(yearsFromCurrent - start).fill(undefined).map((_, i) => start - i);
};
67 changes: 67 additions & 0 deletions src/components/date2/date2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import ReactDatePicker from 'react-datepicker';
import { FC, ReactNode, ReactElement } from 'react';

const DEFAULT_LANGUAGE = 'en';
const DEFAULT_DATE_FORMAT = 'MM-dd-yyyy';

interface DatePickerProps {
onChange?: (date: Date | any) => void;

Check warning on line 8 in src/components/date2/date2.tsx

GitHub Actions / build

Unexpected any. Specify a different type
onBlur?: () => void;
onFocus?: () => void;
headerNodes?: ReactNode;
disabled?: boolean;
shouldCloseOnSelect?: boolean;
fixedHeight?: boolean;
startDate?: Date | undefined;
endDate?: Date | undefined;
customClassName?: string;
popperClassName?: string;
calendarClassName?: string;
customTimeInput?: ReactElement;
language?: string;
yearsOptions?: number[];
placeholder?: string;
dateFormat?: string;
selects?: 'start' | 'end' | 'none';
value?: Date | null;
}

export const Date2: FC<DatePickerProps> = ({
onChange = () => {},
disabled = false,
onBlur = () => {},
onFocus = () => {},
endDate = undefined,
startDate = undefined,
customTimeInput = undefined,
shouldCloseOnSelect = true,
fixedHeight = false,
language = DEFAULT_LANGUAGE,
placeholder = DEFAULT_DATE_FORMAT.toUpperCase(),
dateFormat = DEFAULT_DATE_FORMAT,
selects = 'start',
value = null,
}) => {
return (
<ReactDatePicker
placeholderText={placeholder}
selected={value}
startDate={startDate}
endDate={endDate}
minDate={selects === 'end' ? startDate : undefined}
disabled={disabled}
shouldCloseOnSelect={shouldCloseOnSelect}
fixedHeight={fixedHeight}
locale={language}
showPopperArrow={false}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
customTimeInput={customTimeInput}
showTimeInput={Boolean(customTimeInput)}
dateFormat={dateFormat}
selectsStart={selects === 'start'}
selectsEnd={selects === 'end'}
/>
);
};
5 changes: 5 additions & 0 deletions src/components/date2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Date2 } from './date2';

export { Date2 };

export default Date2;
34 changes: 34 additions & 0 deletions src/components/datePicker1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## **DatePicker**

width - 380px. Max height - flexible.

### Props:

- **disabled** : _bool_, optional, default = false
- **endDate** : _undefined_,_Date_, optional, default = undefined
- **startDate** : _undefined_,_Date_, optional, default = undefined
- **customClassName** : _string_, optional, default : ""
- **customTimeInput** : _ReactElement_, optional, default = undefined
- **shouldCloseOnSelect** : _bool_, optional, default = false
- **showPopperArrow** : _bool_, optional, default=false
- **popperClassName** : _string_, optional, default = ""
- **calendarClassName** : _string_, optional, default = ""
- **fixedHeight** : _bool_, optional, default = false
- **headerNodes** : _node_, optional, default = null
- **value** : _string_, optional, default = new Date()
- **language** : _string_ or _Locale_, optional, default = 'en'
- **yearsOptions** : _number[]_, optional, default = 'en'
- **placeholder** : _string_, optional, default = 'MM-DD-YYYY'
- **dateFormat** : _string_, optional, default = 'MM-dd-yyyy'
- **selects** : _string('start' | 'end')_, optional, default = ''
- **value** : _Date_, optional, default = null

### Events:

- **onChange**
- **onBlur**
- **onFocus**


### Setup:
- **registerDatePickerLocale**
262 changes: 262 additions & 0 deletions src/components/datePicker1/datePicker.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
@import 'src/assets/styles/variables/typography';
@import 'src/assets/styles/mixins/font-scale';

$DAY_OF_THE_WEEK_Z_INDEX: 3;
$DAY_OF_THE_WEEK_HOVERED_Z_INDEX: 2;
$POPOVER_Z_INDEX: 10;

@mixin setMonthContainerProperties {
.react-datepicker__month-container {
width: 100%;
height: 100%;
float: none;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
@include setCustomHeaderProperties;
@include setMothProperties;
}
}

@mixin setCustomHeaderProperties {
.react-datepicker__header.react-datepicker__header--custom {
width: 100%;
background-color: var(--rp-ui-base-bg-000);
border-bottom: none;
padding: 0;
@include setDaysNameOfTheWeekProperties;
}
}

@mixin setMothProperties {
.react-datepicker__month {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
row-gap: 8px;
@include setDaysDigitsContainerProperties;
}
}

@mixin setDaysDigitsContainerProperties {
.react-datepicker__week {
height: 32px;
display: flex;
align-items: center;
justify-content: space-between;
font-family: var(--rp-ui-base-font-family);
color: var(--rp-ui-base-dark-e-500);
@include font-scale();
.react-datepicker__day--range-end:first-child:before {
display: none;
}
}

.react-datepicker__day {
cursor: pointer;

&.react-datepicker__day--in-selecting-range {
&:not(.react-datepicker__day--selecting-range-end, .react-datepicker__day--selecting-range-start, .react-datepicker__day--range-start, .react-datepicker__day--range-end) {
background-color: var(--rp-ui-base-bg-200);
height: 32px;
line-height: 32px;
position: relative;
}
}

&.react-datepicker__day--selected {
position: relative;
border-radius: 50%;
background-color: var(--rp-ui-base-topaz);
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
color: var(--rp-ui-base-bg-000);
}
}

.react-datepicker__day--disabled {
cursor: default;

&:hover {
border: none !important;
line-height: 40px !important;
}
}
}

@mixin setDaysNameOfTheWeekProperties {
.react-datepicker__day-names {
display: flex;
height: 40px;
justify-content: space-between;
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
@include font-scale();
color: var(--rp-ui-base-dark-e-500);
vertical-align: middle;

.react-datepicker__day-name {
width: 40px;
text-align: center;
}
}
}

@mixin removeAriaLive {
.react-datepicker__aria-live {
display: none;
}
}

@mixin setHoverState($backgroundColor, $textColor: inherit) {
border-radius: 50%;
border: 1px solid var(--rp-ui-base-topaz);
background-color: $backgroundColor;
line-height: 38px;
color: $textColor;
}

@mixin removeOutline {
&:focus-visible {
outline: none;
}
}

@mixin verticalAlign($height) {
height: $height;
line-height: $height;
}

@mixin drawBeforePseudoElement {
position: absolute;
content: '';
height: 32px;
background-color: var(--rp-ui-base-bg-200);
width: 16px;
top: 0;
left: -9px;
z-index: 1;
}

@mixin drawAfterPseudoElement($displayValue, $borderWidth) {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 40px;
height: 40px;
border: $borderWidth solid var(--rp-ui-base-topaz);
border-radius: 50%;
display: $displayValue;
z-index: $DAY_OF_THE_WEEK_HOVERED_Z_INDEX;
box-sizing: border-box;
}

:global {
@include removeAriaLive;
}

.calendar {
box-sizing: border-box;
background-color: var(--rp-ui-base-bg-000);
width: 344px;
padding: 30px 32px 32px;
border-radius: 8px;
box-shadow: 0px 8px 40px var(--rp-ui-base-dark-bg-light);
border: none;
margin-top: 4px;

:global {
@include setMonthContainerProperties;
}

.current-date,
.date {
width: 40px;
margin: 0;
box-sizing: border-box;
@include verticalAlign(40px);
@include removeOutline;
}

.date {
background-color: transparent;
border-radius: unset;
color: inherit;
text-align: center;
}

.current-date,
.current-date:hover {
position: relative;
z-index: $DAY_OF_THE_WEEK_Z_INDEX;
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
@include setHoverState(var(--rp-ui-base-topaz), var(--rp-ui-base-bg-000));
}

.date:hover:not(.current-date):not(.selected-range):not(.end-date) {
@include setHoverState(transparent);
}

.end-date {
position: relative;
border-radius: 50%;
background-color: var(--rp-ui-base-topaz);
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
color: var(--rp-ui-base-bg-000);
}

.end-date::after {
@include drawAfterPseudoElement(block, 10px);
}

.end-date::before {
@include drawBeforePseudoElement;
top: 4px;
}

.selected-range {
background-color: var(--rp-ui-base-bg-200);
border-radius: 8px;
@include verticalAlign(32px);
position: relative;
&:hover {
@include verticalAlign(40px);
border-radius: 50%;
background: var(--rp-ui-base-bg-200);
&::after {
display: block;
}
&:not(:first-child)::before {
top: 4px;
}
}
}

.selected-range::after {
@include drawAfterPseudoElement(none, 1px);
}

.selected-range:not(:first-child)::before {
@include drawBeforePseudoElement;
}

.disabled {
color: var(--rp-ui-base-e-400);
background-color: transparent;
@include removeOutline;
}
}
.popper {
z-index: $POPOVER_Z_INDEX;
}

.input {
width: 100%;
min-width: 138px;
}
121 changes: 121 additions & 0 deletions src/components/datePicker1/datePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import ReactDatePicker, { ReactDatePickerCustomHeaderProps } from 'react-datepicker';
import classNames from 'classnames/bind';
import { FC, ReactNode, useRef, ReactElement } from 'react';
import { FieldText } from '@components/fieldText';
import { CalendarIcon } from '@components/icons';
import { DatePickerHeader } from './header/datePickerHeader';
import styles from './datePicker.module.scss';

const cx = classNames.bind(styles);

const DEFAULT_LANGUAGE = 'en';
const DEFAULT_DATE_FORMAT = 'MM-dd-yyyy';

interface DatePickerProps {
onChange?: (date: Date | any) => void;

Check warning on line 15 in src/components/datePicker1/datePicker.tsx

GitHub Actions / build

Unexpected any. Specify a different type
onBlur?: () => void;
onFocus?: () => void;
headerNodes?: ReactNode;
disabled?: boolean;
shouldCloseOnSelect?: boolean;
fixedHeight?: boolean;
startDate?: Date | undefined;
endDate?: Date | undefined;
customClassName?: string;
popperClassName?: string;
calendarClassName?: string;
customTimeInput?: ReactElement;
language?: string;
yearsOptions?: number[];
placeholder?: string;
dateFormat?: string;
selects?: 'start' | 'end' | 'none';
value?: Date | null;
}

export const DatePicker1: FC<DatePickerProps> = ({
onChange = () => {},
disabled = false,
onBlur = () => {},
onFocus = () => {},
endDate = undefined,
startDate = undefined,
headerNodes = null,
customClassName = '',
customTimeInput = undefined,
shouldCloseOnSelect = true,
popperClassName = '',
calendarClassName = '',
fixedHeight = false,
language = DEFAULT_LANGUAGE,
yearsOptions = [],
placeholder = DEFAULT_DATE_FORMAT.toUpperCase(),
dateFormat = DEFAULT_DATE_FORMAT,
selects = 'start',
value = null,
}) => {
const inputRef = useRef(null);
const startDateString = startDate?.toDateString();
const endDateString = endDate?.toDateString();
const isValidEndDate = endDate && startDate && endDate > startDate;

const getDayClassName = (displayedDates: Date) => {
const displayedDateString = displayedDates.toDateString();
const isCurrentDate = displayedDateString === startDateString;
const isEndDate = isValidEndDate && displayedDateString === endDateString;

const isInsideSelectedRange =
startDate && endDate && displayedDates > startDate && displayedDates < endDate;

return cx('date', {
'current-date': isCurrentDate,
'selected-range': isInsideSelectedRange && !isEndDate,
'end-date': isEndDate && isValidEndDate,
disabled,
});
};

return (
<ReactDatePicker
customInput={
<FieldText
className={cx('input')}
defaultWidth={false}
endIcon={<CalendarIcon />}
ref={inputRef}
/>
}
placeholderText={placeholder}
selected={value}
startDate={startDate}
endDate={endDate}
minDate={selects === 'end' ? startDate : undefined}
disabled={disabled}
shouldCloseOnSelect={shouldCloseOnSelect}
fixedHeight={fixedHeight}
locale={language}
showPopperArrow={false}
dayClassName={getDayClassName}
calendarClassName={cx(calendarClassName, 'calendar')}
renderCustomHeader={(customHeaderProps: ReactDatePickerCustomHeaderProps) => (
<DatePickerHeader
{...customHeaderProps}
headerNodes={headerNodes}
customClassName={customClassName}
yearsOptions={yearsOptions}
locale={language}
/>
)}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
customTimeInput={customTimeInput}
showTimeInput={Boolean(customTimeInput)}
popperClassName={cx(popperClassName, 'popper')}
dateFormat={dateFormat}
selectsStart={selects === 'start'}
selectsEnd={selects === 'end'}
className={cx('datepicker')}
/>
);
};
72 changes: 72 additions & 0 deletions src/components/datePicker1/header/datePickerHeader.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
@import 'src/assets/styles/variables/typography';

@mixin arrowHoverState($arrow-color) {
&:hover:not(.disabled) {
cursor: pointer;
svg > path {
fill: $arrow-color;
}
}
}

@mixin disabledState {
opacity: 0.3;
pointer-events: none;
}

@mixin setArrowDefaultProps {
align-self: center;
width: 16px;
height: 16px;
svg {
width: 16px;
height: 16px;
}
}

.header {
display: flex;
align-items: center;
justify-content: space-between;
background-color: var(--rp-ui-base-bg-000);
padding-bottom: 18px;

.dropdowns-wrapper {
display: flex;
align-items: center;
column-gap: 8px;
}

.button-prev,
.button-next {
all: unset;

@include setArrowDefaultProps();
@include arrowHoverState(var(--rp-ui-base-e-400));
&.disabled {
@include disabledState;
}
}

.button-next {
transform: rotate(180deg);
}

.dropdown {
width: auto;

&.month-dropdown {
width: 117px;
}

.toggle-button > span {
color: var(--rp-ui-base-topaz);
font-family: var(--rp-ui-base-font-family);
font-weight: $fw-bold;
}

.toggle-button:hover > span {
color: var(-rp-ui-base-topaz-hover);
}
}
}
117 changes: 117 additions & 0 deletions src/components/datePicker1/header/datePickerHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { FC, ReactNode, useMemo } from 'react';
import classNames from 'classnames/bind';
import { Dropdown } from '@components/dropdown';
import { CalendarArrowIcon } from '@components/icons';
import { DropdownOptionType, DropdownValue } from '@components/dropdown/types';
import { getYearsFrom } from '../utils';
import styles from './datePickerHeader.module.scss';

const cx = classNames.bind(styles);

export interface DatePickerHeaderProps {
changeYear: (year: number) => void;
changeMonth: (month: number) => void;
decreaseMonth: () => void;
increaseMonth: () => void;
headerNodes: ReactNode;
date: Date;
prevMonthButtonDisabled: boolean;
nextMonthButtonDisabled: boolean;
customClassName: string;
yearsOptions: number[];
locale: string;
}

export const DatePickerHeader: FC<DatePickerHeaderProps> = ({
date = new Date(),
changeYear = () => {},
changeMonth = () => {},
decreaseMonth = () => {},
increaseMonth = () => {},
prevMonthButtonDisabled = false,
nextMonthButtonDisabled = false,
headerNodes = null,
customClassName = '',
yearsOptions = [],
locale,
}) => {
const year = date.getFullYear();
const month = date.getMonth();

const monthDropdownOptions = useMemo(() => {
const monthList = Array(12).keys();
const formatter = new Intl.DateTimeFormat(locale, {
month: 'long',
});
const getMonthName = (monthIndex: number) => formatter.format(new Date(year, monthIndex));
const months = Array.from(monthList, getMonthName);

return months.reduce((acc: DropdownOptionType[], monthValue, monthNumber) => {
return acc.concat({
value: monthNumber,
label: monthValue,
});
}, []);
}, []);

Check warning on line 55 in src/components/datePicker1/header/datePickerHeader.tsx

GitHub Actions / build

React Hook useMemo has missing dependencies: 'locale' and 'year'. Either include them or remove the dependency array

const yearDropdownOptions: DropdownOptionType[] = useMemo(() => {
const yearValues = yearsOptions.length > 0 ? yearsOptions : getYearsFrom(year);
return yearValues.reduce(
(acc: DropdownOptionType[], yearValue) =>
acc.concat({ value: yearValue, label: `${yearValue}` }),
[],
);
}, [yearsOptions]);

Check warning on line 64 in src/components/datePicker1/header/datePickerHeader.tsx

GitHub Actions / build

React Hook useMemo has a missing dependency: 'year'. Either include it or remove the dependency array

const onMonthChange = (changedMonth: DropdownValue) => {
const numberMonth: number = changedMonth as number;
changeMonth(numberMonth);
};

const onYearChange = (changedYear: DropdownValue) => {
const numberYear: number = changedYear as number;
changeYear(numberYear);
};

return (
<>
{headerNodes && <div className={cx(customClassName)}>{headerNodes}</div>}
<div className={cx('header')}>
<button
aria-label="Previous Months"
disabled={prevMonthButtonDisabled}
onClick={decreaseMonth}
className={cx('button-prev', { disabled: prevMonthButtonDisabled })}
>
<CalendarArrowIcon />
</button>
<div className={cx('dropdowns-wrapper')}>
<Dropdown
options={monthDropdownOptions}
value={month}
onChange={onMonthChange}
transparentBackground
className={cx('dropdown', 'month-dropdown')}
toggleButtonClassName={cx('toggle-button')}
/>
<Dropdown
options={yearDropdownOptions}
value={year}
onChange={onYearChange}
transparentBackground
className={cx('dropdown')}
toggleButtonClassName={cx('toggle-button')}
/>
</div>
<button
aria-label="Next Months"
disabled={nextMonthButtonDisabled}
onClick={increaseMonth}
className={cx('button-next', { disabled: nextMonthButtonDisabled })}
>
<CalendarArrowIcon />
</button>
</div>
</>
);
};
5 changes: 5 additions & 0 deletions src/components/datePicker1/header/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DatePickerHeader } from './datePickerHeader';

export { DatePickerHeader };

export default DatePickerHeader;
5 changes: 5 additions & 0 deletions src/components/datePicker1/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { DatePicker1 } from './datePicker';

export { DatePicker1 };

export default DatePicker1;
11 changes: 11 additions & 0 deletions src/components/datePicker1/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Locale } from 'date-fns';
import { registerLocale } from 'react-datepicker';

export const registerDatePickerLocale = (language: string, locale: Locale) => {
registerLocale(language, locale);
};

export const getYearsFrom = (start: number, amountYearsToGenerate = 20) => {
const yearsFromCurrent = start + amountYearsToGenerate;
return new Array(yearsFromCurrent - start).fill(undefined).map((_, i) => start - i);
};
3 changes: 3 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -16,5 +16,8 @@ export { Popover } from './popover';
export { Pagination } from './pagination';
export { Table } from './table';
export { DatePicker } from './datePicker';
export { DatePicker1 } from './datePicker1';
export { DatePicker2 } from './dataPicker2/dataPicker2';
export { Date2 } from './date2';
export { SystemAlert } from './systemAlert';
export * from './icons';
2 changes: 2 additions & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -49,6 +49,8 @@ export default defineConfig(() => ({
pagination: resolve('src', 'components', 'pagination'),
table: resolve('src', 'components', 'table'),
datePicker: resolve('src', 'components', 'datePicker'),
datePicker1: resolve('src', 'components', 'datePicker1'),
date2: resolve('src', 'components', 'date2'),
},
name: 'ui-kit',
formats: ['es'],

0 comments on commit e91dec3

Please sign in to comment.