Skip to content

Commit

Permalink
first
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaScant committed Jan 31, 2025
1 parent ed2ac63 commit 6493519
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 81 deletions.
144 changes: 91 additions & 53 deletions polaris-react/src/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ export interface DatePickerProps {
/** ID for the element */
id?: string;
/** The selected date or range of dates */
selected?: Date | Range;
selected?: Date | Range | Date[];
/** The month to show, from 0 to 11. 0 is January, 1 is February ... 11 is December */
month: number;
/** The year to show */
year: number;
/** Allow a range of dates to be selected */
allowRange?: boolean;
/** Allow multiple dates to be selected */
allowMultiple?: boolean;
/** Disable selecting dates before this. */
disableDatesBefore?: Date;
/** Disable selecting dates after this. */
Expand All @@ -48,7 +50,7 @@ export interface DatePickerProps {
/** Visually hidden prefix text for selected days on single selection date pickers */
dayAccessibilityLabelPrefix?: string;
/** Callback when date is selected. */
onChange?(date: Range): void;
onChange?(date: Range | Date[]): void;
/** Callback when month is changed. */
onMonthChange?(month: number, year: number): void;
}
Expand All @@ -59,6 +61,7 @@ export function DatePicker({
month,
year,
allowRange,
allowMultiple,
multiMonth,
disableDatesBefore,
disableDatesAfter,
Expand Down Expand Up @@ -92,14 +95,32 @@ export function DatePicker({
);

const handleDateSelection = useCallback(
(range: Range) => {
const {end} = range;
(range: Range | Date) => {
if (allowMultiple) {
const newSelected = Array.isArray(selected) ? [...selected] : [];
const dateToAdd = range instanceof Date ? range : range.start;

const existingIndex = newSelected.findIndex((date) =>
date.getTime() === dateToAdd.getTime()
);

if (existingIndex >= 0) {
newSelected.splice(existingIndex, 1);
} else {
newSelected.push(dateToAdd);
}

setHoverDate(end);
setFocusDate(new Date(end));
onChange(range);
setHoverDate(dateToAdd);
setFocusDate(dateToAdd);
onChange(newSelected);
} else {
const rangeValue = range instanceof Date ? {start: range, end: range} : range;
setHoverDate(rangeValue.end);
setFocusDate(new Date(rangeValue.end));
onChange(rangeValue);
}
},
[onChange],
[allowMultiple, onChange, selected],
);

const handleMonthChangeClick = useCallback(
Expand All @@ -122,7 +143,7 @@ export function DatePicker({
const {key} = event;

const range = deriveRange(selected);
const focusedDate = focusDate || (range && range.start);
const focusedDate = focusDate || (Array.isArray(range) ? range[0]?.start : range?.start);

if (focusedDate == null) {
return;
Expand Down Expand Up @@ -196,6 +217,25 @@ export function DatePicker({
],
);

const monthIsSelected = useMemo(() => {
if (allowMultiple && Array.isArray(selected)) {
return selected.map((date) => ({start: date, end: date}));
}
return deriveRange(selected);
}, [selected, allowMultiple]);

const firstDatePickerAccessibilityLabelPrefix = allowRange
? i18n.translate(`Polaris.DatePicker.start`)
: dayAccessibilityLabelPrefix;
const secondDatePickerAccessibilityLabelPrefix = i18n.translate(
`Polaris.DatePicker.end`,
);

const accessibilityLabelPrefixes: [string | undefined, string] = [
firstDatePickerAccessibilityLabelPrefix,
secondDatePickerAccessibilityLabelPrefix,
];

const showNextYear = getNextDisplayYear(month, year);
const showNextMonth = getNextDisplayMonth(month);

Expand All @@ -215,20 +255,6 @@ export function DatePicker({
: i18n.translate(`Polaris.DatePicker.months.${monthName(showNextMonth)}`);
const nextYear = multiMonth ? showNextToNextYear : showNextYear;

const monthIsSelected = useMemo(() => deriveRange(selected), [selected]);

const firstDatePickerAccessibilityLabelPrefix = allowRange
? i18n.translate(`Polaris.DatePicker.start`)
: dayAccessibilityLabelPrefix;
const secondDatePickerAccessibilityLabelPrefix = i18n.translate(
`Polaris.DatePicker.end`,
);

const accessibilityLabelPrefixes: [string | undefined, string] = [
firstDatePickerAccessibilityLabelPrefix,
secondDatePickerAccessibilityLabelPrefix,
];

const secondDatePicker = multiMonth ? (
<Month
onFocus={handleFocus}
Expand All @@ -243,43 +269,53 @@ export function DatePicker({
disableDatesAfter={disableDatesAfter}
disableSpecificDates={disableSpecificDates}
allowRange={allowRange}
allowMultiple={allowMultiple}
weekStartsOn={weekStartsOn}
accessibilityLabelPrefixes={accessibilityLabelPrefixes}
/>
) : null;

const datePickerClassName = classNames(styles.DatePicker);

return (
<div
id={id}
className={datePickerClassName}
onKeyDown={handleKeyDown}
className={classNames(
styles.DatePicker,
multiMonth && styles.DatePickerMultiMonth,
)}
onKeyUp={handleKeyUp}
>
<div className={styles.Header}>
<Button
variant="tertiary"
icon={ArrowLeftIcon}
accessibilityLabel={i18n.translate(
'Polaris.DatePicker.previousMonth',
{
previousMonthName,
showPreviousYear,
},
)}
onClick={() =>
handleMonthChangeClick(showPreviousMonth, showPreviousYear)
}
disabled={
disableDatesBefore != null &&
isDateBefore(
new Date(showPreviousYear, showPreviousMonth, 1),
disableDatesBefore,
)
}
icon={ArrowLeftIcon}
accessibilityLabel={i18n.translate('Polaris.DatePicker.previousMonth', {
previousMonth: previousMonthName,
showPreviousYear,
})}
/>
<Button
variant="tertiary"
onClick={() => handleMonthChangeClick(showNextMonth, nextYear)}
disabled={
disableDatesAfter != null &&
isDateAfter(
new Date(nextYear, showNextMonth, 1),
disableDatesAfter,
)
}
icon={ArrowRightIcon}
accessibilityLabel={i18n.translate('Polaris.DatePicker.nextMonth', {
nextMonth,
nextYear,
})}
onClick={() => handleMonthChangeClick(showNextMonth, showNextYear)}
/>
</div>
<div className={styles.MonthLayout}>
Expand All @@ -288,14 +324,15 @@ export function DatePicker({
focusedDate={focusDate}
month={month}
year={year}
selected={deriveRange(selected)}
selected={monthIsSelected}
hoverDate={hoverDate}
onChange={handleDateSelection}
onHover={handleHover}
disableDatesBefore={disableDatesBefore}
disableDatesAfter={disableDatesAfter}
disableSpecificDates={disableSpecificDates}
allowRange={allowRange}
allowMultiple={allowMultiple}
weekStartsOn={weekStartsOn}
accessibilityLabelPrefixes={accessibilityLabelPrefixes}
/>
Expand All @@ -307,20 +344,21 @@ export function DatePicker({

function noop() {}

function handleKeyDown(event: React.KeyboardEvent<HTMLElement>) {
const {key} = event;

if (
key === 'ArrowUp' ||
key === 'ArrowDown' ||
key === 'ArrowLeft' ||
key === 'ArrowRight'
) {
event.preventDefault();
event.stopPropagation();
function deriveRange(selected?: Date | Range | Date[]) {
if (selected == null) {
return undefined;
}

if (Array.isArray(selected)) {
return selected.map((date) => ({start: date, end: date}));
}

if (selected instanceof Date) {
return {
start: selected,
end: selected,
};
}
}

function deriveRange(selected?: Date | Range) {
return selected instanceof Date ? {start: selected, end: selected} : selected;
return selected;
}
70 changes: 45 additions & 25 deletions polaris-react/src/components/DatePicker/components/Month/Month.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ import {monthName, weekdayName} from '../../utilities';

export interface MonthProps {
focusedDate?: Date;
selected?: Range;
selected?: Range | Range[];
hoverDate?: Date;
month: number;
year: number;
disableDatesBefore?: Date;
disableDatesAfter?: Date;
disableSpecificDates?: Date[];
allowRange?: boolean;
allowMultiple?: boolean;
weekStartsOn: number;
accessibilityLabelPrefixes: [string | undefined, string];
onChange?(date: Range): void;
onChange?(date: Range | Date): void;
onHover?(hoverEnd: Date): void;
onFocus?(date: Date): void;
}
Expand All @@ -43,7 +44,8 @@ export function Month({
disableDatesBefore,
disableDatesAfter,
disableSpecificDates,
allowRange,
allowRange = false,
allowMultiple = false,
onChange = noop,
onHover = noop,
onFocus = noop,
Expand Down Expand Up @@ -74,9 +76,13 @@ export function Month({

const handleDateClick = useCallback(
(selectedDate: Date) => {
onChange(getNewRange(allowRange ? selected : undefined, selectedDate));
if (allowMultiple) {
onChange(selectedDate);
} else {
onChange(getNewRange(allowRange ? selected as Range : undefined, selectedDate));
}
},
[allowRange, onChange, selected],
[allowRange, allowMultiple, onChange, selected],
);

const lastDayOfMonth = useMemo(
Expand All @@ -90,26 +96,35 @@ export function Month({
<Day key={dayIndex} onHover={onHover} lastDayOfMonth={lastDayOfMonth} />
);
}

const disabled =
(disableDatesBefore && isDateBefore(day, disableDatesBefore)) ||
(disableDatesAfter && isDateAfter(day, disableDatesAfter)) ||
(disableSpecificDates && isDateDisabled(day, disableSpecificDates));

const isFirstSelectedDay =
allowRange && selected && isDateStart(day, selected);
const isLastSelectedDay =
allowRange &&
selected &&
((!isSameDay(selected.start, selected.end) && isDateEnd(day, selected)) ||
(hoverDate &&
isSameDay(selected.start, selected.end) &&
isDateAfter(hoverDate, selected.start) &&
isSameDay(day, hoverDate) &&
!isFirstSelectedDay));
const rangeIsDifferent = !(
selected && isSameDay(selected.start, selected.end)
);
const isHoveringRight = hoverDate && isDateBefore(day, hoverDate);
let isFirstSelectedDay = false;
let isLastSelectedDay = false;
let isSelected = false;
let isInRange = false;

if (allowMultiple && Array.isArray(selected)) {
isSelected = selected.some((range) => dateIsSelected(day, range));
} else if (selected) {
const singleSelected = selected as Range;
isFirstSelectedDay = Boolean(allowRange && isDateStart(day, singleSelected));
isLastSelectedDay = Boolean(
allowRange &&
((!isSameDay(singleSelected.start, singleSelected.end) && isDateEnd(day, singleSelected)) ||
(hoverDate &&
isSameDay(singleSelected.start, singleSelected.end) &&
isDateAfter(hoverDate, singleSelected.start) &&
isSameDay(day, hoverDate) &&
!isFirstSelectedDay))
);
isSelected = dateIsSelected(day, singleSelected);
isInRange = dateIsInRange(day, singleSelected);
}

const [firstAccessibilityLabelPrefix, lastAccessibilityLabelPrefix] =
accessibilityLabelPrefixes;
let accessibilityLabelPrefix;
Expand All @@ -133,18 +148,23 @@ export function Month({
onFocus={onFocus}
onClick={handleDateClick}
onHover={onHover}
selected={selected != null && dateIsSelected(day, selected)}
inRange={selected != null && dateIsInRange(day, selected)}
selected={isSelected}
inRange={isInRange}
disabled={disabled}
inHoveringRange={
selected != null &&
hoverDate != null &&
isInHoveringRange(day, selected, hoverDate)
!allowMultiple &&
isInHoveringRange(day, selected as Range, hoverDate)
}
isLastSelectedDay={isLastSelectedDay}
isFirstSelectedDay={isFirstSelectedDay}
isHoveringRight={isHoveringRight}
rangeIsDifferent={rangeIsDifferent}
isHoveringRight={hoverDate && isDateBefore(day, hoverDate)}
rangeIsDifferent={
selected && !allowMultiple
? !isSameDay((selected as Range).start, (selected as Range).end)
: false
}
/>
);
}
Expand Down
Loading

0 comments on commit 6493519

Please sign in to comment.