@codecademy/gamut
68.2.268.2.3-alpha.794432.0
dist/DatePicker/DatePicker.js+
dist/DatePicker/DatePicker.jsNew file+155
Index: package/dist/DatePicker/DatePicker.js
===================================================================
--- package/dist/DatePicker/DatePicker.js
+++ package/dist/DatePicker/DatePicker.js
@@ -0,0 +1,155 @@
+import { MiniArrowRightIcon } from '@codecademy/gamut-icons';
+import { useCallback, useId, useMemo, useRef, useState } from 'react';
+import { Box, FlexBox } from '../Box';
+import { PopoverContainer } from '../PopoverContainer';
+import { DatePickerCalendar } from './DatePickerCalendar';
+import { DatePickerProvider } from './DatePickerContext';
+import { DatePickerInput } from './DatePickerInput';
+import { isRangeProps } from './utils/dateSelect';
+import { useResolvedLocale } from './utils/locale';
+import { DEFAULT_DATE_PICKER_TRANSLATIONS } from './utils/translations';
+
+/**
+ * DatePicker: single-date or range. Holds shared state and provides it via context.
+ * Single: selectedDate, setSelectedDate. Range: startDate, endDate, setStartDate, setEndDate.
+ * With no children, renders default layout (input + calendar popover).
+ */
+import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
+export const DatePicker = props => {
+ const {
+ locale,
+ disabledDates = [],
+ placeholder,
+ mode,
+ children,
+ translations: translationsProp,
+ inputSize
+ } = props;
+ const [isCalendarOpen, setIsCalendarOpen] = useState(false);
+ const [focusGridSignal, setFocusGridSignal] = useState(false);
+ const [gridFocusRequested, setGridFocusRequested] = useState(false);
+ const [activeRangePart, setActiveRangePart] = useState(null);
+ const inputRef = useRef(null);
+ const dialogId = useId();
+ const calendarDialogId = `datepicker-dialog-${dialogId.replace(/:/g, '')}`;
+ const clearGridFocusRequest = useCallback(() => {
+ setGridFocusRequested(false);
+ }, []);
+ const resolvedLocale = useResolvedLocale(locale);
+ const openCalendar = useCallback(options => {
+ const moveFocus = options?.moveFocusIntoCalendar ?? false;
+ setIsCalendarOpen(true);
+ if (moveFocus) {
+ setGridFocusRequested(true);
+ setFocusGridSignal(signal => !signal);
+ } else {
+ setGridFocusRequested(false);
+ }
+ }, []);
+ const focusCalendarGrid = useCallback(() => {
+ setGridFocusRequested(true);
+ setFocusGridSignal(signal => !signal);
+ }, []);
+ const closeCalendar = useCallback(() => {
+ setIsCalendarOpen(false);
+ setActiveRangePart(null);
+ setGridFocusRequested(false);
+ inputRef.current?.focus();
+ }, []);
+ const startOrSelectedDate = isRangeProps(props) ? props.startDate : props.selectedDate;
+ const endDate = isRangeProps(props) ? props.endDate : null;
+ const setSelection = useCallback((start, end) => {
+ if (isRangeProps(props)) {
+ props.setStartDate(start);
+ props.setEndDate(end ?? null);
+ } else {
+ props.setSelectedDate(start);
+ }
+ }, [props]);
+ const contextValue = useMemo(() => {
+ const translations = {
+ ...DEFAULT_DATE_PICKER_TRANSLATIONS,
+ ...translationsProp
+ };
+ const base = {
+ startOrSelectedDate,
+ setSelection,
+ isCalendarOpen,
+ openCalendar,
+ focusCalendarGrid,
+ focusGridSignal,
+ gridFocusRequested,
+ clearGridFocusRequest,
+ closeCalendar,
+ locale: resolvedLocale,
+ disabledDates,
+ calendarDialogId,
+ translations
+ };
+ return mode === 'range' ? {
+ ...base,
+ mode: 'range',
+ endDate,
+ activeRangePart,
+ setActiveRangePart
+ } : {
+ ...base,
+ mode: 'single'
+ };
+ }, [mode, startOrSelectedDate, endDate, setSelection, activeRangePart, setActiveRangePart, isCalendarOpen, openCalendar, focusCalendarGrid, focusGridSignal, gridFocusRequested, clearGridFocusRequest, closeCalendar, resolvedLocale, disabledDates, calendarDialogId, translationsProp]);
+ const content = children !== undefined ? children : /*#__PURE__*/_jsxs(_Fragment, {
+ children: [/*#__PURE__*/_jsx(FlexBox, {
+ gap: inputSize === 'small' ? 4 : 8,
+ width: "fit-content",
+ children: mode === 'range' ? /*#__PURE__*/_jsxs(_Fragment, {
+ children: [/*#__PURE__*/_jsx(DatePickerInput, {
+ label: props.startLabel,
+ placeholder: placeholder,
+ rangePart: "start",
+ ref: inputRef,
+ size: inputSize
+ }), /*#__PURE__*/_jsx(Box, {
+ alignSelf: "center",
+ mt: 32,
+ children: /*#__PURE__*/_jsx(MiniArrowRightIcon, {})
+ }), /*#__PURE__*/_jsx(DatePickerInput, {
+ label: props.endLabel,
+ placeholder: placeholder,
+ rangePart: "end",
+ size: inputSize
+ // does this need a ref?
+ })]
+ }) : /*#__PURE__*/_jsx(DatePickerInput, {
+ label: props.label,
+ placeholder: placeholder,
+ ref: inputRef,
+ size: inputSize
+ })
+ }), /*#__PURE__*/_jsx(PopoverContainer, {
+ alignment: "bottom-left",
+ allowPageInteraction: true,
+ focusOnProps: {
+ autoFocus: false,
+ focusLock: false
+ },
+ invertAxis: "x",
+ isOpen: isCalendarOpen,
+ targetRef: inputRef,
+ x: -20,
+ y: -16,
+ onRequestClose: closeCalendar,
+ children: /*#__PURE__*/_jsx("div", {
+ "aria-label": contextValue.translations.calendarDialogAriaLabel,
+ id: calendarDialogId,
+ role: "dialog",
+ children: /*#__PURE__*/_jsx(DatePickerCalendar, {
+ dialogId: calendarDialogId
+ })
+ })
+ })]
+ });
+ return /*#__PURE__*/_jsx(DatePickerProvider, {
+ value: contextValue,
+ children: content
+ });
+};
\ No newline at end of file