UNPKG

flowbite-react

Version:

Official React components built for Flowbite and Tailwind CSS

273 lines (270 loc) 10.1 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { forwardRef, useMemo, useState, useRef, useImperativeHandle, useEffect } from 'react'; import { get } from '../../helpers/get.js'; import { resolveProps } from '../../helpers/resolve-props.js'; import { useResolveTheme } from '../../helpers/resolve-theme.js'; import { twMerge } from '../../helpers/tailwind-merge.js'; import { ArrowLeftIcon } from '../../icons/arrow-left-icon.js'; import { ArrowRightIcon } from '../../icons/arrow-right-icon.js'; import { CalendarIcon } from '../../icons/calendar-icon.js'; import { useThemeProvider } from '../../theme/provider.js'; import { TextInput } from '../TextInput/TextInput.js'; import { DatepickerContext } from './DatepickerContext.js'; import { WeekStart, getFirstDateInRange, Views, isDateEqual, getFormattedDate, addYears, addMonths, startOfYearPeriod } from './helpers.js'; import { datePickerTheme } from './theme.js'; import { DatepickerViewsDays } from './Views/Days.js'; import { DatepickerViewsDecades } from './Views/Decades.js'; import { DatepickerViewsMonth } from './Views/Months.js'; import { DatepickerViewsYears } from './Views/Years.js'; const Datepicker = forwardRef((props, ref) => { const provider = useThemeProvider(); const theme = useResolveTheme( [datePickerTheme, provider.theme?.datepicker, props.theme], [get(provider.clearTheme, "datepicker"), props.clearTheme], [get(provider.applyTheme, "datepicker"), props.applyTheme] ); const { title, open, inline = false, autoHide = true, // Hide when selected the day showClearButton = true, labelClearButton = "Clear", showTodayButton = true, labelTodayButton = "Today", defaultValue, minDate, maxDate, language = "en", weekStart = WeekStart.Sunday, className, onChange, label, value, ...restProps } = resolveProps(props, provider.props?.datepicker); const initialDate = defaultValue ? getFirstDateInRange(defaultValue, minDate, maxDate) : null; const effectiveDefaultView = useMemo(() => { return defaultValue ? getFirstDateInRange(defaultValue, minDate, maxDate) : /* @__PURE__ */ new Date(); }, []); const [isOpen, setIsOpen] = useState(open); const [view, setView] = useState(Views.Days); const [selectedDate, setSelectedDate] = useState(value ?? initialDate); const [viewDate, setViewDate] = useState(value ?? effectiveDefaultView); const inputRef = useRef(null); const datepickerRef = useRef(null); function changeSelectedDate(date, useAutohide) { setSelectedDate(date); if ((date === null || date) && onChange) { onChange(date); } if (autoHide && view === Views.Days && useAutohide == true && !inline) { setIsOpen(false); } } function clearDate() { changeSelectedDate(initialDate, true); if (defaultValue) { setViewDate(defaultValue); } } useImperativeHandle(ref, () => ({ focus() { inputRef.current?.focus(); }, clear() { clearDate(); } })); function renderView(type) { switch (type) { case Views.Decades: return /* @__PURE__ */ jsx(DatepickerViewsDecades, {}); case Views.Years: return /* @__PURE__ */ jsx(DatepickerViewsYears, {}); case Views.Months: return /* @__PURE__ */ jsx(DatepickerViewsMonth, {}); case Views.Days: default: return /* @__PURE__ */ jsx(DatepickerViewsDays, {}); } } function getNextView() { switch (view) { case Views.Days: return Views.Months; case Views.Months: return Views.Years; case Views.Years: return Views.Decades; } return view; } function getViewTitle() { switch (view) { case Views.Decades: return `${startOfYearPeriod(viewDate, 100) - 10} - ${startOfYearPeriod(viewDate, 100) + 100}`; case Views.Years: return `${startOfYearPeriod(viewDate, 10)} - ${startOfYearPeriod(viewDate, 10) + 11}`; case Views.Months: return getFormattedDate(language, viewDate, { year: "numeric" }); case Views.Days: default: return getFormattedDate(language, viewDate, { month: "long", year: "numeric" }); } } function getViewDatePage(view2, date, value2) { switch (view2) { case Views.Days: return new Date(addMonths(date, value2)); case Views.Months: return new Date(addYears(date, value2)); case Views.Years: return new Date(addYears(date, value2 * 10)); case Views.Decades: return new Date(addYears(date, value2 * 100)); default: return new Date(addYears(date, value2 * 10)); } } useEffect(() => { const handleClickOutside = (event) => { const clickedInsideDatepicker = datepickerRef?.current?.contains(event.target); const clickedInsideInput = inputRef?.current?.contains(event.target); if (!clickedInsideDatepicker && !clickedInsideInput) { setIsOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [inputRef, datepickerRef, setIsOpen]); useEffect(() => { const effectiveValue = value && getFirstDateInRange(new Date(value), minDate, maxDate); const effectiveSelectedDate = selectedDate && getFirstDateInRange(new Date(selectedDate), minDate, maxDate); if (effectiveSelectedDate && effectiveValue && !isDateEqual(effectiveValue, effectiveSelectedDate)) { setSelectedDate(effectiveValue); } if (selectedDate == null) { setSelectedDate(initialDate); } }, [value, setSelectedDate, setViewDate, selectedDate]); const displayValue = value === null ? label : getFormattedDate(language, selectedDate || /* @__PURE__ */ new Date()); return /* @__PURE__ */ jsx( DatepickerContext.Provider, { value: { theme, language, minDate, maxDate, weekStart, isOpen, setIsOpen, view, setView, viewDate, setViewDate, selectedDate, setSelectedDate, changeSelectedDate }, children: /* @__PURE__ */ jsxs("div", { className: twMerge(theme.root.base, className), children: [ !inline && /* @__PURE__ */ jsx( TextInput, { theme: theme.root.input, icon: CalendarIcon, ref: inputRef, onFocus: () => { if (selectedDate && !isDateEqual(viewDate, selectedDate)) { setViewDate(selectedDate); } setIsOpen(true); }, value: displayValue, defaultValue: initialDate ? getFormattedDate(language, initialDate) : label, readOnly: true, ...restProps } ), (isOpen || inline) && /* @__PURE__ */ jsx("div", { ref: datepickerRef, className: twMerge(theme.popup.root.base, inline && theme.popup.root.inline), children: /* @__PURE__ */ jsxs("div", { className: theme.popup.root.inner, children: [ /* @__PURE__ */ jsxs("div", { className: theme.popup.header.base, children: [ title && /* @__PURE__ */ jsx("div", { className: theme.popup.header.title, children: title }), /* @__PURE__ */ jsxs("div", { className: theme.popup.header.selectors.base, children: [ /* @__PURE__ */ jsx( "button", { type: "button", className: twMerge( theme.popup.header.selectors.button.base, theme.popup.header.selectors.button.prev ), onClick: () => setViewDate(getViewDatePage(view, viewDate, -1)), children: /* @__PURE__ */ jsx(ArrowLeftIcon, {}) } ), /* @__PURE__ */ jsx( "button", { type: "button", className: twMerge( theme.popup.header.selectors.button.base, theme.popup.header.selectors.button.view ), onClick: () => setView(getNextView()), children: getViewTitle() } ), /* @__PURE__ */ jsx( "button", { type: "button", className: twMerge( theme.popup.header.selectors.button.base, theme.popup.header.selectors.button.next ), onClick: () => setViewDate(getViewDatePage(view, viewDate, 1)), children: /* @__PURE__ */ jsx(ArrowRightIcon, {}) } ) ] }) ] }), /* @__PURE__ */ jsx("div", { className: theme.popup.view.base, children: renderView(view) }), (showClearButton || showTodayButton) && /* @__PURE__ */ jsxs("div", { className: theme.popup.footer.base, children: [ showTodayButton && /* @__PURE__ */ jsx( "button", { type: "button", className: twMerge(theme.popup.footer.button.base, theme.popup.footer.button.today), onClick: () => { const today = /* @__PURE__ */ new Date(); changeSelectedDate(today, true); setViewDate(today); }, children: labelTodayButton } ), showClearButton && /* @__PURE__ */ jsx( "button", { type: "button", className: twMerge(theme.popup.footer.button.base, theme.popup.footer.button.clear), onClick: () => { changeSelectedDate(null, true); }, children: labelClearButton } ) ] }) ] }) }) ] }) } ); }); Datepicker.displayName = "Datepicker"; export { Datepicker }; //# sourceMappingURL=Datepicker.js.map