Skip to content

Commit eeaea3c

Browse files
committed
Code cleanup
1 parent bf30769 commit eeaea3c

24 files changed

+905
-1273
lines changed

components/dash-core-components/package-lock.json

Lines changed: 29 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/dash-core-components/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"react-docgen": "^5.4.3",
6262
"react-dropzone": "^4.1.2",
6363
"react-fast-compare": "^3.2.2",
64+
"react-input-autosize": "^3.0.0",
6465
"react-markdown": "^4.3.1",
6566
"react-virtualized-select": "^3.1.3",
6667
"remark-math": "^3.0.1",
@@ -85,6 +86,7 @@
8586
"@types/ramda": "^0.31.0",
8687
"@types/react": "^16.14.8",
8788
"@types/react-dom": "^16.9.13",
89+
"@types/react-input-autosize": "^2.2.4",
8890
"@types/uniqid": "^5.3.4",
8991
"@typescript-eslint/eslint-plugin": "^5.59.7",
9092
"@typescript-eslint/parser": "^5.59.7",

components/dash-core-components/src/components/DatePickerRange.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export default function DatePickerRange({
2525
number_of_months_shown = 2,
2626
stay_open_on_select = false,
2727
reopen_calendar_on_clear = false,
28+
show_outside_days = false,
2829
clearable = false,
2930
disabled = false,
3031
updatemode = 'singledate',
@@ -41,6 +42,7 @@ export default function DatePickerRange({
4142
calendar_orientation={calendar_orientation}
4243
is_RTL={is_RTL}
4344
day_size={day_size}
45+
show_outside_days={show_outside_days}
4446
with_portal={with_portal}
4547
with_full_screen_portal={with_full_screen_portal}
4648
first_day_of_week={first_day_of_week}

components/dash-core-components/src/components/css/calendar.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
opacity: 0.6;
5858
cursor: not-allowed;
5959
background-color: inherit;
60+
pointer-events: none;
6061
}
6162

6263
.dash-datepicker-calendar td input {

components/dash-core-components/src/fragments/DatePickerRange.tsx

Lines changed: 111 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import {
44
CalendarIcon,
55
CaretDownIcon,
66
Cross1Icon,
7+
ArrowLeftIcon,
78
ArrowRightIcon,
89
} from '@radix-ui/react-icons';
10+
import AutosizeInput from 'react-input-autosize';
911
import Calendar from '../utils/calendar/Calendar';
1012
import {DatePickerRangeProps, CalendarDirection} from '../types';
1113
import {
@@ -15,9 +17,9 @@ import {
1517
isDateDisabled,
1618
isSameDay,
1719
} from '../utils/calendar/helpers';
18-
import {DateSet} from '../utils/calendar/DateSet';
1920
import '../components/css/datepickers.css';
2021
import uuid from 'uniqid';
22+
import moment from 'moment';
2123

2224
const DatePickerRange = ({
2325
id,
@@ -28,6 +30,7 @@ const DatePickerRange = ({
2830
max_date_allowed,
2931
initial_visible_month = start_date ?? min_date_allowed ?? max_date_allowed,
3032
disabled_days,
33+
minimum_nights,
3134
first_day_of_week,
3235
show_outside_days,
3336
clearable,
@@ -59,24 +62,45 @@ const DatePickerRange = ({
5962
const initialMonth = strAsDate(initial_visible_month);
6063
const minDate = strAsDate(min_date_allowed);
6164
const maxDate = strAsDate(max_date_allowed);
62-
const disabledDates = useMemo(
63-
() => new DateSet(disabled_days),
64-
[disabled_days]
65-
);
65+
const disabledDates = useMemo(() => {
66+
const baseDates =
67+
disabled_days
68+
?.map(d => strAsDate(d))
69+
.filter((d): d is Date => d !== undefined) || [];
70+
71+
// Add minimum_nights constraint: disable dates within the minimum nights range
72+
if (
73+
internalStartDate &&
74+
minimum_nights &&
75+
minimum_nights > 0 &&
76+
!internalEndDate
77+
) {
78+
const minimumNightsDates: Date[] = [];
79+
for (let i = 1; i < minimum_nights; i++) {
80+
minimumNightsDates.push(
81+
moment(internalStartDate).add(i, 'day').toDate()
82+
);
83+
minimumNightsDates.push(
84+
moment(internalStartDate).subtract(i, 'day').toDate()
85+
);
86+
}
87+
return [...baseDates, ...minimumNightsDates];
88+
}
89+
90+
return baseDates;
91+
}, [disabled_days, internalStartDate, internalEndDate, minimum_nights]);
6692

6793
const [isCalendarOpen, setIsCalendarOpen] = useState(false);
68-
const [startInputValue, setStartInputValue] = useState<string>(
69-
(internalStartDate && formatDate(internalStartDate, display_format)) ??
70-
''
94+
const [startInputValue, setStartInputValue] = useState(
95+
formatDate(internalStartDate, display_format)
7196
);
72-
const [endInputValue, setEndInputValue] = useState<string>(
73-
(internalEndDate && formatDate(internalEndDate, display_format)) ?? ''
97+
const [endInputValue, setEndInputValue] = useState(
98+
formatDate(internalEndDate, display_format)
7499
);
75100

76101
const containerRef = useRef<HTMLDivElement>(null);
77-
const startInputRef = useRef<HTMLInputElement>(null);
78-
const endInputRef = useRef<HTMLInputElement>(null);
79-
const calendarRef = useRef<HTMLDivElement>(null);
102+
const startInputRef = useRef<HTMLInputElement | null>(null);
103+
const endInputRef = useRef<HTMLInputElement | null>(null);
80104

81105
useEffect(() => {
82106
setInternalStartDate(strAsDate(start_date));
@@ -119,7 +143,7 @@ const DatePickerRange = ({
119143
endInputRef.current?.focus();
120144
}
121145
}
122-
}, [isCalendarOpen]);
146+
}, [isCalendarOpen, startInputValue]);
123147

124148
const sendStartInputAsDate = useCallback(() => {
125149
const parsed = strAsDate(startInputValue, display_format);
@@ -224,6 +248,42 @@ const DatePickerRange = ({
224248
classNames += ' ' + className;
225249
}
226250

251+
const initialCalendarDate =
252+
initialMonth || internalStartDate || internalEndDate;
253+
254+
const ArrowIcon =
255+
direction === CalendarDirection.LeftToRight
256+
? ArrowRightIcon
257+
: ArrowLeftIcon;
258+
259+
const handleSelectionChange = useCallback(
260+
(start?: Date, end?: Date) => {
261+
const isNewSelection =
262+
isSameDay(start, end) &&
263+
((!internalStartDate && !internalEndDate) ||
264+
(internalStartDate && internalEndDate));
265+
266+
if (isNewSelection) {
267+
setInternalStartDate(start);
268+
setInternalEndDate(undefined);
269+
} else {
270+
// Normalize dates: ensure start <= end
271+
if (start && end && start > end) {
272+
setInternalStartDate(end);
273+
setInternalEndDate(start);
274+
} else {
275+
setInternalStartDate(start);
276+
setInternalEndDate(end);
277+
}
278+
279+
if (end && !stay_open_on_select) {
280+
setIsCalendarOpen(false);
281+
}
282+
}
283+
},
284+
[internalStartDate, internalEndDate, stay_open_on_select]
285+
);
286+
227287
return (
228288
<div className="dash-datepicker" ref={containerRef}>
229289
<Popover.Root
@@ -241,30 +301,44 @@ const DatePickerRange = ({
241301
aria-disabled={disabled}
242302
>
243303
<CalendarIcon className="dash-datepicker-trigger-icon" />
244-
<input
245-
ref={startInputRef}
304+
<AutosizeInput
305+
inputRef={node => {
306+
startInputRef.current = node;
307+
}}
246308
type="text"
247309
id={start_date_id || accessibleId}
248-
className="dash-datepicker-input dash-datepicker-start-date"
310+
inputClassName="dash-datepicker-input dash-datepicker-start-date"
249311
value={startInputValue}
250312
onChange={e => setStartInputValue(e.target.value)}
251313
onKeyDown={handleStartInputKeyDown}
252314
onBlur={sendStartInputAsDate}
315+
onClick={() => {
316+
if (!isCalendarOpen && !disabled) {
317+
setIsCalendarOpen(true);
318+
}
319+
}}
253320
placeholder={start_date_placeholder_text}
254321
disabled={disabled}
255322
dir={direction}
256323
aria-label={start_date_placeholder_text}
257324
/>
258-
<ArrowRightIcon />
259-
<input
260-
ref={endInputRef}
325+
<ArrowIcon />
326+
<AutosizeInput
327+
inputRef={node => {
328+
endInputRef.current = node;
329+
}}
261330
type="text"
262331
id={end_date_id || accessibleId + '-end-date'}
263-
className="dash-datepicker-input dash-datepicker-end-date"
332+
inputClassName="dash-datepicker-input dash-datepicker-end-date"
264333
value={endInputValue}
265334
onChange={e => setEndInputValue(e.target.value)}
266335
onKeyDown={handleEndInputKeyDown}
267336
onBlur={sendEndInputAsDate}
337+
onClick={() => {
338+
if (!isCalendarOpen && !disabled) {
339+
setIsCalendarOpen(true);
340+
}
341+
}}
268342
placeholder={end_date_placeholder_text}
269343
disabled={disabled}
270344
dir={direction}
@@ -290,47 +364,22 @@ const DatePickerRange = ({
290364
sideOffset={5}
291365
onOpenAutoFocus={e => e.preventDefault()}
292366
>
293-
<div ref={calendarRef}>
294-
<Calendar
295-
initialVisibleDate={
296-
initialMonth ||
297-
internalStartDate ||
298-
internalEndDate
299-
}
300-
selectionStart={internalStartDate}
301-
selectionEnd={internalEndDate}
302-
minDateAllowed={minDate}
303-
maxDateAllowed={maxDate}
304-
disabledDates={disabledDates}
305-
firstDayOfWeek={first_day_of_week}
306-
showOutsideDays={show_outside_days}
307-
monthFormat={month_format}
308-
numberOfMonthsShown={number_of_months_shown}
309-
calendarOrientation={calendar_orientation}
310-
daySize={day_size}
311-
direction={direction}
312-
onSelectionChange={(start, end) => {
313-
const isNewSelection =
314-
isSameDay(start, end) &&
315-
((!internalStartDate &&
316-
!internalEndDate) ||
317-
(internalStartDate &&
318-
internalEndDate));
319-
320-
if (isNewSelection) {
321-
setInternalStartDate(start);
322-
setInternalEndDate(undefined);
323-
} else {
324-
setInternalStartDate(start);
325-
setInternalEndDate(end);
326-
327-
if (end && !stay_open_on_select) {
328-
setIsCalendarOpen(false);
329-
}
330-
}
331-
}}
332-
/>
333-
</div>
367+
<Calendar
368+
initialVisibleDate={initialCalendarDate}
369+
selectionStart={internalStartDate}
370+
selectionEnd={internalEndDate}
371+
minDateAllowed={minDate}
372+
maxDateAllowed={maxDate}
373+
disabledDates={disabledDates}
374+
firstDayOfWeek={first_day_of_week}
375+
showOutsideDays={show_outside_days}
376+
monthFormat={month_format}
377+
numberOfMonthsShown={number_of_months_shown}
378+
calendarOrientation={calendar_orientation}
379+
daySize={day_size}
380+
direction={direction}
381+
onSelectionChange={handleSelectionChange}
382+
/>
334383
</Popover.Content>
335384
</Popover.Portal>
336385
</Popover.Root>

0 commit comments

Comments
 (0)