@codecademy/gamut
68.6.268.6.3-alpha.92d8ae.0
dist/DatePicker/DatePickerInput/index.js~
dist/DatePicker/DatePickerInput/index.jsModified+64−191
Index: package/dist/DatePicker/DatePickerInput/index.js
===================================================================
--- package/dist/DatePicker/DatePickerInput/index.js
+++ package/dist/DatePicker/DatePickerInput/index.js
@@ -1,215 +1,88 @@
-import { MiniCalendarIcon } from '@codecademy/gamut-icons';
-import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
-import { FlexBox } from '../../Box';
+import { forwardRef, useId } from 'react';
+import { Box, FlexBox } from '../../Box';
import { FormGroup } from '../../Form/elements/FormGroup';
-import { isSameDay } from '../DatePickerCalendar/Calendar/utils/dateGrid';
-import { handleDateSelectRange } from '../DatePickerCalendar/utils/dateSelect';
+import { FormGroupLabel } from '../../Form/elements/FormGroupLabel';
+import { DATE_PICKER_FIELD_WIDTH } from '../constants';
import { useDatePicker } from '../DatePickerContext';
-import { SegmentedShell } from './elements';
-import { DatePickerInputSegment } from './Segment';
-import { SegmentLiteral } from './Segment/elements';
-import { getDateSegmentsFromDate, normalizeSegmentValues, parseSegmentsToDate } from './Segment/utils';
-import { formatDateISO8601DateOnly, getDateFieldOrder, getDateFormatLayout } from './utils';
+import { createDatePickerFieldIds, createDatePickerShellId } from '../utils/fieldIds';
+import { DatePickerInputShell } from './DatePickerInputShell';
+import { DatePickerDescription } from './elements';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
+export { DatePickerDescription } from './elements';
export const DatePickerInput = /*#__PURE__*/forwardRef(({
disabled,
error,
form,
label,
name,
rangePart,
size = 'base',
+ description,
...rest
}, ref) => {
const context = useDatePicker();
if (context === null) {
throw new Error('DatePickerInput must be used inside a DatePicker (it reads shared state from context).');
}
const {
- mode,
- openCalendar,
- focusCalendar,
- locale,
- isCalendarOpen,
- translations,
- disableDate
+ translations
} = context;
- const isRange = mode === 'range';
- const endDate = isRange ? context.endDate : null;
- const date = isRange ? context.startDate : context.selectedDate;
- const inputID = useId();
- const inputId = `datepicker-input-${inputID.replace(/:/g, '')}`;
- const {
- layout,
- fieldOrder
- } = useMemo(() => {
- const layout = getDateFormatLayout(locale);
- return {
- layout,
- fieldOrder: getDateFieldOrder(layout)
- };
- }, [locale]);
- const defaultLabel = !isRange ? translations.dateLabel : rangePart === 'end' ? translations.endDateLabel : translations.startDateLabel;
- const boundDate = isRange && rangePart === 'end' ? endDate : date;
- const segmentsFromBound = useMemo(() => getDateSegmentsFromDate(boundDate), [boundDate]);
- const [segments, setSegments] = useState(segmentsFromBound);
- const parsedForHidden = parseSegmentsToDate(segments);
- const hiddenValue = parsedForHidden ? formatDateISO8601DateOnly(parsedForHidden) : '';
- const isInputFocusedRef = useRef(false);
- const containerRef = useRef(null);
- const segmentElRefs = useRef({});
- const assignSegmentRef = useCallback((field, el) => {
- segmentElRefs.current[field] = el;
- }, [segmentElRefs]);
- const onSiblingSegmentFocus = useCallback(field => {
- segmentElRefs.current[field]?.focus();
- }, [segmentElRefs]);
- const shellRef = useCallback(el => {
- containerRef.current = el;
- if (typeof ref === 'function') ref(el);else if (ref !== null) ref.current = el;
- }, [ref]);
- useEffect(() => {
- if (!isInputFocusedRef.current) {
- setSegments(segmentsFromBound);
- }
- }, [segmentsFromBound]);
- const commitParsedDate = useCallback(parsed => {
- if (!isRange) {
- context.onSelection(parsed);
- }
- if (isRange && rangePart) {
- handleDateSelectRange({
- date: parsed,
- activeRangePart: rangePart,
- startDate: date,
- endDate,
- onRangeSelection: context.onRangeSelection,
- disableDate
- });
- }
- }, [isRange, rangePart, context, endDate, date, disableDate]);
- const clearSelection = useCallback(() => {
- if (!isRange) {
- context.onSelection(null);
- }
- if (isRange && rangePart) {
- if (rangePart === 'start') context.onRangeSelection(null, endDate);else context.onRangeSelection(date, null);
- }
- }, [isRange, rangePart, context, endDate, date]);
- const onSegmentChange = useCallback(next => {
- const parsed = parseSegmentsToDate(next);
- if (parsed) commitParsedDate(parsed);else if (!next.month && !next.day && !next.year) clearSelection();
- }, [clearSelection, commitParsedDate]);
- const onContainerBlur = useCallback(e => {
- if (containerRef.current?.contains(e.relatedTarget)) return;
- isInputFocusedRef.current = false;
- setSegments(prev => {
- const normalized = normalizeSegmentValues(prev);
- const parsed = parseSegmentsToDate(normalized);
- if (parsed) {
- const sameAsBound = isSameDay(parsed, boundDate);
- if (isCalendarOpen && !sameAsBound) {
- queueMicrotask(() => {
- commitParsedDate(parsed);
- });
- }
- return normalized;
- }
- if (!normalized.month && !normalized.day && !normalized.year) {
- queueMicrotask(() => {
- clearSelection();
- });
- return getDateSegmentsFromDate(null);
- }
- return segmentsFromBound;
+ const labelUid = useId();
+ const inputUid = useId();
+ const shellProps = {
+ disabled,
+ error,
+ form,
+ size,
+ ...rest
+ };
+ if (rangePart) {
+ const shellId = createDatePickerShellId(inputUid);
+ const defaultLabel = rangePart === 'end' ? translations.endDateLabel : translations.startDateLabel;
+ return /*#__PURE__*/_jsxs(FormGroup, {
+ alignItems: "flex-start",
+ id: shellId,
+ isSoloField: true,
+ label: label ?? defaultLabel,
+ mb: 0,
+ pb: 0,
+ spacing: "tight",
+ width: "fit-content",
+ children: [description ? /*#__PURE__*/_jsx(DatePickerDescription, {
+ "aria-live": "assertive",
+ children: description
+ }) : null, /*#__PURE__*/_jsx(DatePickerInputShell, {
+ ...shellProps,
+ labelledById: shellId,
+ name: name,
+ rangePart: rangePart,
+ ref: ref,
+ shellId: shellId
+ })]
});
- }, [containerRef, boundDate, segmentsFromBound, clearSelection, commitParsedDate, isCalendarOpen]);
- const setActiveRangePartForField = useCallback(() => {
- if (isRange && rangePart) context.setActiveRangePart(rangePart);
- }, [isRange, rangePart, context]);
- const onSegmentFocus = useCallback(() => {
- isInputFocusedRef.current = true;
- setActiveRangePartForField();
- }, [isInputFocusedRef, setActiveRangePartForField]);
- const onShellFocus = useCallback(() => {
- setActiveRangePartForField();
- }, [setActiveRangePartForField]);
- const onShellClick = useCallback(() => {
- if (disabled) return;
- setActiveRangePartForField();
- openCalendar();
- }, [disabled, setActiveRangePartForField, openCalendar]);
- const onSegmentAltArrowDown = useCallback(() => {
- if (!isCalendarOpen) openCalendar();
- focusCalendar();
- }, [isCalendarOpen, openCalendar, focusCalendar]);
- return /*#__PURE__*/_jsx(FormGroup, {
- htmlFor: inputId,
- isSoloField: true,
- label: label ?? defaultLabel,
- mb: 0,
- pb: 0,
- spacing: "tight",
+ }
+ const fieldIds = createDatePickerFieldIds(inputUid, labelUid);
+ return /*#__PURE__*/_jsxs(Box, {
width: "fit-content",
- children: /*#__PURE__*/_jsxs(SegmentedShell, {
- id: inputId,
- inputSize: size,
- ref: shellRef,
- role: "group",
- variant: error ? 'error' : 'default',
- width: "113px",
- onBlur: onContainerBlur,
- onClick: onShellClick,
- onFocus: onShellFocus,
- ...rest,
- children: [/*#__PURE__*/_jsx(FlexBox, {
- alignItems: "center",
- justifyContent: "center",
- children: layout.map((item, index) => {
- if (item.kind === 'literal') {
- return /*#__PURE__*/_jsx(SegmentLiteral, {
- "aria-hidden": true
- // eslint-disable-next-line react/no-array-index-key
- ,
- children: `${item.text}`
- }, `literal-${item.text}-${index}`);
- }
- const idx = fieldOrder.indexOf(item.field);
- const prevField = idx > 0 ? fieldOrder[idx - 1] : null;
- const nextField = idx < fieldOrder.length - 1 ? fieldOrder[idx + 1] : null;
- return /*#__PURE__*/_jsx(DatePickerInputSegment, {
- applySegments: onSegmentChange,
- assignSegmentRef: assignSegmentRef,
- disabled: !!disabled,
- error: !!error,
- field: item.field,
- nextField: nextField,
- prevField: prevField,
- segments: segments,
- setSegments: setSegments,
- onAltArrowDown: onSegmentAltArrowDown,
- onFocus: onSegmentFocus,
- onSiblingFocus: onSiblingSegmentFocus
- }, item.field);
- })
- }), /*#__PURE__*/_jsx("input", {
- "aria-hidden": true,
- form: form,
+ children: [/*#__PURE__*/_jsx(Box, {
+ id: fieldIds.labelledById,
+ width: DATE_PICKER_FIELD_WIDTH,
+ children: /*#__PURE__*/_jsx(FormGroupLabel, {
+ htmlFor: fieldIds.shellId,
+ isSoloField: true,
+ children: label ?? translations.dateLabel
+ })
+ }), description ? /*#__PURE__*/_jsx(DatePickerDescription, {
+ "aria-live": "assertive",
+ children: description
+ }) : null, /*#__PURE__*/_jsx(FlexBox, {
+ ref: ref,
+ children: /*#__PURE__*/_jsx(DatePickerInputShell, {
+ ...shellProps,
+ labelledById: fieldIds.labelledById,
name: name ?? 'datePickerInput',
- tabIndex: -1,
- type: "hidden",
- value: hiddenValue
- }), /*#__PURE__*/_jsx(FlexBox, {
- alignItems: "center",
- justifyContent: "center",
- pl: 16,
- pr: 8,
- role: "presentation",
- children: /*#__PURE__*/_jsx(MiniCalendarIcon, {
- "aria-hidden": true,
- size: 16
- })
- })]
- })
+ shellId: fieldIds.shellId
+ })
+ })]
});
});
\ No newline at end of file