-
-
Notifications
You must be signed in to change notification settings - Fork 733
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf: improve performance when selecting long ranges (#2537)
* Add RangeLong example * Improve performance for long date range Improve for the use case when excludeDisabled && disabled is false. * Add more long range examples * Improve useRange performance for long date range * Fix RangeLongExcludeDisabled example * Add rangeMatchModifiers utils, use it in useRange hook * Minor refactor * Update performance test to use RangeLongExcludeDisabled * Fix import * Attend to PR change requests * Add rangeContainsDayOfWeek.test.ts * Rename rangeMatchModifiers to rangeContainsModifiers * Add rangeContainsModifiers.test.ts Fix bugs detected by the tests * Improve rangeContainsModifiers.test.ts * Refactor rangeContainsModifiers, add areRangesOverlapping utils * Improve docs * Fix dateMatchModifiers docs * Add areRangesOverlapping.test.ts * Improve long range examples --------- Co-authored-by: Giampaolo Bellavite <io@gpbl.dev>
- Loading branch information
Showing
13 changed files
with
577 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React, { useState } from "react"; | ||
|
||
import { type DateRange, DayPicker } from "react-day-picker"; | ||
|
||
export function RangeLongExcludeDisabled() { | ||
const [range, setRange] = useState<DateRange | undefined>({ | ||
from: new Date(100, 0, 1), | ||
to: new Date(2024, 9, 10) | ||
}); | ||
|
||
return ( | ||
<DayPicker | ||
id="test" | ||
mode="range" | ||
defaultMonth={new Date(2024, 9)} | ||
selected={range} | ||
onSelect={setRange} | ||
excludeDisabled | ||
disabled={new Date(2000, 0, 1)} | ||
/> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { defaultDateLib } from "../classes/DateLib"; | ||
|
||
import { areRangesOverlapping } from "./areRangesOverlapping"; | ||
|
||
const sunday = new Date(2024, 8, 1); | ||
const monday = new Date(2024, 8, 2); | ||
const tuesday = new Date(2024, 8, 3); | ||
const thursday = new Date(2024, 8, 5); | ||
const saturday = new Date(2024, 8, 7); | ||
const nextWeekSunday = new Date(2024, 8, 8); | ||
|
||
const leftRange = { from: monday, to: saturday }; | ||
|
||
test('should return true when matching the "from" date', () => { | ||
const rightRange = { from: sunday, to: monday }; | ||
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
test('should return true when matching the "to" date', () => { | ||
const rightRange = { from: saturday, to: nextWeekSunday }; | ||
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
test("should return true when left date range contains right date range", () => { | ||
const rightRange = { from: tuesday, to: thursday }; | ||
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
test("should return true when right date range contains left date range", () => { | ||
const rightRange = { from: sunday, to: nextWeekSunday }; | ||
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
test("should return true when a date range is inverted", () => { | ||
const rightRange = { to: sunday, from: nextWeekSunday }; | ||
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
test('should return false on the edge of the "from" date', () => { | ||
const rightRange = { from: new Date(2000, 1, 1), to: sunday }; | ||
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib); | ||
expect(result).toBe(false); | ||
}); | ||
|
||
test('should return false on the edge of the "to" date', () => { | ||
const rightRange = { from: nextWeekSunday, to: new Date(2077, 1, 1) }; | ||
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib); | ||
expect(result).toBe(false); | ||
}); | ||
|
||
test("should return false when a date range is inverted", () => { | ||
const rightRange = { to: nextWeekSunday, from: new Date(2077, 1, 1) }; | ||
const result = areRangesOverlapping(leftRange, rightRange, defaultDateLib); | ||
expect(result).toBe(false); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { defaultDateLib } from "../classes/index.js"; | ||
|
||
import { rangeIncludesDate } from "./rangeIncludesDate.js"; | ||
|
||
/** | ||
* Determines whether a given range overlaps with another range. | ||
* | ||
* @group Utilities | ||
*/ | ||
export function areRangesOverlapping( | ||
rangeLeft: { from: Date; to: Date }, | ||
rangeRight: { from: Date; to: Date }, | ||
dateLib = defaultDateLib | ||
): boolean { | ||
return ( | ||
rangeIncludesDate(rangeLeft, rangeRight.from, false, dateLib) || | ||
rangeIncludesDate(rangeLeft, rangeRight.to, false, dateLib) || | ||
rangeIncludesDate(rangeRight, rangeLeft.from, false, dateLib) || | ||
rangeIncludesDate(rangeRight, rangeLeft.to, false, dateLib) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
export * from "./addToRange.js"; | ||
export * from "./areRangesOverlapping.js"; | ||
export * from "./dateMatchModifiers.js"; | ||
export * from "./rangeContainsDayOfWeek.js"; | ||
export * from "./rangeIncludesDate.js"; | ||
export * from "./rangeContainsModifiers.js"; | ||
export * from "./typeguards.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { defaultDateLib } from "../classes/DateLib"; | ||
import { DayOfWeek } from "../types"; | ||
|
||
import { rangeContainsDayOfWeek } from "./rangeContainsDayOfWeek"; | ||
|
||
const sunday = new Date(2024, 8, 1); // day of the week 0 | ||
const monday = new Date(2024, 8, 2); // day of the week 1 | ||
const friday = new Date(2024, 8, 6); // day of the week 5 | ||
const saturday = new Date(2024, 8, 7); // day of the week 6 | ||
const nextWeekSunday = new Date(2024, 8, 8); // day of the week 0 | ||
|
||
describe("should return false", () => { | ||
const testCases: Array<[{ from: Date; to: Date }, DayOfWeek]> = [ | ||
[{ from: monday, to: saturday }, { dayOfWeek: 0 }], | ||
[{ from: monday, to: friday }, { dayOfWeek: [0, 6] }], | ||
[{ from: sunday, to: friday }, { dayOfWeek: 6 }] | ||
]; | ||
|
||
for (const [range, dayOfWeek] of testCases) { | ||
it(`range from ${range.from} to ${range.to} should not contain ${JSON.stringify(dayOfWeek)}`, () => { | ||
expect(rangeContainsDayOfWeek(range, dayOfWeek, defaultDateLib)).toBe( | ||
false | ||
); | ||
}); | ||
} | ||
}); | ||
|
||
describe("should return true", () => { | ||
const testCases: Array<[{ from: Date; to: Date }, DayOfWeek]> = [ | ||
[{ from: sunday, to: saturday }, { dayOfWeek: 0 }], | ||
[{ from: monday, to: friday }, { dayOfWeek: 1 }], | ||
[{ from: monday, to: friday }, { dayOfWeek: 2 }], | ||
[{ from: monday, to: friday }, { dayOfWeek: 3 }], | ||
[{ from: monday, to: friday }, { dayOfWeek: 4 }], | ||
[{ from: monday, to: friday }, { dayOfWeek: 5 }], | ||
[{ from: monday, to: saturday }, { dayOfWeek: 6 }], | ||
[{ from: monday, to: saturday }, { dayOfWeek: [0, 6] }], | ||
[{ from: monday, to: nextWeekSunday }, { dayOfWeek: 0 }], | ||
[{ from: monday, to: nextWeekSunday }, { dayOfWeek: 6 }] | ||
]; | ||
|
||
for (const [range, dayOfWeek] of testCases) { | ||
it(`range from ${range.from} to ${range.to} should contain ${JSON.stringify(dayOfWeek)}`, () => { | ||
expect(rangeContainsDayOfWeek(range, dayOfWeek, defaultDateLib)).toBe( | ||
true | ||
); | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { defaultDateLib, type DateLib } from "../classes/DateLib.js"; | ||
import type { DayOfWeek } from "../types/index.js"; | ||
|
||
/** | ||
* Returns whether a date range matches against a given {@link DayOfWeek}. | ||
* | ||
* ```tsx | ||
* const range: DateRange = { | ||
* from: new Date(2024, 8, 1), // day of the week 0 | ||
* to: new Date(2024, 8, 6) // day of the week 5 | ||
* }; | ||
* rangeContainsDayOfWeek(date, { dayOfWeek: 0 }); // true | ||
* ``` | ||
* | ||
* @group Utilities | ||
*/ | ||
export function rangeContainsDayOfWeek( | ||
range: { from: Date; to: Date }, | ||
matcher: DayOfWeek, | ||
dateLib: DateLib = defaultDateLib | ||
) { | ||
const dayOfWeekArr = !Array.isArray(matcher.dayOfWeek) | ||
? [matcher.dayOfWeek] | ||
: matcher.dayOfWeek; | ||
let date = range.from; | ||
const totalDays = dateLib.differenceInCalendarDays(range.to, range.from); | ||
|
||
// iterate at maximum one week or the total days if the range is shorter than one week | ||
const totalDaysLimit = Math.min(totalDays, 6); | ||
for (let i = 0; i <= totalDaysLimit; i++) { | ||
if (dayOfWeekArr.includes(date.getDay())) { | ||
return true; | ||
} | ||
date = dateLib.addDays(date, 1); | ||
} | ||
return false; | ||
} |
Oops, something went wrong.