periplo-ui 3.28.1 → 3.28.2
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.
|
@@ -10,11 +10,11 @@ export type IsoDateRange = {
|
|
|
10
10
|
from?: string;
|
|
11
11
|
to?: string;
|
|
12
12
|
};
|
|
13
|
-
type DatePickerOutput<Variant extends 'single' | 'range', Format extends ValueFormat> = Variant extends 'single' ? Format extends 'date' ? Date |
|
|
14
|
-
type DatePickerInput<V extends 'single' | 'range'> = V extends 'single' ? Date | string |
|
|
13
|
+
type DatePickerOutput<Variant extends 'single' | 'range', Format extends ValueFormat> = Variant extends 'single' ? Format extends 'date' ? Date | null : string | null : Format extends 'date' ? DateRange | null : IsoDateRange | null;
|
|
14
|
+
type DatePickerInput<V extends 'single' | 'range'> = V extends 'single' ? Date | string | null : {
|
|
15
15
|
from?: Date | string;
|
|
16
16
|
to?: Date | string;
|
|
17
|
-
} |
|
|
17
|
+
} | null;
|
|
18
18
|
export type DatePickerProps<V extends 'single' | 'range' = 'single', F extends ValueFormat = 'iso'> = {
|
|
19
19
|
/**
|
|
20
20
|
* Placeholder text displayed when no date is selected
|
|
@@ -52,11 +52,12 @@ export type DatePickerProps<V extends 'single' | 'range' = 'single', F extends V
|
|
|
52
52
|
readonly closeOnSelect?: boolean;
|
|
53
53
|
/**
|
|
54
54
|
* Determines the format of the value provided to the onChange callback
|
|
55
|
-
* - 'iso' (default): onChange receives ISO string(s) ('yyyy-MM-dd')
|
|
56
|
-
* - 'date': onChange receives JavaScript Date object(s)
|
|
55
|
+
* - 'iso' (default): onChange receives ISO string(s) ('yyyy-MM-dd') or empty string for cleared values
|
|
56
|
+
* - 'date': onChange receives JavaScript Date object(s) or null for cleared values
|
|
57
57
|
*
|
|
58
58
|
* Note: The component accepts both Date objects and ISO strings for value/initialValue
|
|
59
|
-
* regardless of this setting.
|
|
59
|
+
* regardless of this setting. Empty values are sent as null (date format) or '' (iso format)
|
|
60
|
+
* instead of undefined to work properly with React Hook Form.
|
|
60
61
|
* @default 'iso'
|
|
61
62
|
*/
|
|
62
63
|
readonly valueFormat?: F;
|
|
@@ -57,27 +57,34 @@ function DatePicker({
|
|
|
57
57
|
});
|
|
58
58
|
React.useEffect(() => {
|
|
59
59
|
if (variant === "single") {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
setInternalSingleDate(parsedValue);
|
|
63
|
-
}
|
|
60
|
+
const newDate = parseInputDate(valueProp);
|
|
61
|
+
setInternalSingleDate(newDate);
|
|
64
62
|
} else {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
setInternalDateRange(parsedValue);
|
|
68
|
-
}
|
|
63
|
+
const newRange = parseInputRange(valueProp);
|
|
64
|
+
setInternalDateRange(newRange);
|
|
69
65
|
}
|
|
70
66
|
}, [valueProp, variant]);
|
|
71
67
|
const singleDate = internalSingleDate;
|
|
72
68
|
const dateRange = internalDateRange;
|
|
69
|
+
const getEmptyValue = React.useCallback(() => {
|
|
70
|
+
if (variant === "single") {
|
|
71
|
+
return valueFormat === "date" ? null : "";
|
|
72
|
+
} else {
|
|
73
|
+
return valueFormat === "date" ? null : { from: "", to: "" };
|
|
74
|
+
}
|
|
75
|
+
}, [variant, valueFormat]);
|
|
73
76
|
const handleSelect = React.useCallback(
|
|
74
77
|
(selectedDate) => {
|
|
75
78
|
if (variant === "single") {
|
|
76
79
|
const date = selectedDate;
|
|
77
80
|
setInternalSingleDate(date);
|
|
78
81
|
if (onChange) {
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
if (date) {
|
|
83
|
+
const output = valueFormat === "date" ? date : formatOutputDate(date, "iso");
|
|
84
|
+
onChange(output);
|
|
85
|
+
} else {
|
|
86
|
+
onChange(getEmptyValue());
|
|
87
|
+
}
|
|
81
88
|
}
|
|
82
89
|
if (closeOnSelect) {
|
|
83
90
|
setIsOpen(false);
|
|
@@ -86,12 +93,16 @@ function DatePicker({
|
|
|
86
93
|
const range = selectedDate;
|
|
87
94
|
setInternalDateRange(range);
|
|
88
95
|
if (onChange) {
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
if (range) {
|
|
97
|
+
const output = valueFormat === "date" ? range : formatOutputRange(range, "iso");
|
|
98
|
+
onChange(output);
|
|
99
|
+
} else {
|
|
100
|
+
onChange(getEmptyValue());
|
|
101
|
+
}
|
|
91
102
|
}
|
|
92
103
|
}
|
|
93
104
|
},
|
|
94
|
-
[variant, valueFormat, onChange, closeOnSelect]
|
|
105
|
+
[variant, valueFormat, onChange, closeOnSelect, getEmptyValue]
|
|
95
106
|
);
|
|
96
107
|
const handleClear = React.useCallback(
|
|
97
108
|
(event) => {
|
|
@@ -99,22 +110,17 @@ function DatePicker({
|
|
|
99
110
|
event.stopPropagation();
|
|
100
111
|
if (variant === "single") {
|
|
101
112
|
setInternalSingleDate(void 0);
|
|
102
|
-
if (onChange) {
|
|
103
|
-
const output = valueFormat === "date" ? void 0 : formatOutputDate(void 0, "iso");
|
|
104
|
-
onChange(output);
|
|
105
|
-
}
|
|
106
113
|
} else {
|
|
107
114
|
setInternalDateRange(void 0);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
115
|
+
}
|
|
116
|
+
if (onChange) {
|
|
117
|
+
onChange(getEmptyValue());
|
|
112
118
|
}
|
|
113
119
|
if (typeof onClear === "function") {
|
|
114
120
|
onClear();
|
|
115
121
|
}
|
|
116
122
|
},
|
|
117
|
-
[variant,
|
|
123
|
+
[variant, onChange, onClear, getEmptyValue]
|
|
118
124
|
);
|
|
119
125
|
const formatForDisplay = () => {
|
|
120
126
|
const formatOptions = { locale: resolvedLocale };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.js","sources":["../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["'use client'\n\nimport { CalendarBlank, X } from '@phosphor-icons/react'\nimport { format as formatFn, Locale } from 'date-fns'\nimport { enUS, es, pt, enGB, de, it, fr } from 'date-fns/locale'\nimport * as React from 'react'\nimport { PropsBase, PropsRangeRequired, PropsSingleRequired } from 'react-day-picker'\n\nimport { Button, buttonVariants } from '../Button'\nimport { Calendar } from '../Calendar'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\n\nimport {\n formatOutputDate,\n formatOutputRange,\n parseInputDate,\n parseInputRange,\n InputDate,\n InputRange,\n ValueFormat,\n} from '@/lib/dateUtils'\nimport { cn } from '@/lib/utils'\n\ntype SupportedLocaleString = 'enUS' | 'es' | 'pt' | 'enGB' | 'de' | 'it' | 'fr'\n\nconst localeMap: Record<SupportedLocaleString, Locale> = {\n enUS,\n es,\n pt,\n enGB,\n de,\n it,\n fr,\n}\n\nexport type DateRange = {\n from?: Date\n to?: Date\n}\n\nexport type IsoDateRange = {\n from?: string\n to?: string\n}\n\ntype DatePickerOutput<Variant extends 'single' | 'range', Format extends ValueFormat> = Variant extends 'single'\n ? Format extends 'date'\n ? Date | undefined\n : string | undefined\n : Format extends 'date'\n ? DateRange | undefined\n : IsoDateRange | undefined\n\ntype DatePickerInput<V extends 'single' | 'range'> = V extends 'single'\n ? Date | string | undefined\n : { from?: Date | string; to?: Date | string } | undefined\n\nexport type DatePickerProps<V extends 'single' | 'range' = 'single', F extends ValueFormat = 'iso'> = {\n /**\n * Placeholder text displayed when no date is selected\n * @default 'Pick a date' for single mode, 'Pick a date range' for range mode\n */\n readonly placeholder?: string\n\n /**\n * Format string to use when displaying the selected date in the button\n * @default 'MMM d, yyyy'\n */\n readonly displayFormat?: string\n\n /**\n * ClassName for the button\n */\n readonly buttonClassName?: string\n\n /**\n * Whether to allow the user to show the year switcher menu\n * @default true for single mode, false for range mode\n */\n readonly showYearSwitcher?: boolean\n\n /**\n * Initial value for the date picker\n * Accepts both Date objects and ISO strings regardless of valueFormat setting\n */\n readonly initialValue?: DatePickerInput<V>\n\n /**\n * Current value for the date picker\n * Accepts both Date objects and ISO strings regardless of valueFormat setting\n */\n readonly value?: DatePickerInput<V>\n\n /**\n * Determines if the picker should close after a selection\n * @default true for single mode, false for range mode\n */\n readonly closeOnSelect?: boolean\n\n /**\n * Determines the format of the value provided to the onChange callback\n * - 'iso' (default): onChange receives ISO string(s) ('yyyy-MM-dd')\n * - 'date': onChange receives JavaScript Date object(s)\n *\n * Note: The component accepts both Date objects and ISO strings for value/initialValue\n * regardless of this setting.\n * @default 'iso'\n */\n readonly valueFormat?: F\n\n /**\n * Callback when date or date range changes\n */\n readonly onChange?: (value: DatePickerOutput<V, F>) => void\n\n /**\n * DatePicker mode - single date or date range\n * @default 'single'\n */\n readonly variant?: V\n\n /**\n * The locale to use for formatting dates and determining the start of the week.\n * Can be a string identifier for supported locales ('enUS', 'es', 'pt', 'enGB', 'de', 'it', 'fr')\n * or a Locale object from date-fns/locale for other languages.\n * @default 'enUS'\n */\n readonly locale?: SupportedLocaleString | Locale\n\n /**\n * Callback function executed when the clear button is clicked.\n * When provided, an X button will appear on hover to clear the selected date.\n */\n readonly onClear?: boolean | (() => void)\n} & Omit<PropsBase, 'mode' | 'selected' | 'onSelect' | 'locale'>\n\nfunction DatePicker<V extends 'single' | 'range' = 'single', F extends ValueFormat = 'iso'>({\n variant = 'single' as V,\n placeholder = variant === 'single' ? 'Pick a date' : 'Pick a date range',\n valueFormat = 'iso' as F,\n initialValue: initialValueProp,\n value: valueProp,\n onChange,\n buttonClassName,\n displayFormat = 'MMM d, yyyy',\n closeOnSelect = variant === 'single',\n showYearSwitcher = variant === 'single',\n locale: localeProp = 'enUS',\n defaultMonth,\n onClear,\n ...rest\n}: DatePickerProps<V, F>) {\n const [isOpen, setIsOpen] = React.useState(false)\n const [isHovered, setIsHovered] = React.useState(false)\n\n const resolvedLocale = React.useMemo(() => {\n if (typeof localeProp === 'string') {\n return localeMap[localeProp]\n }\n return localeProp\n }, [localeProp])\n\n const [internalSingleDate, setInternalSingleDate] = React.useState<Date | undefined>(() => {\n if (variant === 'single') {\n return parseInputDate(initialValueProp as InputDate)\n }\n return undefined\n })\n\n const [internalDateRange, setInternalDateRange] = React.useState<DateRange | undefined>(() => {\n if (variant === 'range') {\n return parseInputRange(initialValueProp as InputRange)\n }\n return undefined\n })\n\n React.useEffect(() => {\n if (variant === 'single') {\n const parsedValue = parseInputDate(valueProp as InputDate)\n if (parsedValue?.getTime() !== internalSingleDate?.getTime()) {\n setInternalSingleDate(parsedValue)\n }\n } else {\n const parsedValue = parseInputRange(valueProp as InputRange)\n if (\n parsedValue?.from?.getTime() !== internalDateRange?.from?.getTime() ||\n parsedValue?.to?.getTime() !== internalDateRange?.to?.getTime()\n ) {\n setInternalDateRange(parsedValue)\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [valueProp, variant])\n\n const singleDate = internalSingleDate\n const dateRange = internalDateRange\n\n const handleSelect = React.useCallback(\n (selectedDate: Date | DateRange | undefined) => {\n if (variant === 'single') {\n const date = selectedDate as Date | undefined\n setInternalSingleDate(date)\n if (onChange) {\n const output = valueFormat === 'date' ? date : formatOutputDate(date, 'iso')\n onChange(output as DatePickerOutput<V, F>)\n }\n if (closeOnSelect) {\n setIsOpen(false)\n }\n } else {\n const range = selectedDate as DateRange | undefined\n setInternalDateRange(range)\n if (onChange) {\n const output = valueFormat === 'date' ? range : formatOutputRange(range, 'iso')\n onChange(output as DatePickerOutput<V, F>)\n }\n }\n },\n [variant, valueFormat, onChange, closeOnSelect],\n )\n\n const handleClear = React.useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n\n if (variant === 'single') {\n setInternalSingleDate(undefined)\n if (onChange) {\n const output = valueFormat === 'date' ? undefined : formatOutputDate(undefined, 'iso')\n onChange(output as DatePickerOutput<V, F>)\n }\n } else {\n setInternalDateRange(undefined)\n if (onChange) {\n const output = valueFormat === 'date' ? undefined : formatOutputRange(undefined, 'iso')\n onChange(output as DatePickerOutput<V, F>)\n }\n }\n\n if (typeof onClear === 'function') {\n onClear()\n }\n },\n [variant, valueFormat, onChange, onClear],\n )\n\n const formatForDisplay = () => {\n const formatOptions = { locale: resolvedLocale }\n if (variant === 'single') {\n return singleDate ? formatFn(singleDate, displayFormat, formatOptions) : placeholder\n }\n\n if (!dateRange) return placeholder\n const fromStr = dateRange.from ? formatFn(dateRange.from, displayFormat, formatOptions) : '...'\n const toStr = dateRange.to ? formatFn(dateRange.to, displayFormat, formatOptions) : '...'\n if (!dateRange.from && !dateRange.to) return placeholder\n if (!dateRange.from) return `... - ${toStr}`\n if (!dateRange.to) return `${fromStr} - ...`\n return `${fromStr} - ${toStr}`\n }\n\n const calendarProps = React.useMemo(() => {\n const baseProps = {\n defaultMonth: (variant === 'single' ? singleDate : dateRange?.from) ?? defaultMonth,\n locale: resolvedLocale,\n initialFocus: true,\n ...rest,\n } as PropsBase\n\n if (variant === 'single') {\n return {\n ...baseProps,\n mode: 'single' as const,\n selected: singleDate,\n onSelect: (date: Date | undefined) => handleSelect(date),\n } as PropsSingleRequired\n }\n\n return {\n ...baseProps,\n mode: 'range' as const,\n selected: dateRange,\n onSelect: (range: DateRange | undefined) => handleSelect(range),\n numberOfMonths: rest.numberOfMonths ?? 2,\n } as PropsRangeRequired\n }, [variant, rest, singleDate, dateRange, handleSelect, resolvedLocale, defaultMonth])\n\n const hasValue = variant === 'single' ? singleDate : dateRange\n const showClearButton = onClear && hasValue && isHovered\n\n return (\n <PopoverRoot open={isOpen} onOpenChange={setIsOpen}>\n <PopoverTrigger asChild>\n <Button\n id={rest.id}\n variant=\"ghost\"\n className={cn(\n 'relative flex w-fit items-center justify-start text-left font-normal',\n !hasValue && 'text-muted-foreground',\n buttonVariants({ variant: 'input' }),\n buttonClassName,\n )}\n disabled={typeof rest.disabled === 'boolean' ? rest.disabled : false}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n <span className=\"w-full pr-7 text-center\">{formatForDisplay()}</span>\n <CalendarBlank\n className={cn(\n 'absolute right-4 h-4 w-4 shrink-0 transition-opacity duration-150',\n showClearButton ? 'opacity-0' : 'opacity-100',\n )}\n />\n {onClear && hasValue && (\n <X\n className={cn(\n 'absolute right-4 h-4 w-4 shrink-0 cursor-pointer transition-opacity duration-150',\n showClearButton ? 'opacity-100 hover:opacity-70' : 'opacity-0',\n )}\n onClick={handleClear}\n />\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"center\">\n <Calendar\n {...calendarProps}\n className=\"border-0\"\n showYearSwitcher={variant === 'single' ? showYearSwitcher : false}\n />\n </PopoverContent>\n </PopoverRoot>\n )\n}\n\nDatePicker.displayName = 'DatePicker'\n\nexport { DatePicker }\n"],"names":[],"mappings":";;;;;;;;;;;;AAyBA;AAAyD;AACvD;AACA;AACA;AACA;AACA;AACA;AAEF;AAuGA;AAA4F;AAChF;AAC2C;AACvC;AACA;AACP;AACP;AACA;AACgB;AACY;AACG;AACV;AACrB;AACA;AAEF;AACE;AACA;AAEA;AACE;AACE;AAA2B;AAE7B;AAAO;AAGT;AACE;AACE;AAAmD;AAErD;AAAO;AAGT;AACE;AACE;AAAqD;AAEvD;AAAO;AAGT;AACE;AACE;AACA;AACE;AAAiC;AACnC;AAEA;AACA;AAIE;AAAgC;AAClC;AACF;AAIF;AACA;AAEA;AAA2B;AAEvB;AACE;AACA;AACA;AACE;AACA;AAAyC;AAE3C;AACE;AAAe;AACjB;AAEA;AACA;AACA;AACE;AACA;AAAyC;AAC3C;AACF;AACF;AAC8C;AAGhD;AAA0B;AAEtB;AACA;AAEA;AACE;AACA;AACE;AACA;AAAyC;AAC3C;AAEA;AACA;AACE;AACA;AAAyC;AAC3C;AAGF;AACE;AAAQ;AACV;AACF;AACwC;AAG1C;AACE;AACA;AACE;AAAyE;AAG3E;AACA;AACA;AACA;AACA;AACA;AACA;AAA4B;AAG9B;AACE;AAAkB;AACuD;AAC/D;AACM;AACX;AAGL;AACE;AAAO;AACF;AACG;AACI;AAC6C;AACzD;AAGF;AAAO;AACF;AACG;AACI;AACoD;AACvB;AACzC;AAGF;AACA;AAEA;AAEI;AACE;AAAC;AAAA;AACU;AACD;AACG;AACT;AACa;AACsB;AACnC;AACF;AAC+D;AAC1B;AACC;AAEtC;AAA8D;AAC9D;AAAC;AAAA;AACY;AACT;AACgC;AAClC;AAAA;AACF;AAEE;AAAC;AAAA;AACY;AACT;AACmD;AACrD;AACS;AAAA;AACX;AAAA;AAAA;AAGN;AAEE;AAAC;AAAA;AACK;AACM;AACkD;AAAA;AAEhE;AAGN;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"DatePicker.js","sources":["../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["'use client'\n\nimport { CalendarBlank, X } from '@phosphor-icons/react'\nimport { format as formatFn, Locale } from 'date-fns'\nimport { enUS, es, pt, enGB, de, it, fr } from 'date-fns/locale'\nimport * as React from 'react'\nimport { PropsBase, PropsRangeRequired, PropsSingleRequired } from 'react-day-picker'\n\nimport { Button, buttonVariants } from '../Button'\nimport { Calendar } from '../Calendar'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\n\nimport {\n formatOutputDate,\n formatOutputRange,\n parseInputDate,\n parseInputRange,\n InputDate,\n InputRange,\n ValueFormat,\n} from '@/lib/dateUtils'\nimport { cn } from '@/lib/utils'\n\ntype SupportedLocaleString = 'enUS' | 'es' | 'pt' | 'enGB' | 'de' | 'it' | 'fr'\n\nconst localeMap: Record<SupportedLocaleString, Locale> = {\n enUS,\n es,\n pt,\n enGB,\n de,\n it,\n fr,\n}\n\nexport type DateRange = {\n from?: Date\n to?: Date\n}\n\nexport type IsoDateRange = {\n from?: string\n to?: string\n}\n\ntype DatePickerOutput<Variant extends 'single' | 'range', Format extends ValueFormat> = Variant extends 'single'\n ? Format extends 'date'\n ? Date | null\n : string | null\n : Format extends 'date'\n ? DateRange | null\n : IsoDateRange | null\n\ntype DatePickerInput<V extends 'single' | 'range'> = V extends 'single'\n ? Date | string | null\n : { from?: Date | string; to?: Date | string } | null\n\nexport type DatePickerProps<V extends 'single' | 'range' = 'single', F extends ValueFormat = 'iso'> = {\n /**\n * Placeholder text displayed when no date is selected\n * @default 'Pick a date' for single mode, 'Pick a date range' for range mode\n */\n readonly placeholder?: string\n\n /**\n * Format string to use when displaying the selected date in the button\n * @default 'MMM d, yyyy'\n */\n readonly displayFormat?: string\n\n /**\n * ClassName for the button\n */\n readonly buttonClassName?: string\n\n /**\n * Whether to allow the user to show the year switcher menu\n * @default true for single mode, false for range mode\n */\n readonly showYearSwitcher?: boolean\n\n /**\n * Initial value for the date picker\n * Accepts both Date objects and ISO strings regardless of valueFormat setting\n */\n readonly initialValue?: DatePickerInput<V>\n\n /**\n * Current value for the date picker\n * Accepts both Date objects and ISO strings regardless of valueFormat setting\n */\n readonly value?: DatePickerInput<V>\n\n /**\n * Determines if the picker should close after a selection\n * @default true for single mode, false for range mode\n */\n readonly closeOnSelect?: boolean\n\n /**\n * Determines the format of the value provided to the onChange callback\n * - 'iso' (default): onChange receives ISO string(s) ('yyyy-MM-dd') or empty string for cleared values\n * - 'date': onChange receives JavaScript Date object(s) or null for cleared values\n *\n * Note: The component accepts both Date objects and ISO strings for value/initialValue\n * regardless of this setting. Empty values are sent as null (date format) or '' (iso format)\n * instead of undefined to work properly with React Hook Form.\n * @default 'iso'\n */\n readonly valueFormat?: F\n\n /**\n * Callback when date or date range changes\n */\n readonly onChange?: (value: DatePickerOutput<V, F>) => void\n\n /**\n * DatePicker mode - single date or date range\n * @default 'single'\n */\n readonly variant?: V\n\n /**\n * The locale to use for formatting dates and determining the start of the week.\n * Can be a string identifier for supported locales ('enUS', 'es', 'pt', 'enGB', 'de', 'it', 'fr')\n * or a Locale object from date-fns/locale for other languages.\n * @default 'enUS'\n */\n readonly locale?: SupportedLocaleString | Locale\n\n /**\n * Callback function executed when the clear button is clicked.\n * When provided, an X button will appear on hover to clear the selected date.\n */\n readonly onClear?: boolean | (() => void)\n} & Omit<PropsBase, 'mode' | 'selected' | 'onSelect' | 'locale'>\n\nfunction DatePicker<V extends 'single' | 'range' = 'single', F extends ValueFormat = 'iso'>({\n variant = 'single' as V,\n placeholder = variant === 'single' ? 'Pick a date' : 'Pick a date range',\n valueFormat = 'iso' as F,\n initialValue: initialValueProp,\n value: valueProp,\n onChange,\n buttonClassName,\n displayFormat = 'MMM d, yyyy',\n closeOnSelect = variant === 'single',\n showYearSwitcher = variant === 'single',\n locale: localeProp = 'enUS',\n defaultMonth,\n onClear,\n ...rest\n}: DatePickerProps<V, F>) {\n const [isOpen, setIsOpen] = React.useState(false)\n const [isHovered, setIsHovered] = React.useState(false)\n\n const resolvedLocale = React.useMemo(() => {\n if (typeof localeProp === 'string') {\n return localeMap[localeProp]\n }\n return localeProp\n }, [localeProp])\n\n const [internalSingleDate, setInternalSingleDate] = React.useState<Date | undefined>(() => {\n if (variant === 'single') {\n return parseInputDate(initialValueProp as InputDate)\n }\n return undefined\n })\n\n const [internalDateRange, setInternalDateRange] = React.useState<DateRange | undefined>(() => {\n if (variant === 'range') {\n return parseInputRange(initialValueProp as InputRange)\n }\n return undefined\n })\n\n React.useEffect(() => {\n if (variant === 'single') {\n const newDate = parseInputDate(valueProp as InputDate)\n setInternalSingleDate(newDate)\n } else {\n const newRange = parseInputRange(valueProp as InputRange)\n setInternalDateRange(newRange)\n }\n }, [valueProp, variant])\n\n const singleDate = internalSingleDate\n const dateRange = internalDateRange\n\n const getEmptyValue = React.useCallback((): DatePickerOutput<V, F> => {\n if (variant === 'single') {\n return (valueFormat === 'date' ? null : '') as DatePickerOutput<V, F>\n } else {\n return (valueFormat === 'date' ? null : { from: '', to: '' }) as DatePickerOutput<V, F>\n }\n }, [variant, valueFormat])\n\n const handleSelect = React.useCallback(\n (selectedDate: Date | DateRange | undefined) => {\n if (variant === 'single') {\n const date = selectedDate as Date | undefined\n\n setInternalSingleDate(date)\n\n if (onChange) {\n if (date) {\n const output = valueFormat === 'date' ? date : formatOutputDate(date, 'iso')\n onChange(output as DatePickerOutput<V, F>)\n } else {\n onChange(getEmptyValue())\n }\n }\n\n if (closeOnSelect) {\n setIsOpen(false)\n }\n } else {\n const range = selectedDate as DateRange | undefined\n setInternalDateRange(range)\n if (onChange) {\n if (range) {\n const output = valueFormat === 'date' ? range : formatOutputRange(range, 'iso')\n onChange(output as DatePickerOutput<V, F>)\n } else {\n onChange(getEmptyValue())\n }\n }\n }\n },\n [variant, valueFormat, onChange, closeOnSelect, getEmptyValue],\n )\n\n const handleClear = React.useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n\n if (variant === 'single') {\n setInternalSingleDate(undefined)\n } else {\n setInternalDateRange(undefined)\n }\n\n if (onChange) {\n onChange(getEmptyValue())\n }\n\n if (typeof onClear === 'function') {\n onClear()\n }\n },\n [variant, onChange, onClear, getEmptyValue],\n )\n\n const formatForDisplay = () => {\n const formatOptions = { locale: resolvedLocale }\n if (variant === 'single') {\n return singleDate ? formatFn(singleDate, displayFormat, formatOptions) : placeholder\n }\n\n if (!dateRange) return placeholder\n const fromStr = dateRange.from ? formatFn(dateRange.from, displayFormat, formatOptions) : '...'\n const toStr = dateRange.to ? formatFn(dateRange.to, displayFormat, formatOptions) : '...'\n if (!dateRange.from && !dateRange.to) return placeholder\n if (!dateRange.from) return `... - ${toStr}`\n if (!dateRange.to) return `${fromStr} - ...`\n return `${fromStr} - ${toStr}`\n }\n\n const calendarProps = React.useMemo(() => {\n const baseProps = {\n defaultMonth: (variant === 'single' ? singleDate : dateRange?.from) ?? defaultMonth,\n locale: resolvedLocale,\n initialFocus: true,\n ...rest,\n } as PropsBase\n\n if (variant === 'single') {\n return {\n ...baseProps,\n mode: 'single' as const,\n selected: singleDate,\n onSelect: (date: Date | undefined) => handleSelect(date),\n } as PropsSingleRequired\n }\n\n return {\n ...baseProps,\n mode: 'range' as const,\n selected: dateRange,\n onSelect: (range: DateRange | undefined) => handleSelect(range),\n numberOfMonths: rest.numberOfMonths ?? 2,\n } as PropsRangeRequired\n }, [variant, rest, singleDate, dateRange, handleSelect, resolvedLocale, defaultMonth])\n\n const hasValue = variant === 'single' ? singleDate : dateRange\n const showClearButton = onClear && hasValue && isHovered\n\n return (\n <PopoverRoot open={isOpen} onOpenChange={setIsOpen}>\n <PopoverTrigger asChild>\n <Button\n id={rest.id}\n variant=\"ghost\"\n className={cn(\n 'relative flex w-fit items-center justify-start text-left font-normal',\n !hasValue && 'text-muted-foreground',\n buttonVariants({ variant: 'input' }),\n buttonClassName,\n )}\n disabled={typeof rest.disabled === 'boolean' ? rest.disabled : false}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n <span className=\"w-full pr-7 text-center\">{formatForDisplay()}</span>\n <CalendarBlank\n className={cn(\n 'absolute right-4 h-4 w-4 shrink-0 transition-opacity duration-150',\n showClearButton ? 'opacity-0' : 'opacity-100',\n )}\n />\n {onClear && hasValue && (\n <X\n className={cn(\n 'absolute right-4 h-4 w-4 shrink-0 cursor-pointer transition-opacity duration-150',\n showClearButton ? 'opacity-100 hover:opacity-70' : 'opacity-0',\n )}\n onClick={handleClear}\n />\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"center\">\n <Calendar\n {...calendarProps}\n className=\"border-0\"\n showYearSwitcher={variant === 'single' ? showYearSwitcher : false}\n />\n </PopoverContent>\n </PopoverRoot>\n )\n}\n\nDatePicker.displayName = 'DatePicker'\n\nexport { DatePicker }\n"],"names":[],"mappings":";;;;;;;;;;;;AAyBA;AAAyD;AACvD;AACA;AACA;AACA;AACA;AACA;AAEF;AAwGA;AAA4F;AAChF;AAC2C;AACvC;AACA;AACP;AACP;AACA;AACgB;AACY;AACG;AACV;AACrB;AACA;AAEF;AACE;AACA;AAEA;AACE;AACE;AAA2B;AAE7B;AAAO;AAGT;AACE;AACE;AAAmD;AAErD;AAAO;AAGT;AACE;AACE;AAAqD;AAEvD;AAAO;AAGT;AACE;AACE;AACA;AAA6B;AAE7B;AACA;AAA6B;AAC/B;AAGF;AACA;AAEA;AACE;AACE;AAAwC;AAExC;AAA2D;AAC7D;AAGF;AAA2B;AAEvB;AACE;AAEA;AAEA;AACE;AACE;AACA;AAAyC;AAEzC;AAAwB;AAC1B;AAGF;AACE;AAAe;AACjB;AAEA;AACA;AACA;AACE;AACE;AACA;AAAyC;AAEzC;AAAwB;AAC1B;AACF;AACF;AACF;AAC6D;AAG/D;AAA0B;AAEtB;AACA;AAEA;AACE;AAA+B;AAE/B;AAA8B;AAGhC;AACE;AAAwB;AAG1B;AACE;AAAQ;AACV;AACF;AAC0C;AAG5C;AACE;AACA;AACE;AAAyE;AAG3E;AACA;AACA;AACA;AACA;AACA;AACA;AAA4B;AAG9B;AACE;AAAkB;AACuD;AAC/D;AACM;AACX;AAGL;AACE;AAAO;AACF;AACG;AACI;AAC6C;AACzD;AAGF;AAAO;AACF;AACG;AACI;AACoD;AACvB;AACzC;AAGF;AACA;AAEA;AAEI;AACE;AAAC;AAAA;AACU;AACD;AACG;AACT;AACa;AACsB;AACnC;AACF;AAC+D;AAC1B;AACC;AAEtC;AAA8D;AAC9D;AAAC;AAAA;AACY;AACT;AACgC;AAClC;AAAA;AACF;AAEE;AAAC;AAAA;AACY;AACT;AACmD;AACrD;AACS;AAAA;AACX;AAAA;AAAA;AAGN;AAEE;AAAC;AAAA;AACK;AACM;AACkD;AAAA;AAEhE;AAGN;AAEA;;"}
|