@trackunit/react-date-and-time-components 1.4.11 → 1.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +105 -66
- package/index.esm.js +109 -70
- package/package.json +13 -14
- package/src/DayPicker/DayPicker.d.ts +15 -10
- package/src/DayPicker/DayRangePicker.d.ts +4 -8
- package/src/DayPicker/DayRangePicker.stories.d.ts +1 -0
- package/src/DayRangeSelect/DayRangeSelect.d.ts +2 -1
- package/src/DayRangeSelect/hooks/useCalculateDateRange/useCalculateDateRange.d.ts +2 -1
- package/src/DayRangeSelect/types.d.ts +1 -4
- package/src/DayRangeSelect/utils/formatCustomDateRangeLabel/formatCustomDateRangeLabel.d.ts +1 -1
- package/src/DayRangeSelect/utils/isTemporalPeriod/isTemporalPeriod.d.ts +2 -1
- package/src/dateTime/DateTime.stories.d.ts +1 -1
- package/src/DayPicker/useLocale.d.ts +0 -8
package/index.cjs.js
CHANGED
|
@@ -8,9 +8,8 @@ var uiDesignTokens = require('@trackunit/ui-design-tokens');
|
|
|
8
8
|
var react = require('react');
|
|
9
9
|
var reactComponents = require('@trackunit/react-components');
|
|
10
10
|
var sharedUtils = require('@trackunit/shared-utils');
|
|
11
|
-
var
|
|
11
|
+
var Calendar = require('react-calendar');
|
|
12
12
|
var tailwindMerge = require('tailwind-merge');
|
|
13
|
-
var locale = require('date-fns/locale');
|
|
14
13
|
var reactFormComponents = require('@trackunit/react-form-components');
|
|
15
14
|
var cssClassVarianceUtilities = require('@trackunit/css-class-variance-utilities');
|
|
16
15
|
var stringTs = require('string-ts');
|
|
@@ -138,75 +137,33 @@ const DateTimeHumanized = ({ value }) => {
|
|
|
138
137
|
return (jsxRuntime.jsx(reactComponents.Tooltip, { label: jsxRuntime.jsx(DateTime, { format: tooltipFormat, value: value }), children: jsxRuntime.jsx(DateTime, { className: "underline decoration-neutral-300 decoration-dotted", fromNow: true, value: value }) }));
|
|
139
138
|
};
|
|
140
139
|
|
|
141
|
-
const localeLookup = {
|
|
142
|
-
en: locale.enGB,
|
|
143
|
-
"en-US": locale.enUS,
|
|
144
|
-
"en-GB": locale.enGB,
|
|
145
|
-
da: locale.da,
|
|
146
|
-
de: locale.de,
|
|
147
|
-
cs: locale.cs,
|
|
148
|
-
nl: locale.nl,
|
|
149
|
-
fr: locale.fr,
|
|
150
|
-
fi: locale.fi,
|
|
151
|
-
hu: locale.hu,
|
|
152
|
-
it: locale.it,
|
|
153
|
-
nb: locale.nb,
|
|
154
|
-
pl: locale.pl,
|
|
155
|
-
pt: locale.pt,
|
|
156
|
-
ru: locale.ru,
|
|
157
|
-
ro: locale.ro,
|
|
158
|
-
es: locale.es,
|
|
159
|
-
sv: locale.sv,
|
|
160
|
-
ja: locale.ja,
|
|
161
|
-
th: locale.th,
|
|
162
|
-
};
|
|
163
|
-
/**
|
|
164
|
-
* A helper function to get the matching Locale object for a given language.
|
|
165
|
-
*
|
|
166
|
-
* @param {string} language The language to match
|
|
167
|
-
* @returns {Locale} The corresponding Locale object
|
|
168
|
-
*/
|
|
169
|
-
const useLocale = (language) => {
|
|
170
|
-
return localeLookup[language] || localeLookup.en || locale.enGB;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* The DayPicker component is used when the user needs to select a date.
|
|
175
|
-
|
|
176
|
-
* @param {DayPickerProps} props - The props for the DayPicker component
|
|
177
|
-
* @returns {ReactElement} DayPicker component
|
|
178
|
-
*/
|
|
179
|
-
const DayPicker = ({ onDaySelect, disabledDays, selectedDays, language, className, dataTestId, max, }) => {
|
|
180
|
-
const locale = useLocale(language);
|
|
181
|
-
return (jsxRuntime.jsx(reactDayPicker.DayPicker, { className: tailwindMerge.twMerge("custom-day-picker", className), disabled: disabledDays, footer: jsxRuntime.jsx("div", { "data-testid": dataTestId }), locale: locale, max: max, onDayClick: onDaySelect, selected: selectedDays }));
|
|
182
|
-
};
|
|
183
|
-
|
|
184
140
|
/**
|
|
185
141
|
* The DayRangePicker component should be used when the user needs to select a range of dates.
|
|
186
142
|
*
|
|
187
143
|
* @param {DayRangePickerProps} props - The props for the DayRangePicker component
|
|
188
144
|
* @returns {ReactElement} DayRangePicker component
|
|
189
145
|
*/
|
|
190
|
-
const DayRangePicker = ({ onRangeSelect, selectedDays, disabledDays, dataTestId, language, className,
|
|
191
|
-
const [newRange, setNewRange] = react.useState(selectedDays ?? {
|
|
146
|
+
const DayRangePicker = ({ onRangeSelect, selectedDays, disabledDays, dataTestId, language, className, timezone, cancelButtonLabel, onClose, }) => {
|
|
147
|
+
const [newRange, setNewRange] = react.useState(selectedDays ?? { start: undefined, end: undefined });
|
|
192
148
|
const [t] = useTranslation();
|
|
193
|
-
const
|
|
149
|
+
const { subtract } = reactDateAndTimeHooks.useDateAndTime();
|
|
150
|
+
const calculateDateRange = useCalculateDateRange();
|
|
194
151
|
react.useEffect(() => {
|
|
195
152
|
if (selectedDays) {
|
|
196
153
|
setNewRange(selectedDays);
|
|
197
154
|
}
|
|
198
155
|
}, [selectedDays]);
|
|
199
156
|
const handleOnRangeSelect = react.useCallback((range) => {
|
|
200
|
-
if (range && range.
|
|
201
|
-
if (range.
|
|
202
|
-
setNewRange({
|
|
157
|
+
if (range && range.start) {
|
|
158
|
+
if (range.end === undefined) {
|
|
159
|
+
setNewRange({ start: range.start, end: range.start });
|
|
203
160
|
}
|
|
204
161
|
else {
|
|
205
|
-
setNewRange({
|
|
162
|
+
setNewRange({ start: range.start, end: range.end });
|
|
206
163
|
}
|
|
207
164
|
}
|
|
208
165
|
else {
|
|
209
|
-
setNewRange({
|
|
166
|
+
setNewRange({ start: undefined, end: undefined });
|
|
210
167
|
}
|
|
211
168
|
}, []);
|
|
212
169
|
const handleCancel = react.useCallback(() => {
|
|
@@ -216,13 +173,47 @@ const DayRangePicker = ({ onRangeSelect, selectedDays, disabledDays, dataTestId,
|
|
|
216
173
|
onClose && onClose();
|
|
217
174
|
}, [selectedDays, onClose]);
|
|
218
175
|
const clearSelectedDays = () => {
|
|
219
|
-
setNewRange({
|
|
176
|
+
setNewRange({ start: undefined, end: undefined });
|
|
220
177
|
};
|
|
221
178
|
const handleApply = react.useCallback(() => {
|
|
222
179
|
onRangeSelect && onRangeSelect(newRange);
|
|
223
180
|
onClose && onClose();
|
|
224
181
|
}, [onRangeSelect, newRange, onClose]);
|
|
225
|
-
return (jsxRuntime.jsxs("div", { className: "flex w-min flex-col gap-4 p-2",
|
|
182
|
+
return (jsxRuntime.jsxs("div", { className: "flex w-min flex-col gap-4 p-2", "data-testid": dataTestId, children: [jsxRuntime.jsx(Calendar, { activeStartDate: newRange.start ?? undefined, allowPartialRange: true, className: tailwindMerge.twMerge("custom-day-picker", "range-picker", className, "p-0"), locale: language, onChange: value => {
|
|
183
|
+
if (Array.isArray(value) && value[0] && value[1]) {
|
|
184
|
+
handleOnRangeSelect({ start: value[0], end: value[1] });
|
|
185
|
+
}
|
|
186
|
+
}, selectRange: true, tileDisabled: ({ date, view }) => {
|
|
187
|
+
if (view === "month") {
|
|
188
|
+
if (typeof disabledDays === "function") {
|
|
189
|
+
return disabledDays(date);
|
|
190
|
+
}
|
|
191
|
+
if (Array.isArray(disabledDays)) {
|
|
192
|
+
return disabledDays.some(dateToCheck => {
|
|
193
|
+
return dateToCheck.toDateString() === date.toDateString();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (typeof disabledDays === "object" && "after" in disabledDays && "before" in disabledDays) {
|
|
197
|
+
return !(date.getTime() >
|
|
198
|
+
(disabledDays.before ? subtract(disabledDays.before, 1, "days").getTime() : date.getTime()) &&
|
|
199
|
+
date.getTime() < (disabledDays.after?.getTime() ?? date.getTime()));
|
|
200
|
+
}
|
|
201
|
+
if (typeof disabledDays === "object" && "furtherBackThanDays" in disabledDays) {
|
|
202
|
+
const dateRange = calculateDateRange({
|
|
203
|
+
direction: "last",
|
|
204
|
+
count: disabledDays.furtherBackThanDays,
|
|
205
|
+
unit: "day",
|
|
206
|
+
});
|
|
207
|
+
if (dateRange.start && dateRange.end) {
|
|
208
|
+
return date < dateRange.start || date > dateRange.end;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}, value:
|
|
214
|
+
// type is wrong here if we add array of 2 elements if end is undefined and set to null it will not highlight the start date
|
|
215
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion, @typescript-eslint/no-explicit-any
|
|
216
|
+
newRange.end ? [newRange.start ?? null, newRange.end ?? null] : (newRange.start ?? null) }), jsxRuntime.jsxs("div", { className: "flex w-full gap-2", children: [jsxRuntime.jsx(reactComponents.Button, { className: "mr-auto", dataTestId: "range-popover-clear-button", onClick: clearSelectedDays, variant: "secondary", children: t("layout.actions.clear") }), jsxRuntime.jsx(reactComponents.Button, { dataTestId: "range-popover-cancel-button", onClick: () => handleCancel(), variant: "ghost-neutral", children: cancelButtonLabel ? cancelButtonLabel : t("layout.actions.cancel") }), jsxRuntime.jsx(reactComponents.Button, { dataTestId: "range-popover-apply-button", disabled: !newRange.start || !newRange.end, onClick: () => handleApply(), children: t("layout.actions.apply") })] })] }));
|
|
226
217
|
};
|
|
227
218
|
|
|
228
219
|
const temporalArithmeticTypeMapping = {
|
|
@@ -250,8 +241,8 @@ const useCalculateDateRange = () => {
|
|
|
250
241
|
const endDate = add(nowDate, count, unitType);
|
|
251
242
|
const adjustedEndDate = subtract(endDate, 1, "days");
|
|
252
243
|
return {
|
|
253
|
-
|
|
254
|
-
|
|
244
|
+
start: startOf(nowDate, "day"),
|
|
245
|
+
end: endOf(adjustedEndDate, "day"),
|
|
255
246
|
};
|
|
256
247
|
}
|
|
257
248
|
else {
|
|
@@ -259,8 +250,8 @@ const useCalculateDateRange = () => {
|
|
|
259
250
|
const startDate = subtract(nowDate, count, unitType);
|
|
260
251
|
const adjustedStartDate = add(startDate, 1, "days");
|
|
261
252
|
return {
|
|
262
|
-
|
|
263
|
-
|
|
253
|
+
start: startOf(adjustedStartDate, "day"),
|
|
254
|
+
end: endOf(nowDate, "day"),
|
|
264
255
|
};
|
|
265
256
|
}
|
|
266
257
|
}, [nowDate, startOf, endOf, subtract, add]);
|
|
@@ -386,8 +377,8 @@ const useFilterTemporalPeriodsInRange = () => {
|
|
|
386
377
|
const { difference } = reactDateAndTimeHooks.useDateAndTime();
|
|
387
378
|
const calculateDateRange = useCalculateDateRange();
|
|
388
379
|
const getDayCountInRange = react.useCallback((temporalPeriod) => {
|
|
389
|
-
const {
|
|
390
|
-
return difference(
|
|
380
|
+
const { start, end } = calculateDateRange(temporalPeriod);
|
|
381
|
+
return difference(start ?? new Date(), end ?? new Date(), "day");
|
|
391
382
|
}, [calculateDateRange, difference]);
|
|
392
383
|
return react.useCallback(({ temporalPeriods, maxDaysInRange, }) => {
|
|
393
384
|
if (!maxDaysInRange) {
|
|
@@ -516,9 +507,9 @@ const createTemporalPeriodCombinations = ({ direction, count, unit, }) => {
|
|
|
516
507
|
* formatCustomDateRangeLabel({ from: new Date("2025-06-16"), to: new Date("2025-06-17") }); // "June 16, 2025 - June 17, 2025"
|
|
517
508
|
*/
|
|
518
509
|
const formatCustomDateRangeLabel = (dateRange) => {
|
|
519
|
-
return (dateAndTimeUtils.formatDateUtil(dateRange.
|
|
510
|
+
return (dateAndTimeUtils.formatDateUtil(dateRange.start ?? new Date(), { dateFormat: "medium", selectFormat: "dateOnly" }) +
|
|
520
511
|
" - " +
|
|
521
|
-
dateAndTimeUtils.formatDateUtil(dateRange.
|
|
512
|
+
dateAndTimeUtils.formatDateUtil(dateRange.end ?? new Date(), { dateFormat: "medium", selectFormat: "dateOnly" }));
|
|
522
513
|
};
|
|
523
514
|
|
|
524
515
|
/**
|
|
@@ -614,13 +605,13 @@ const DayRangeSelect = ({ className, dataTestId, disabled, selectedDateRange, on
|
|
|
614
605
|
onRangeSelect(value);
|
|
615
606
|
}, [onRangeSelect]);
|
|
616
607
|
const handleCustomDateRangeSelect = react.useCallback((dateRange, closeMenu) => {
|
|
617
|
-
if (!dateRange || !dateRange.
|
|
608
|
+
if (!dateRange || !dateRange.start || !dateRange.end) {
|
|
618
609
|
handleReset();
|
|
619
610
|
return;
|
|
620
611
|
}
|
|
621
612
|
closeMenu();
|
|
622
613
|
const _selectedDateRange = {
|
|
623
|
-
dateRange: {
|
|
614
|
+
dateRange: { start: dateRange.start, end: dateRange.end },
|
|
624
615
|
temporalPeriod: undefined,
|
|
625
616
|
};
|
|
626
617
|
setSelectedRange(_selectedDateRange);
|
|
@@ -629,10 +620,58 @@ const DayRangeSelect = ({ className, dataTestId, disabled, selectedDateRange, on
|
|
|
629
620
|
return (jsxRuntime.jsxs(reactComponents.Popover, { onOpenStateChange: state => !state && setIsCustomDateRangeOpen(false), placement: "bottom-start", children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx(reactComponents.Button, { className: cvaTriggerButton({ active: !!selectedDateRange, className }), dataTestId: dataTestId, disabled: disabled, fullWidth: fullWidth, prefix: jsxRuntime.jsx(reactComponents.Icon, { ariaLabel: t("input.icon.tooltip.calendar"), color: disabled ? "secondary" : selectedDateRange ? "primary" : undefined, name: "Calendar", size: size === "large" ? "medium" : "small" }), size: size, variant: "secondary", children: popoverTriggerLabel ?? t("trigger.selectDateRange") }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { dataTestId: dataTestId ? `${dataTestId}-popover-content` : undefined, children: closeMenu => {
|
|
630
621
|
return !isCustomDateRangeOpen ? (jsxRuntime.jsxs(reactComponents.MenuList, { className: "min-w-[280px]", children: [jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [showDateRangeSearch ? (jsxRuntime.jsx(reactFormComponents.Search, { className: "w-full", dataTestId: dataTestId ? `${dataTestId}-search-input` : undefined, onChange: event => handleDateRangeSearch(event.target.value), onClear: () => handleDateRangeSearch(""), placeholder: allowedDirection === "last"
|
|
631
622
|
? t("input.placeholder.customRange.last")
|
|
632
|
-
: t("input.placeholder.customRange.next"), value: dateRangeSearchValue })) : null, jsxRuntime.jsx(reactComponents.Button, { className: "ml-auto", dataTestId: dataTestId ? `${dataTestId}-reset-button` : undefined, disabled: !selectedDateRange, onClick: handleReset, size: "small", variant: "ghost", children: t("layout.actions.reset") })] }), jsxRuntime.jsx("hr", { className: "border-secondary-200 my-1", role: "separator" }), jsxRuntime.jsx(DayRangeSelectOptions, { dataTestId: dataTestId ? `${dataTestId}-options` : undefined, onSelect: value => handleOptionsSelect(value, closeMenu), selectedDateRange: currentSelectedRange, temporalPeriods: temporalPeriods }), showCustomDateRangeOption ? (jsxRuntime.jsxs("section", { children: [jsxRuntime.jsx(reactComponents.MenuDivider, {}), jsxRuntime.jsx(reactComponents.MenuItem, { dataTestId: dataTestId ? `${dataTestId}-custom-range` : undefined, label: t("trigger.customRange"), onClick: () => setIsCustomDateRangeOpen(true), selected: isCustomDateRangeSelected, suffix: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [isCustomDateRangeSelected ? jsxRuntime.jsx(reactComponents.Icon, { color: "primary", name: "Check", size: "small" }) : null, jsxRuntime.jsx(reactComponents.Icon, { name: "ChevronRight", size: "small" })] }) })] })) : null] })) : (jsxRuntime.jsx(reactComponents.Card, { children: jsxRuntime.jsx(DayRangePicker, { cancelButtonLabel: t("layout.actions.back"), disabledDays: customDateRangeDisabledDays, language: "en",
|
|
623
|
+
: t("input.placeholder.customRange.next"), value: dateRangeSearchValue })) : null, jsxRuntime.jsx(reactComponents.Button, { className: "ml-auto", dataTestId: dataTestId ? `${dataTestId}-reset-button` : undefined, disabled: !selectedDateRange, onClick: handleReset, size: "small", variant: "ghost", children: t("layout.actions.reset") })] }), jsxRuntime.jsx("hr", { className: "border-secondary-200 my-1", role: "separator" }), jsxRuntime.jsx(DayRangeSelectOptions, { dataTestId: dataTestId ? `${dataTestId}-options` : undefined, onSelect: value => handleOptionsSelect(value, closeMenu), selectedDateRange: currentSelectedRange, temporalPeriods: temporalPeriods }), showCustomDateRangeOption ? (jsxRuntime.jsxs("section", { children: [jsxRuntime.jsx(reactComponents.MenuDivider, {}), jsxRuntime.jsx(reactComponents.MenuItem, { dataTestId: dataTestId ? `${dataTestId}-custom-range` : undefined, label: t("trigger.customRange"), onClick: () => setIsCustomDateRangeOpen(true), selected: isCustomDateRangeSelected, suffix: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [isCustomDateRangeSelected ? jsxRuntime.jsx(reactComponents.Icon, { color: "primary", name: "Check", size: "small" }) : null, jsxRuntime.jsx(reactComponents.Icon, { name: "ChevronRight", size: "small" })] }) })] })) : null] })) : (jsxRuntime.jsx(reactComponents.Card, { children: jsxRuntime.jsx(DayRangePicker, { cancelButtonLabel: t("layout.actions.back"), disabledDays: customDateRangeDisabledDays, language: "en", onClose: () => setIsCustomDateRangeOpen(false), onRangeSelect: dateRange => handleCustomDateRangeSelect(dateRange
|
|
624
|
+
? {
|
|
625
|
+
start: dateRange.start ?? undefined,
|
|
626
|
+
end: dateRange.end ?? undefined,
|
|
627
|
+
}
|
|
628
|
+
: undefined, closeMenu), selectedDays: currentSelectedRange?.dateRange, timezone: timezone }) }));
|
|
633
629
|
} })] }));
|
|
634
630
|
};
|
|
635
631
|
|
|
632
|
+
/**
|
|
633
|
+
* The DayPicker component is used when the user needs to select a date.
|
|
634
|
+
|
|
635
|
+
* @param {DayPickerProps} props - The props for the DayPicker component
|
|
636
|
+
* @returns {React.ReactElement} DayPicker component
|
|
637
|
+
*/
|
|
638
|
+
const DayPicker = ({ onDaySelect, disabledDays, selectedDay, language, className, dataTestId, }) => {
|
|
639
|
+
const { subtract } = reactDateAndTimeHooks.useDateAndTime();
|
|
640
|
+
const calculateDateRange = useCalculateDateRange();
|
|
641
|
+
return (jsxRuntime.jsx("div", { "data-testid": dataTestId, children: jsxRuntime.jsx(Calendar, { activeStartDate: selectedDay ?? undefined, allowPartialRange: true, className: tailwindMerge.twMerge("custom-day-picker", "range-picker", className, "p-0"), locale: language, onChange: value => {
|
|
642
|
+
if (value) {
|
|
643
|
+
onDaySelect && onDaySelect(value);
|
|
644
|
+
}
|
|
645
|
+
}, selectRange: false, tileDisabled: ({ date, view }) => {
|
|
646
|
+
if (view === "month") {
|
|
647
|
+
if (typeof disabledDays === "function") {
|
|
648
|
+
return disabledDays(date);
|
|
649
|
+
}
|
|
650
|
+
if (Array.isArray(disabledDays)) {
|
|
651
|
+
return disabledDays.some(dateToCheck => {
|
|
652
|
+
return dateToCheck instanceof Date && dateToCheck.toDateString() === date.toDateString();
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
if (typeof disabledDays === "object" && "after" in disabledDays && "before" in disabledDays) {
|
|
656
|
+
return !(date.getTime() >
|
|
657
|
+
(disabledDays.before ? subtract(disabledDays.before, 1, "days").getTime() : date.getTime()) &&
|
|
658
|
+
date.getTime() < (disabledDays.after?.getTime() ?? date.getTime()));
|
|
659
|
+
}
|
|
660
|
+
if (typeof disabledDays === "object" && "furtherBackThanDays" in disabledDays) {
|
|
661
|
+
const dateRange = calculateDateRange({
|
|
662
|
+
direction: "last",
|
|
663
|
+
count: disabledDays.furtherBackThanDays,
|
|
664
|
+
unit: "day",
|
|
665
|
+
});
|
|
666
|
+
if (dateRange.start && dateRange.end) {
|
|
667
|
+
return date < dateRange.start || date > dateRange.end;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return false;
|
|
672
|
+
}, value: selectedDay ?? null }) }));
|
|
673
|
+
};
|
|
674
|
+
|
|
636
675
|
const cvaTimelineElement = cssClassVarianceUtilities.cvaMerge([
|
|
637
676
|
"flex",
|
|
638
677
|
"group/timeline",
|
package/index.esm.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { registerTranslations, useNamespaceTranslation } from '@trackunit/i18n-library-translation';
|
|
3
3
|
import { toDateUtil, formatDateUtil, isEqualUtil, timeSinceAuto, getHourCycle } from '@trackunit/date-and-time-utils';
|
|
4
|
-
import { useLocale
|
|
4
|
+
import { useLocale, useDateAndTime } from '@trackunit/react-date-and-time-hooks';
|
|
5
5
|
import { color } from '@trackunit/ui-design-tokens';
|
|
6
6
|
import { useMemo, useCallback, useState, useEffect, useRef, Children, isValidElement } from 'react';
|
|
7
7
|
import { Tooltip, Button, MenuItem, Icon, Popover, PopoverTrigger, PopoverContent, MenuList, MenuDivider, Card, Text } from '@trackunit/react-components';
|
|
8
8
|
import { DateTimeFormat, nonNullable, objectKeys } from '@trackunit/shared-utils';
|
|
9
|
-
import
|
|
9
|
+
import Calendar from 'react-calendar';
|
|
10
10
|
import { twMerge } from 'tailwind-merge';
|
|
11
|
-
import { enGB, enUS, da, de, cs, nl, fr, fi, hu, it, nb, pl, pt, ru, ro, es, sv, ja, th } from 'date-fns/locale';
|
|
12
11
|
import { Search } from '@trackunit/react-form-components';
|
|
13
12
|
import { cvaMerge } from '@trackunit/css-class-variance-utilities';
|
|
14
13
|
import { lowerCase } from 'string-ts';
|
|
@@ -107,7 +106,7 @@ const MS_PER_HOUR = 60 * 60 * 1000;
|
|
|
107
106
|
*/
|
|
108
107
|
const DateTime = ({ value, format, className, fromNow, withTitle, titleFormat, timezone, subtle, calendar, }) => {
|
|
109
108
|
const { t } = useTranslation();
|
|
110
|
-
const locale = useLocale
|
|
109
|
+
const locale = useLocale();
|
|
111
110
|
const nowDate = useMemo(() => new Date(), []);
|
|
112
111
|
const date = value ? toDateUtil(value) : nowDate;
|
|
113
112
|
const newDateTime = formatDateUtil(date, format, timezone?.id, locale);
|
|
@@ -136,75 +135,33 @@ const DateTimeHumanized = ({ value }) => {
|
|
|
136
135
|
return (jsx(Tooltip, { label: jsx(DateTime, { format: tooltipFormat, value: value }), children: jsx(DateTime, { className: "underline decoration-neutral-300 decoration-dotted", fromNow: true, value: value }) }));
|
|
137
136
|
};
|
|
138
137
|
|
|
139
|
-
const localeLookup = {
|
|
140
|
-
en: enGB,
|
|
141
|
-
"en-US": enUS,
|
|
142
|
-
"en-GB": enGB,
|
|
143
|
-
da,
|
|
144
|
-
de,
|
|
145
|
-
cs,
|
|
146
|
-
nl,
|
|
147
|
-
fr,
|
|
148
|
-
fi,
|
|
149
|
-
hu,
|
|
150
|
-
it,
|
|
151
|
-
nb,
|
|
152
|
-
pl,
|
|
153
|
-
pt,
|
|
154
|
-
ru,
|
|
155
|
-
ro,
|
|
156
|
-
es,
|
|
157
|
-
sv,
|
|
158
|
-
ja,
|
|
159
|
-
th,
|
|
160
|
-
};
|
|
161
|
-
/**
|
|
162
|
-
* A helper function to get the matching Locale object for a given language.
|
|
163
|
-
*
|
|
164
|
-
* @param {string} language The language to match
|
|
165
|
-
* @returns {Locale} The corresponding Locale object
|
|
166
|
-
*/
|
|
167
|
-
const useLocale = (language) => {
|
|
168
|
-
return localeLookup[language] || localeLookup.en || enGB;
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* The DayPicker component is used when the user needs to select a date.
|
|
173
|
-
|
|
174
|
-
* @param {DayPickerProps} props - The props for the DayPicker component
|
|
175
|
-
* @returns {ReactElement} DayPicker component
|
|
176
|
-
*/
|
|
177
|
-
const DayPicker = ({ onDaySelect, disabledDays, selectedDays, language, className, dataTestId, max, }) => {
|
|
178
|
-
const locale = useLocale(language);
|
|
179
|
-
return (jsx(DayPicker$1, { className: twMerge("custom-day-picker", className), disabled: disabledDays, footer: jsx("div", { "data-testid": dataTestId }), locale: locale, max: max, onDayClick: onDaySelect, selected: selectedDays }));
|
|
180
|
-
};
|
|
181
|
-
|
|
182
138
|
/**
|
|
183
139
|
* The DayRangePicker component should be used when the user needs to select a range of dates.
|
|
184
140
|
*
|
|
185
141
|
* @param {DayRangePickerProps} props - The props for the DayRangePicker component
|
|
186
142
|
* @returns {ReactElement} DayRangePicker component
|
|
187
143
|
*/
|
|
188
|
-
const DayRangePicker = ({ onRangeSelect, selectedDays, disabledDays, dataTestId, language, className,
|
|
189
|
-
const [newRange, setNewRange] = useState(selectedDays ?? {
|
|
144
|
+
const DayRangePicker = ({ onRangeSelect, selectedDays, disabledDays, dataTestId, language, className, timezone, cancelButtonLabel, onClose, }) => {
|
|
145
|
+
const [newRange, setNewRange] = useState(selectedDays ?? { start: undefined, end: undefined });
|
|
190
146
|
const [t] = useTranslation();
|
|
191
|
-
const
|
|
147
|
+
const { subtract } = useDateAndTime();
|
|
148
|
+
const calculateDateRange = useCalculateDateRange();
|
|
192
149
|
useEffect(() => {
|
|
193
150
|
if (selectedDays) {
|
|
194
151
|
setNewRange(selectedDays);
|
|
195
152
|
}
|
|
196
153
|
}, [selectedDays]);
|
|
197
154
|
const handleOnRangeSelect = useCallback((range) => {
|
|
198
|
-
if (range && range.
|
|
199
|
-
if (range.
|
|
200
|
-
setNewRange({
|
|
155
|
+
if (range && range.start) {
|
|
156
|
+
if (range.end === undefined) {
|
|
157
|
+
setNewRange({ start: range.start, end: range.start });
|
|
201
158
|
}
|
|
202
159
|
else {
|
|
203
|
-
setNewRange({
|
|
160
|
+
setNewRange({ start: range.start, end: range.end });
|
|
204
161
|
}
|
|
205
162
|
}
|
|
206
163
|
else {
|
|
207
|
-
setNewRange({
|
|
164
|
+
setNewRange({ start: undefined, end: undefined });
|
|
208
165
|
}
|
|
209
166
|
}, []);
|
|
210
167
|
const handleCancel = useCallback(() => {
|
|
@@ -214,13 +171,47 @@ const DayRangePicker = ({ onRangeSelect, selectedDays, disabledDays, dataTestId,
|
|
|
214
171
|
onClose && onClose();
|
|
215
172
|
}, [selectedDays, onClose]);
|
|
216
173
|
const clearSelectedDays = () => {
|
|
217
|
-
setNewRange({
|
|
174
|
+
setNewRange({ start: undefined, end: undefined });
|
|
218
175
|
};
|
|
219
176
|
const handleApply = useCallback(() => {
|
|
220
177
|
onRangeSelect && onRangeSelect(newRange);
|
|
221
178
|
onClose && onClose();
|
|
222
179
|
}, [onRangeSelect, newRange, onClose]);
|
|
223
|
-
return (jsxs("div", { className: "flex w-min flex-col gap-4 p-2",
|
|
180
|
+
return (jsxs("div", { className: "flex w-min flex-col gap-4 p-2", "data-testid": dataTestId, children: [jsx(Calendar, { activeStartDate: newRange.start ?? undefined, allowPartialRange: true, className: twMerge("custom-day-picker", "range-picker", className, "p-0"), locale: language, onChange: value => {
|
|
181
|
+
if (Array.isArray(value) && value[0] && value[1]) {
|
|
182
|
+
handleOnRangeSelect({ start: value[0], end: value[1] });
|
|
183
|
+
}
|
|
184
|
+
}, selectRange: true, tileDisabled: ({ date, view }) => {
|
|
185
|
+
if (view === "month") {
|
|
186
|
+
if (typeof disabledDays === "function") {
|
|
187
|
+
return disabledDays(date);
|
|
188
|
+
}
|
|
189
|
+
if (Array.isArray(disabledDays)) {
|
|
190
|
+
return disabledDays.some(dateToCheck => {
|
|
191
|
+
return dateToCheck.toDateString() === date.toDateString();
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (typeof disabledDays === "object" && "after" in disabledDays && "before" in disabledDays) {
|
|
195
|
+
return !(date.getTime() >
|
|
196
|
+
(disabledDays.before ? subtract(disabledDays.before, 1, "days").getTime() : date.getTime()) &&
|
|
197
|
+
date.getTime() < (disabledDays.after?.getTime() ?? date.getTime()));
|
|
198
|
+
}
|
|
199
|
+
if (typeof disabledDays === "object" && "furtherBackThanDays" in disabledDays) {
|
|
200
|
+
const dateRange = calculateDateRange({
|
|
201
|
+
direction: "last",
|
|
202
|
+
count: disabledDays.furtherBackThanDays,
|
|
203
|
+
unit: "day",
|
|
204
|
+
});
|
|
205
|
+
if (dateRange.start && dateRange.end) {
|
|
206
|
+
return date < dateRange.start || date > dateRange.end;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
}, value:
|
|
212
|
+
// type is wrong here if we add array of 2 elements if end is undefined and set to null it will not highlight the start date
|
|
213
|
+
// eslint-disable-next-line local-rules/no-typescript-assertion, @typescript-eslint/no-explicit-any
|
|
214
|
+
newRange.end ? [newRange.start ?? null, newRange.end ?? null] : (newRange.start ?? null) }), jsxs("div", { className: "flex w-full gap-2", children: [jsx(Button, { className: "mr-auto", dataTestId: "range-popover-clear-button", onClick: clearSelectedDays, variant: "secondary", children: t("layout.actions.clear") }), jsx(Button, { dataTestId: "range-popover-cancel-button", onClick: () => handleCancel(), variant: "ghost-neutral", children: cancelButtonLabel ? cancelButtonLabel : t("layout.actions.cancel") }), jsx(Button, { dataTestId: "range-popover-apply-button", disabled: !newRange.start || !newRange.end, onClick: () => handleApply(), children: t("layout.actions.apply") })] })] }));
|
|
224
215
|
};
|
|
225
216
|
|
|
226
217
|
const temporalArithmeticTypeMapping = {
|
|
@@ -248,8 +239,8 @@ const useCalculateDateRange = () => {
|
|
|
248
239
|
const endDate = add(nowDate, count, unitType);
|
|
249
240
|
const adjustedEndDate = subtract(endDate, 1, "days");
|
|
250
241
|
return {
|
|
251
|
-
|
|
252
|
-
|
|
242
|
+
start: startOf(nowDate, "day"),
|
|
243
|
+
end: endOf(adjustedEndDate, "day"),
|
|
253
244
|
};
|
|
254
245
|
}
|
|
255
246
|
else {
|
|
@@ -257,8 +248,8 @@ const useCalculateDateRange = () => {
|
|
|
257
248
|
const startDate = subtract(nowDate, count, unitType);
|
|
258
249
|
const adjustedStartDate = add(startDate, 1, "days");
|
|
259
250
|
return {
|
|
260
|
-
|
|
261
|
-
|
|
251
|
+
start: startOf(adjustedStartDate, "day"),
|
|
252
|
+
end: endOf(nowDate, "day"),
|
|
262
253
|
};
|
|
263
254
|
}
|
|
264
255
|
}, [nowDate, startOf, endOf, subtract, add]);
|
|
@@ -384,8 +375,8 @@ const useFilterTemporalPeriodsInRange = () => {
|
|
|
384
375
|
const { difference } = useDateAndTime();
|
|
385
376
|
const calculateDateRange = useCalculateDateRange();
|
|
386
377
|
const getDayCountInRange = useCallback((temporalPeriod) => {
|
|
387
|
-
const {
|
|
388
|
-
return difference(
|
|
378
|
+
const { start, end } = calculateDateRange(temporalPeriod);
|
|
379
|
+
return difference(start ?? new Date(), end ?? new Date(), "day");
|
|
389
380
|
}, [calculateDateRange, difference]);
|
|
390
381
|
return useCallback(({ temporalPeriods, maxDaysInRange, }) => {
|
|
391
382
|
if (!maxDaysInRange) {
|
|
@@ -514,9 +505,9 @@ const createTemporalPeriodCombinations = ({ direction, count, unit, }) => {
|
|
|
514
505
|
* formatCustomDateRangeLabel({ from: new Date("2025-06-16"), to: new Date("2025-06-17") }); // "June 16, 2025 - June 17, 2025"
|
|
515
506
|
*/
|
|
516
507
|
const formatCustomDateRangeLabel = (dateRange) => {
|
|
517
|
-
return (formatDateUtil(dateRange.
|
|
508
|
+
return (formatDateUtil(dateRange.start ?? new Date(), { dateFormat: "medium", selectFormat: "dateOnly" }) +
|
|
518
509
|
" - " +
|
|
519
|
-
formatDateUtil(dateRange.
|
|
510
|
+
formatDateUtil(dateRange.end ?? new Date(), { dateFormat: "medium", selectFormat: "dateOnly" }));
|
|
520
511
|
};
|
|
521
512
|
|
|
522
513
|
/**
|
|
@@ -612,13 +603,13 @@ const DayRangeSelect = ({ className, dataTestId, disabled, selectedDateRange, on
|
|
|
612
603
|
onRangeSelect(value);
|
|
613
604
|
}, [onRangeSelect]);
|
|
614
605
|
const handleCustomDateRangeSelect = useCallback((dateRange, closeMenu) => {
|
|
615
|
-
if (!dateRange || !dateRange.
|
|
606
|
+
if (!dateRange || !dateRange.start || !dateRange.end) {
|
|
616
607
|
handleReset();
|
|
617
608
|
return;
|
|
618
609
|
}
|
|
619
610
|
closeMenu();
|
|
620
611
|
const _selectedDateRange = {
|
|
621
|
-
dateRange: {
|
|
612
|
+
dateRange: { start: dateRange.start, end: dateRange.end },
|
|
622
613
|
temporalPeriod: undefined,
|
|
623
614
|
};
|
|
624
615
|
setSelectedRange(_selectedDateRange);
|
|
@@ -627,10 +618,58 @@ const DayRangeSelect = ({ className, dataTestId, disabled, selectedDateRange, on
|
|
|
627
618
|
return (jsxs(Popover, { onOpenStateChange: state => !state && setIsCustomDateRangeOpen(false), placement: "bottom-start", children: [jsx(PopoverTrigger, { children: jsx(Button, { className: cvaTriggerButton({ active: !!selectedDateRange, className }), dataTestId: dataTestId, disabled: disabled, fullWidth: fullWidth, prefix: jsx(Icon, { ariaLabel: t("input.icon.tooltip.calendar"), color: disabled ? "secondary" : selectedDateRange ? "primary" : undefined, name: "Calendar", size: size === "large" ? "medium" : "small" }), size: size, variant: "secondary", children: popoverTriggerLabel ?? t("trigger.selectDateRange") }) }), jsx(PopoverContent, { dataTestId: dataTestId ? `${dataTestId}-popover-content` : undefined, children: closeMenu => {
|
|
628
619
|
return !isCustomDateRangeOpen ? (jsxs(MenuList, { className: "min-w-[280px]", children: [jsxs("div", { className: "flex flex-col gap-1", children: [showDateRangeSearch ? (jsx(Search, { className: "w-full", dataTestId: dataTestId ? `${dataTestId}-search-input` : undefined, onChange: event => handleDateRangeSearch(event.target.value), onClear: () => handleDateRangeSearch(""), placeholder: allowedDirection === "last"
|
|
629
620
|
? t("input.placeholder.customRange.last")
|
|
630
|
-
: t("input.placeholder.customRange.next"), value: dateRangeSearchValue })) : null, jsx(Button, { className: "ml-auto", dataTestId: dataTestId ? `${dataTestId}-reset-button` : undefined, disabled: !selectedDateRange, onClick: handleReset, size: "small", variant: "ghost", children: t("layout.actions.reset") })] }), jsx("hr", { className: "border-secondary-200 my-1", role: "separator" }), jsx(DayRangeSelectOptions, { dataTestId: dataTestId ? `${dataTestId}-options` : undefined, onSelect: value => handleOptionsSelect(value, closeMenu), selectedDateRange: currentSelectedRange, temporalPeriods: temporalPeriods }), showCustomDateRangeOption ? (jsxs("section", { children: [jsx(MenuDivider, {}), jsx(MenuItem, { dataTestId: dataTestId ? `${dataTestId}-custom-range` : undefined, label: t("trigger.customRange"), onClick: () => setIsCustomDateRangeOpen(true), selected: isCustomDateRangeSelected, suffix: jsxs(Fragment, { children: [isCustomDateRangeSelected ? jsx(Icon, { color: "primary", name: "Check", size: "small" }) : null, jsx(Icon, { name: "ChevronRight", size: "small" })] }) })] })) : null] })) : (jsx(Card, { children: jsx(DayRangePicker, { cancelButtonLabel: t("layout.actions.back"), disabledDays: customDateRangeDisabledDays, language: "en",
|
|
621
|
+
: t("input.placeholder.customRange.next"), value: dateRangeSearchValue })) : null, jsx(Button, { className: "ml-auto", dataTestId: dataTestId ? `${dataTestId}-reset-button` : undefined, disabled: !selectedDateRange, onClick: handleReset, size: "small", variant: "ghost", children: t("layout.actions.reset") })] }), jsx("hr", { className: "border-secondary-200 my-1", role: "separator" }), jsx(DayRangeSelectOptions, { dataTestId: dataTestId ? `${dataTestId}-options` : undefined, onSelect: value => handleOptionsSelect(value, closeMenu), selectedDateRange: currentSelectedRange, temporalPeriods: temporalPeriods }), showCustomDateRangeOption ? (jsxs("section", { children: [jsx(MenuDivider, {}), jsx(MenuItem, { dataTestId: dataTestId ? `${dataTestId}-custom-range` : undefined, label: t("trigger.customRange"), onClick: () => setIsCustomDateRangeOpen(true), selected: isCustomDateRangeSelected, suffix: jsxs(Fragment, { children: [isCustomDateRangeSelected ? jsx(Icon, { color: "primary", name: "Check", size: "small" }) : null, jsx(Icon, { name: "ChevronRight", size: "small" })] }) })] })) : null] })) : (jsx(Card, { children: jsx(DayRangePicker, { cancelButtonLabel: t("layout.actions.back"), disabledDays: customDateRangeDisabledDays, language: "en", onClose: () => setIsCustomDateRangeOpen(false), onRangeSelect: dateRange => handleCustomDateRangeSelect(dateRange
|
|
622
|
+
? {
|
|
623
|
+
start: dateRange.start ?? undefined,
|
|
624
|
+
end: dateRange.end ?? undefined,
|
|
625
|
+
}
|
|
626
|
+
: undefined, closeMenu), selectedDays: currentSelectedRange?.dateRange, timezone: timezone }) }));
|
|
631
627
|
} })] }));
|
|
632
628
|
};
|
|
633
629
|
|
|
630
|
+
/**
|
|
631
|
+
* The DayPicker component is used when the user needs to select a date.
|
|
632
|
+
|
|
633
|
+
* @param {DayPickerProps} props - The props for the DayPicker component
|
|
634
|
+
* @returns {React.ReactElement} DayPicker component
|
|
635
|
+
*/
|
|
636
|
+
const DayPicker = ({ onDaySelect, disabledDays, selectedDay, language, className, dataTestId, }) => {
|
|
637
|
+
const { subtract } = useDateAndTime();
|
|
638
|
+
const calculateDateRange = useCalculateDateRange();
|
|
639
|
+
return (jsx("div", { "data-testid": dataTestId, children: jsx(Calendar, { activeStartDate: selectedDay ?? undefined, allowPartialRange: true, className: twMerge("custom-day-picker", "range-picker", className, "p-0"), locale: language, onChange: value => {
|
|
640
|
+
if (value) {
|
|
641
|
+
onDaySelect && onDaySelect(value);
|
|
642
|
+
}
|
|
643
|
+
}, selectRange: false, tileDisabled: ({ date, view }) => {
|
|
644
|
+
if (view === "month") {
|
|
645
|
+
if (typeof disabledDays === "function") {
|
|
646
|
+
return disabledDays(date);
|
|
647
|
+
}
|
|
648
|
+
if (Array.isArray(disabledDays)) {
|
|
649
|
+
return disabledDays.some(dateToCheck => {
|
|
650
|
+
return dateToCheck instanceof Date && dateToCheck.toDateString() === date.toDateString();
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
if (typeof disabledDays === "object" && "after" in disabledDays && "before" in disabledDays) {
|
|
654
|
+
return !(date.getTime() >
|
|
655
|
+
(disabledDays.before ? subtract(disabledDays.before, 1, "days").getTime() : date.getTime()) &&
|
|
656
|
+
date.getTime() < (disabledDays.after?.getTime() ?? date.getTime()));
|
|
657
|
+
}
|
|
658
|
+
if (typeof disabledDays === "object" && "furtherBackThanDays" in disabledDays) {
|
|
659
|
+
const dateRange = calculateDateRange({
|
|
660
|
+
direction: "last",
|
|
661
|
+
count: disabledDays.furtherBackThanDays,
|
|
662
|
+
unit: "day",
|
|
663
|
+
});
|
|
664
|
+
if (dateRange.start && dateRange.end) {
|
|
665
|
+
return date < dateRange.start || date > dateRange.end;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return false;
|
|
670
|
+
}, value: selectedDay ?? null }) }));
|
|
671
|
+
};
|
|
672
|
+
|
|
634
673
|
const cvaTimelineElement = cvaMerge([
|
|
635
674
|
"flex",
|
|
636
675
|
"group/timeline",
|
|
@@ -726,7 +765,7 @@ const Timeline = ({ className, dataTestId, children, dateHeader, customHeader, t
|
|
|
726
765
|
const [visibleCount, setVisibleCount] = useState(ITEMS_PER_PAGE);
|
|
727
766
|
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
728
767
|
const { formatDate, formatRange } = useDateAndTime();
|
|
729
|
-
const locale = useLocale
|
|
768
|
+
const locale = useLocale();
|
|
730
769
|
const hourCycle = getHourCycle(locale);
|
|
731
770
|
const { t } = useTranslation();
|
|
732
771
|
const childrenArray = useMemo(() => Children.toArray(children), [children]);
|
|
@@ -799,7 +838,7 @@ const Timeline = ({ className, dataTestId, children, dateHeader, customHeader, t
|
|
|
799
838
|
const TimelineElement = ({ date, children, className, dataTestId = "timeline-element", header, onClick, actionButton, selected, customDot, lineStyle = "solid", hoverBehavior = false, }) => {
|
|
800
839
|
const [isHovered, setIsHovered] = useState(false);
|
|
801
840
|
const { formatDate } = useDateAndTime();
|
|
802
|
-
const locale = useLocale
|
|
841
|
+
const locale = useLocale();
|
|
803
842
|
const formattedDate = date ? formatDate(date, { selectFormat: "timeOnly", timeFormat: "short" }) : null;
|
|
804
843
|
const handleMouseEnter = () => {
|
|
805
844
|
if (hoverBehavior) {
|
package/package.json
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-date-and-time-components",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.13",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.x"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"date-fns": "^2.30.0",
|
|
11
|
-
"react-day-picker": "9.5.1",
|
|
12
10
|
"react": "19.0.0",
|
|
13
|
-
"@trackunit/react-components": "1.4.
|
|
14
|
-
"@trackunit/react-date-and-time-hooks": "1.3.
|
|
15
|
-
"@trackunit/date-and-time-utils": "1.3.
|
|
16
|
-
"@trackunit/css-class-variance-utilities": "1.3.
|
|
17
|
-
"@trackunit/ui-icons": "1.3.
|
|
18
|
-
"@trackunit/ui-design-tokens": "1.3.
|
|
19
|
-
"@trackunit/shared-utils": "1.5.
|
|
20
|
-
"@trackunit/i18n-library-translation": "1.3.
|
|
21
|
-
"@trackunit/react-test-setup": "1.0.
|
|
22
|
-
"@trackunit/react-form-components": "1.3.
|
|
11
|
+
"@trackunit/react-components": "1.4.204",
|
|
12
|
+
"@trackunit/react-date-and-time-hooks": "1.3.196",
|
|
13
|
+
"@trackunit/date-and-time-utils": "1.3.166",
|
|
14
|
+
"@trackunit/css-class-variance-utilities": "1.3.166",
|
|
15
|
+
"@trackunit/ui-icons": "1.3.167",
|
|
16
|
+
"@trackunit/ui-design-tokens": "1.3.164",
|
|
17
|
+
"@trackunit/shared-utils": "1.5.166",
|
|
18
|
+
"@trackunit/i18n-library-translation": "1.3.190",
|
|
19
|
+
"@trackunit/react-test-setup": "1.0.56",
|
|
20
|
+
"@trackunit/react-form-components": "1.3.230",
|
|
23
21
|
"string-ts": "^2.0.0",
|
|
24
|
-
"tailwind-merge": "^2.0.0"
|
|
22
|
+
"tailwind-merge": "^2.0.0",
|
|
23
|
+
"react-calendar": "^6.0.0"
|
|
25
24
|
},
|
|
26
25
|
"module": "./index.esm.js",
|
|
27
26
|
"main": "./index.cjs.js",
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { CommonProps } from "@trackunit/react-components";
|
|
2
|
-
|
|
2
|
+
export type FurtherBackThanDays = {
|
|
3
|
+
/**
|
|
4
|
+
* This disables dates that are further back than the number of days specified from today.
|
|
5
|
+
*/
|
|
6
|
+
furtherBackThanDays: number;
|
|
7
|
+
};
|
|
8
|
+
export type DisabledDaysMatcher = ((date: Date) => boolean) | {
|
|
9
|
+
before?: Date;
|
|
10
|
+
after?: Date;
|
|
11
|
+
} | Array<Date> | FurtherBackThanDays;
|
|
3
12
|
export interface DayPickerProps extends CommonProps {
|
|
4
13
|
/**
|
|
5
14
|
* A callback function for when a date is selected
|
|
@@ -8,24 +17,20 @@ export interface DayPickerProps extends CommonProps {
|
|
|
8
17
|
/**
|
|
9
18
|
* Define any days which may not be selected
|
|
10
19
|
*/
|
|
11
|
-
disabledDays?:
|
|
20
|
+
disabledDays?: DisabledDaysMatcher | Array<DisabledDaysMatcher>;
|
|
12
21
|
/**
|
|
13
|
-
* Set any
|
|
22
|
+
* Set any day which are selected
|
|
14
23
|
*/
|
|
15
|
-
|
|
24
|
+
selectedDay?: Date;
|
|
16
25
|
/**
|
|
17
26
|
* Set the language
|
|
18
27
|
*/
|
|
19
28
|
language: string;
|
|
20
|
-
/**
|
|
21
|
-
* The maximum amount of days that can be selected
|
|
22
|
-
*/
|
|
23
|
-
max?: number;
|
|
24
29
|
}
|
|
25
30
|
/**
|
|
26
31
|
* The DayPicker component is used when the user needs to select a date.
|
|
27
32
|
|
|
28
33
|
* @param {DayPickerProps} props - The props for the DayPicker component
|
|
29
|
-
* @returns {ReactElement} DayPicker component
|
|
34
|
+
* @returns {React.ReactElement} DayPicker component
|
|
30
35
|
*/
|
|
31
|
-
export declare const DayPicker: ({ onDaySelect, disabledDays,
|
|
36
|
+
export declare const DayPicker: ({ onDaySelect, disabledDays, selectedDay, language, className, dataTestId, }: DayPickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CommonProps } from "@trackunit/react-components";
|
|
2
|
+
import { DateRange } from "@trackunit/date-and-time-utils";
|
|
2
3
|
import { ReactElement } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import { TimeZone } from "./index";
|
|
4
|
+
import { DisabledDaysMatcher, TimeZone } from "./index";
|
|
5
5
|
export interface DayRangePickerProps extends CommonProps {
|
|
6
6
|
/**
|
|
7
7
|
* A callback function for when a range is selected
|
|
@@ -10,7 +10,7 @@ export interface DayRangePickerProps extends CommonProps {
|
|
|
10
10
|
/**
|
|
11
11
|
* Define any days which may not be selected
|
|
12
12
|
*/
|
|
13
|
-
disabledDays?:
|
|
13
|
+
disabledDays?: DisabledDaysMatcher;
|
|
14
14
|
/**
|
|
15
15
|
* Set any days which are selected
|
|
16
16
|
*/
|
|
@@ -19,10 +19,6 @@ export interface DayRangePickerProps extends CommonProps {
|
|
|
19
19
|
* Set the language
|
|
20
20
|
*/
|
|
21
21
|
language: string;
|
|
22
|
-
/**
|
|
23
|
-
* The maximum amount of days that can be selected
|
|
24
|
-
*/
|
|
25
|
-
max?: number;
|
|
26
22
|
/**
|
|
27
23
|
* Shown time ranges will take timezone into consideration
|
|
28
24
|
*/
|
|
@@ -42,4 +38,4 @@ export interface DayRangePickerProps extends CommonProps {
|
|
|
42
38
|
* @param {DayRangePickerProps} props - The props for the DayRangePicker component
|
|
43
39
|
* @returns {ReactElement} DayRangePicker component
|
|
44
40
|
*/
|
|
45
|
-
export declare const DayRangePicker: ({ onRangeSelect, selectedDays, disabledDays, dataTestId, language, className,
|
|
41
|
+
export declare const DayRangePicker: ({ onRangeSelect, selectedDays, disabledDays, dataTestId, language, className, timezone, cancelButtonLabel, onClose, }: DayRangePickerProps) => ReactElement;
|
|
@@ -8,3 +8,4 @@ export declare const packageName: () => import("react/jsx-runtime").JSX.Element;
|
|
|
8
8
|
export declare const DefaultSDA: () => import("react/jsx-runtime").JSX.Element;
|
|
9
9
|
export declare const VariantWithMax: () => import("react/jsx-runtime").JSX.Element;
|
|
10
10
|
export declare const VariantWithDisabledDays: () => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare const VariantWithDisableRange: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { DateRange } from "@trackunit/date-and-time-utils";
|
|
1
2
|
import { CommonProps } from "@trackunit/react-components";
|
|
2
3
|
import { Size } from "@trackunit/shared-utils";
|
|
3
4
|
import { DayRangePickerProps } from "../DayPicker/DayRangePicker";
|
|
4
|
-
import {
|
|
5
|
+
import { SelectedDateRange, TemporalDirection, TemporalPeriod } from "./types";
|
|
5
6
|
export type DayRangeSelectProps = CommonProps & {
|
|
6
7
|
/**
|
|
7
8
|
* Whether the component is disabled.
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
+
import { DateRange } from "@trackunit/date-and-time-utils";
|
|
1
2
|
import { TemporalArithmeticType, TemporalSameType } from "@trackunit/react-date-and-time-hooks";
|
|
2
|
-
export type DateRange = {
|
|
3
|
-
from: Date;
|
|
4
|
-
to: Date;
|
|
5
|
-
};
|
|
6
3
|
export type TemporalUnit = Exclude<TemporalArithmeticType | TemporalSameType, "year" | "hours" | "minutes" | "years">;
|
|
7
4
|
export type TemporalDirection = "next" | "last";
|
|
8
5
|
export type TemporalPeriod = {
|
|
@@ -3,5 +3,5 @@ import { DateTime } from "./DateTime";
|
|
|
3
3
|
type Story = StoryObj<typeof DateTime>;
|
|
4
4
|
declare const meta: Meta<typeof DateTime>;
|
|
5
5
|
export default meta;
|
|
6
|
-
export declare const packageName: () => import("react/jsx-runtime").JSX.Element;
|
|
7
6
|
export declare const Default: Story;
|
|
7
|
+
export declare const packageName: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Locale } from "date-fns";
|
|
2
|
-
/**
|
|
3
|
-
* A helper function to get the matching Locale object for a given language.
|
|
4
|
-
*
|
|
5
|
-
* @param {string} language The language to match
|
|
6
|
-
* @returns {Locale} The corresponding Locale object
|
|
7
|
-
*/
|
|
8
|
-
export declare const useLocale: (language: string) => Locale;
|