@khanacademy/wonder-blocks-date-picker 1.0.21 → 1.1.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @khanacademy/wonder-blocks-date-picker
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5dd4192: DatePicker: the calendar overlay now only opens by clicking the calendar toggle button (or activating it via keyboard), matching native browser date/time inputs.
8
+
9
+ DatePicker's focused day button in the calendar grid now also uses the standard Wonder Blocks focus style (`focusStyles` from `@khanacademy/wonder-blocks-styles`) instead of react-day-picker's default focus ring.
10
+
11
+ - 63dcc94: DatePicker: ArrowUp/ArrowDown now adjust the day/month/year segment under the caret for numeric date formats. ArrowLeft/ArrowRight move the selection between segments, respecting the input's writing direction (RTL/LTR) instead of relying on native bidi caret movement.
12
+
13
+ ### Patch Changes
14
+
15
+ - a207883: Update date picker styling for accessibility
16
+ - Updated dependencies [63dcc94]
17
+ - Updated dependencies [5dd4192]
18
+ - @khanacademy/wonder-blocks-core@12.5.0
19
+ - @khanacademy/wonder-blocks-modal@8.8.1
20
+ - @khanacademy/wonder-blocks-icon-button@11.5.0
21
+ - @khanacademy/wonder-blocks-form@7.6.12
22
+
23
+ ## 1.0.22
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [e11b241]
28
+ - @khanacademy/wonder-blocks-modal@8.8.0
29
+
3
30
  ## 1.0.21
4
31
 
5
32
  ### Patch Changes
@@ -21,14 +21,36 @@ interface Props {
21
21
  * Called when the input element loses focus.
22
22
  */
23
23
  onBlur?: (e: React.FocusEvent<HTMLInputElement>) => unknown;
24
- /**
25
- * Called when the input element is clicked.
26
- */
27
- onClick?: (arg1: React.MouseEvent<Element>) => unknown;
28
24
  /**
29
25
  * Called when the input element gains focus.
30
26
  */
31
27
  onFocus?: (e: React.FocusEvent<HTMLInputElement>) => unknown;
28
+ /**
29
+ * Whether the calendar overlay is currently open. Reflected on the
30
+ * calendar toggle button via `aria-expanded`.
31
+ */
32
+ expanded: boolean;
33
+ /**
34
+ * Called when the calendar toggle button is clicked (or activated via
35
+ * keyboard) to open/close the calendar overlay.
36
+ */
37
+ onToggleOverlay: () => unknown;
38
+ /**
39
+ * Called on keydown while the calendar toggle button is focused. Used to
40
+ * move focus into the calendar overlay on ArrowDown when it's already
41
+ * open, and to close it on Escape.
42
+ */
43
+ onCalendarButtonKeyDown?: (e: React.KeyboardEvent) => unknown;
44
+ /**
45
+ * The aria-label for the calendar toggle button.
46
+ */
47
+ calendarButtonAriaLabel?: string;
48
+ /**
49
+ * Ref to the calendar toggle button's DOM node. Lets the parent
50
+ * (DatePicker) focus the button directly (e.g. after Escape closes the
51
+ * overlay) and use it as the overlay's focus-trap anchor.
52
+ */
53
+ calendarButtonRef?: React.Ref<HTMLButtonElement>;
32
54
  /**
33
55
  * Called if the user press a key inside the input element.
34
56
  */
@@ -13,6 +13,17 @@ interface Props {
13
13
  * The reference element used to position the popper.
14
14
  */
15
15
  referenceElement: HTMLElement | null | undefined;
16
+ /**
17
+ * The element used as the focus-trap's anchor: Tab from this element
18
+ * enters the overlay, and Shift+Tab out of the overlay's first element
19
+ * returns focus to it. Defaults to `referenceElement` if not given.
20
+ *
21
+ * This is separate from `referenceElement` because the popper should
22
+ * stay positioned relative to the input field, even when a different
23
+ * element (e.g. a toggle button) is what keyboard focus should treat as
24
+ * the overlay's anchor.
25
+ */
26
+ focusReferenceElement?: HTMLElement | null | undefined;
16
27
  /**
17
28
  * Text direction: when "rtl", the overlay is positioned at the end (e.g. bottom-end)
18
29
  * so it aligns with the input in RTL layout. Defaults to "ltr" (bottom-start).
@@ -22,6 +33,10 @@ interface Props {
22
33
  * Styles that will be applied to the children.
23
34
  */
24
35
  style?: StyleType;
36
+ /**
37
+ * The aria-label for the calendar grid region. Defaults to "Date picker calendar".
38
+ */
39
+ calendarGridRegionAriaLabel?: string;
25
40
  }
26
41
  /**
27
42
  * The custom overlay wrapper that will be used to render the calendar popup
@@ -31,5 +46,5 @@ interface Props {
31
46
  * calendar popup in the current view. This includes using it inside a normal
32
47
  * page or inside a Modal component.
33
48
  */
34
- declare const DatePickerOverlay: ({ children, referenceElement, onClose, dir, style, }: Props) => React.ReactElement | null;
49
+ declare const DatePickerOverlay: ({ children, referenceElement, focusReferenceElement, onClose, dir, style, calendarGridRegionAriaLabel, }: Props) => React.ReactElement | null;
35
50
  export default DatePickerOverlay;
@@ -68,6 +68,15 @@ interface Props {
68
68
  * is no visible label associated with the date picker, such as with LabeledField.
69
69
  */
70
70
  inputAriaLabel?: string;
71
+ /**
72
+ * The aria-label for the calendar toggle button that opens/closes the
73
+ * calendar overlay. Defaults to "Toggle calendar".
74
+ */
75
+ calendarButtonAriaLabel?: string;
76
+ /**
77
+ * The aria-label for the calendar grid region. Defaults to "Date picker calendar".
78
+ */
79
+ calendarGridRegionAriaLabel?: string;
71
80
  /**
72
81
  * The placeholder assigned to the date field
73
82
  */
package/dist/es/index.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { StyleSheet } from 'aphrodite';
2
+ import { StyleSheet, css } from 'aphrodite';
3
3
  import { Temporal } from 'temporal-polyfill';
4
4
  import * as React from 'react';
5
- import { DayPicker } from 'react-day-picker';
5
+ import { UI, getDefaultClassNames, DayPicker } from 'react-day-picker';
6
6
  import { enUS } from 'react-day-picker/locale';
7
- import { useOnMountEffect, View, findFocusableNodes } from '@khanacademy/wonder-blocks-core';
8
- import { semanticColor, sizing, border, boxShadow, font } from '@khanacademy/wonder-blocks-tokens';
7
+ import { useDirectionDetection, useOnMountEffect, View, findFocusableNodes } from '@khanacademy/wonder-blocks-core';
8
+ import { sizing, semanticColor, border, boxShadow, font } from '@khanacademy/wonder-blocks-tokens';
9
+ import { focusStyles } from '@khanacademy/wonder-blocks-styles';
9
10
  import { TextField } from '@khanacademy/wonder-blocks-form';
10
- import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
11
+ import IconButton from '@khanacademy/wonder-blocks-icon-button';
11
12
  import calendarIcon from '@phosphor-icons/core/bold/calendar-blank-bold.svg';
12
13
  import { createPortal } from 'react-dom';
13
14
  import { Popper } from 'react-popper';
@@ -16,7 +17,9 @@ import 'react-day-picker/style.css';
16
17
 
17
18
  function useCloseOnOutsideClick({refWrapper,datePickerRef,showOverlay,closeOnSelect,close}){React.useEffect(()=>{const handleClick=e=>{const target=e.target;const thisElement=refWrapper.current;const dayPickerCalendar=datePickerRef.current;const isElement=target instanceof Element;const inThisElement=isElement&&thisElement?.contains(target);const inCalendar=isElement&&dayPickerCalendar?.contains(target);const inPortal=isElement&&target.closest("[data-placement]")!==null;const shouldClose=showOverlay&&closeOnSelect&&thisElement&&!inThisElement&&!inCalendar&&!inPortal;if(shouldClose){close();}};document.addEventListener("mouseup",handleClick);return ()=>{document.removeEventListener("mouseup",handleClick);}},[refWrapper,datePickerRef,showOverlay,closeOnSelect,close]);}
18
19
 
19
- const enUSLocaleCode="en-US";const TEXT_FORMAT_STRINGS=["LL","MMMM D, YYYY","MMM D, YYYY"];function isTextFormatDate(formatString){return formatString!=null&&TEXT_FORMAT_STRINGS.includes(formatString)}function normalizeDateStringForComparison(s){return s.trim().toLowerCase().replace(/\s+/g," ").replace(/[,.\u202f]/g," ").replace(/\s+/g," ").trim()}function formatDate(date,formatString,locale){const localeCode=typeof locale==="string"?locale:locale?.code??enUSLocaleCode;if(!formatString){return date.toLocaleString(localeCode,{year:"numeric",month:"numeric",day:"numeric"})}if(formatString==="L"){return date.toLocaleString(localeCode,{year:"numeric",month:"numeric",day:"numeric"})}if(formatString==="LL"){return date.toLocaleString(localeCode,{dateStyle:"long"})}if(formatString==="dateStyle:short"||formatString==="dateStyle:medium"||formatString==="dateStyle:long"||formatString==="dateStyle:full"){const style=formatString.split(":")[1];return date.toLocaleString(localeCode,{dateStyle:style})}if(formatString==="YYYY-MM-DD"){return date.toString()}if(formatString==="MMMM D, YYYY"||formatString==="MMM D, YYYY"){try{const monthFormat=formatString==="MMMM D, YYYY"?"long":"short";const monthName=date.toLocaleString(localeCode,{month:monthFormat});return `${monthName} ${date.day}, ${date.year}`}catch(error){return date.toString()}}if(formatString==="MM/DD/YYYY"||formatString==="M/D/YYYY"||formatString==="DD/MM/YYYY"){const shouldPad=formatString.includes("MM")||formatString.includes("DD");const month=shouldPad?String(date.month).padStart(2,"0"):String(date.month);const day=shouldPad?String(date.day).padStart(2,"0"):String(date.day);return `${month}/${day}/${date.year}`}try{const options=getOptionsForFormat(formatString);return date.toLocaleString(localeCode,options)}catch(error){console.warn(`Failed to format date with format "${formatString}" and locale "${localeCode}". Falling back to ISO format.`,error);return date.toString()}}function parseDate(str,formatString,locale){if(!str||str.trim()===""){return undefined}try{return Temporal.PlainDate.from(str)}catch{}const format=formatString||"L";try{const parsed=parseWithFormat(str,format,locale);if(parsed){return parsed}}catch{}return undefined}const getModifiersForDay=(day,modifiers)=>{const matchedModifiers=[];for(const[modifierName,matcher]of Object.entries(modifiers)){if(!matcher){continue}if(typeof matcher==="function"){if(matcher(day)){matchedModifiers.push(modifierName);}}else if(matcher instanceof Date){if(day.getFullYear()===matcher.getFullYear()&&day.getMonth()===matcher.getMonth()&&day.getDate()===matcher.getDate()){matchedModifiers.push(modifierName);}}}return matchedModifiers};function temporalDateToJsDate(date){return new Date(date.year,date.month-1,date.day)}function jsDateToTemporalDate(date){return Temporal.PlainDate.from({year:date.getFullYear(),month:date.getMonth()+1,day:date.getDate()})}function parseDateToJsDate(value,formatString,locale){if(value instanceof Date){return value}const temporalDate=parseDate(value,formatString,locale||undefined);if(temporalDate){const formatted=formatDate(temporalDate,formatString,locale||undefined);if(formatted===value){return temporalDateToJsDate(temporalDate)}const normalizedFormatted=formatted.replace(/\b0(\d)\b/g,"$1");const normalizedValue=value.replace(/\b0(\d)\b/g,"$1");if(normalizedFormatted===normalizedValue){return temporalDateToJsDate(temporalDate)}if(value===temporalDate.toString()){return temporalDateToJsDate(temporalDate)}const isTextFormat=isTextFormatDate(formatString);if(isTextFormat){const normalizedFormatted=formatted.replace(/\s+/g," ").trim().toLowerCase();const normalizedValue=value.replace(/\s+/g," ").trim().toLowerCase();if(normalizedFormatted===normalizedValue){return temporalDateToJsDate(temporalDate)}if(normalizedFormatted.startsWith(normalizedValue)||normalizedValue.startsWith(normalizedFormatted)){return temporalDateToJsDate(temporalDate)}return undefined}return undefined}return undefined}function getMonths(locale){const format=new Intl.DateTimeFormat(locale||enUSLocaleCode,{month:"long"});const formatShort=new Intl.DateTimeFormat(locale||enUSLocaleCode,{month:"short"});const months=[];for(let i=0;i<12;i++){const date=new Date(2021,i,15);months.push([format.format(date),formatShort.format(date)]);}return months}function getOptionsForFormat(format){const options={};if(format.includes("YYYY")){options.year="numeric";}else if(format.includes("YY")){options.year="2-digit";}if(format.includes("MMMM")){options.month="long";}else if(format.includes("MMM")){options.month="short";}else if(format.includes("MM")){options.month="2-digit";}else if(format.includes("M")){options.month="numeric";}if(format.includes("DD")){options.day="2-digit";}else if(format.includes("D")){options.day="numeric";}if(format.includes("dddd")){options.weekday="long";}else if(format.includes("ddd")){options.weekday="short";}return options}function parseLocaleAwareDate(str,locale){const localeStr=locale||enUSLocaleCode;const cleaned=str.trim();if(!cleaned){return undefined}try{const formatter=new Intl.DateTimeFormat(localeStr,{dateStyle:"short"});const testDate=new Date(2020,0,15);const parts=formatter.formatToParts(testDate);const pattern=parts.map(p=>({type:p.type,value:p.value}));const separators=pattern.filter(p=>p.type==="literal").map(p=>p.value);const inputParts=cleaned.split(new RegExp(`[${separators.map(s=>`\\${s}`).join("")}]`));if(inputParts.length!==3){throw new Error("Not a numeric date format")}const dateComponents={};let partIndex=0;for(const patternPart of pattern){if(patternPart.type==="literal"){continue}const value=parseInt(inputParts[partIndex],10);if(isNaN(value)){throw new Error("Not a numeric date format")}dateComponents[patternPart.type]=value;partIndex++;}if(!dateComponents.year||!dateComponents.month||!dateComponents.day||dateComponents.month<1||dateComponents.month>12||dateComponents.day<1||dateComponents.day>31||dateComponents.year<1e3||dateComponents.year>9999){throw new Error("Invalid date range")}return Temporal.PlainDate.from({year:dateComponents.year,month:dateComponents.month,day:dateComponents.day})}catch{return parseTextDate(cleaned,localeStr)}}function parseTextDate(str,locale){try{const months=getMonths(locale);const numbers=str.match(/\d+/g)??[];const lowerStr=str.toLowerCase();let monthIndex=-1;for(let i=0;i<months.length;i++){const[longName,shortName]=months[i];if(lowerStr.includes(longName.toLowerCase())||lowerStr.includes(shortName.toLowerCase())){monthIndex=i+1;break}}if(monthIndex===-1){return undefined}const now=Temporal.Now.plainDateISO();if(numbers.length>=2){const n1=numbers[0];const n2=numbers[1];if(n1===undefined||n2===undefined){return undefined}let day;let year;const num1=parseInt(n1,10);const num2=parseInt(n2,10);if(num1>31){year=num1;day=num2;}else if(num2>31||num2.toString().length===4){day=num1;year=num2;}else {day=num1;year=num2;}if(day<1||day>31||year<1e3||year>9999){return undefined}return Temporal.PlainDate.from({year,month:monthIndex,day})}if(numbers.length===1){const n0=numbers[0];if(n0===undefined){return undefined}const n=parseInt(n0,10);const isYear=n>=1e3&&n<=9999;const year=isYear?n:now.year;const day=isYear?1:n>=1&&n<=31?n:1;return Temporal.PlainDate.from({year,month:monthIndex,day})}return Temporal.PlainDate.from({year:now.year,month:monthIndex,day:1})}catch{return undefined}}function parseWithFormat(str,format,locale){if(!format){return undefined}if(format==="L"||format==="LL"||format.startsWith("dateStyle:")){return parseLocaleAwareDate(str,locale)}if(format==="M/D/YYYY"||format==="M-D-YYYY"||format==="MM/DD/YYYY"||format==="MM-DD-YYYY"){const separator=format.includes("/")?"/":"-";const parts=str.split(separator);if(parts.length===3){const month=parseInt(parts[0],10);const day=parseInt(parts[1],10);const year=parseInt(parts[2],10);if(isNaN(month)||isNaN(day)||isNaN(year)||month<1||month>12||day<1||day>31||year<1e3||year>9999){return undefined}try{return Temporal.PlainDate.from({year,month,day})}catch{return undefined}}}if(format==="MMMM D, YYYY"||format==="MMM D, YYYY"){try{const cleaned=str.trim();const localeStr=locale||enUSLocaleCode;const parts=cleaned.split(",");if(parts.length===2){const[monthDay,yearStr]=parts;const year=parseInt(yearStr.trim(),10);if(year<1e3||year>9999){return undefined}const months=getMonths(localeStr).map(m=>m[0]);const monthDayParts=monthDay.trim().split(" ");if(monthDayParts.length===2){const monthName=monthDayParts[0];const day=parseInt(monthDayParts[1],10);const monthIndex=months.findIndex(m=>m.toLowerCase()===monthName.toLowerCase()||m.slice(0,3).toLowerCase()===monthName.toLowerCase());if(monthIndex>=0&&!isNaN(day)&&!isNaN(year)){return Temporal.PlainDate.from({year,month:monthIndex+1,day})}}}return parseTextDate(cleaned,localeStr)}catch{return undefined}}return undefined}const startOfIsoWeek=date=>{const dayOfWeek=date.dayOfWeek;return date.subtract({days:dayOfWeek-1})};const startOfDay=date=>{const result=new Date(date);result.setHours(0,0,0,0);return result};const endOfDay=date=>{const result=new Date(date);result.setHours(23,59,59,999);return result};const TemporalLocaleUtils={formatDate,isTextFormatDate,normalizeDateStringForComparison,parseDate,parseDateToJsDate,startOfIsoWeek,startOfDay,endOfDay,temporalDateToJsDate,jsDateToTemporalDate,getModifiersForDay};
20
+ const enUSLocaleCode="en-US";const TEXT_FORMAT_STRINGS=["LL","MMMM D, YYYY","MMM D, YYYY"];const SLASH_FORMAT_STRINGS=new Set(["MM/DD/YYYY","M/D/YYYY","DD/MM/YYYY"]);const INTL_TYPE_TO_SEGMENT_TYPE={day:"day",month:"month",year:"year"};
21
+
22
+ function isTextFormatDate(formatString){return formatString!=null&&TEXT_FORMAT_STRINGS.includes(formatString)}function normalizeDateStringForComparison(s){return s.trim().toLowerCase().replace(/\s+/g," ").replace(/[,.\u202f]/g," ").replace(/\s+/g," ").trim()}function formatDate(date,formatString,locale){const localeCode=typeof locale==="string"?locale:locale?.code??enUSLocaleCode;if(!formatString){return date.toLocaleString(localeCode,{year:"numeric",month:"numeric",day:"numeric"})}if(formatString==="L"){return date.toLocaleString(localeCode,{year:"numeric",month:"numeric",day:"numeric"})}if(formatString==="LL"){return date.toLocaleString(localeCode,{dateStyle:"long"})}if(formatString==="dateStyle:short"||formatString==="dateStyle:medium"||formatString==="dateStyle:long"||formatString==="dateStyle:full"){const style=formatString.split(":")[1];return date.toLocaleString(localeCode,{dateStyle:style})}if(formatString==="YYYY-MM-DD"){return date.toString()}if(formatString==="MMMM D, YYYY"||formatString==="MMM D, YYYY"){try{const monthFormat=formatString==="MMMM D, YYYY"?"long":"short";const monthName=date.toLocaleString(localeCode,{month:monthFormat});return `${monthName} ${date.day}, ${date.year}`}catch(error){return date.toString()}}if(formatString==="MM/DD/YYYY"||formatString==="M/D/YYYY"||formatString==="DD/MM/YYYY"){const shouldPad=formatString.includes("MM")||formatString.includes("DD");const month=shouldPad?String(date.month).padStart(2,"0"):String(date.month);const day=shouldPad?String(date.day).padStart(2,"0"):String(date.day);return `${month}/${day}/${date.year}`}try{const options=getOptionsForFormat(formatString);return date.toLocaleString(localeCode,options)}catch(error){console.warn(`Failed to format date with format "${formatString}" and locale "${localeCode}". Falling back to ISO format.`,error);return date.toString()}}function parseDate(str,formatString,locale){if(!str||str.trim()===""){return undefined}try{return Temporal.PlainDate.from(str)}catch{}const format=formatString||"L";try{const parsed=parseWithFormat(str,format,locale);if(parsed){return parsed}}catch{}return undefined}const getModifiersForDay=(day,modifiers)=>{const matchedModifiers=[];for(const[modifierName,matcher]of Object.entries(modifiers)){if(!matcher){continue}if(typeof matcher==="function"){if(matcher(day)){matchedModifiers.push(modifierName);}}else if(matcher instanceof Date){if(day.getFullYear()===matcher.getFullYear()&&day.getMonth()===matcher.getMonth()&&day.getDate()===matcher.getDate()){matchedModifiers.push(modifierName);}}}return matchedModifiers};function temporalDateToJsDate(date){return new Date(date.year,date.month-1,date.day)}function jsDateToTemporalDate(date){return Temporal.PlainDate.from({year:date.getFullYear(),month:date.getMonth()+1,day:date.getDate()})}function parseDateToJsDate(value,formatString,locale){if(value instanceof Date){return value}const temporalDate=parseDate(value,formatString,locale||undefined);if(temporalDate){const formatted=formatDate(temporalDate,formatString,locale||undefined);if(formatted===value){return temporalDateToJsDate(temporalDate)}const normalizedFormatted=formatted.replace(/\b0(\d)\b/g,"$1");const normalizedValue=value.replace(/\b0(\d)\b/g,"$1");if(normalizedFormatted===normalizedValue){return temporalDateToJsDate(temporalDate)}if(value===temporalDate.toString()){return temporalDateToJsDate(temporalDate)}const isTextFormat=isTextFormatDate(formatString);if(isTextFormat){const normalizedFormatted=formatted.replace(/\s+/g," ").trim().toLowerCase();const normalizedValue=value.replace(/\s+/g," ").trim().toLowerCase();if(normalizedFormatted===normalizedValue){return temporalDateToJsDate(temporalDate)}if(normalizedFormatted.startsWith(normalizedValue)||normalizedValue.startsWith(normalizedFormatted)){return temporalDateToJsDate(temporalDate)}return undefined}return undefined}return undefined}function getMonths(locale){const format=new Intl.DateTimeFormat(locale||enUSLocaleCode,{month:"long"});const formatShort=new Intl.DateTimeFormat(locale||enUSLocaleCode,{month:"short"});const months=[];for(let i=0;i<12;i++){const date=new Date(2021,i,15);months.push([format.format(date),formatShort.format(date)]);}return months}function getOptionsForFormat(format){const options={};if(format.includes("YYYY")){options.year="numeric";}else if(format.includes("YY")){options.year="2-digit";}if(format.includes("MMMM")){options.month="long";}else if(format.includes("MMM")){options.month="short";}else if(format.includes("MM")){options.month="2-digit";}else if(format.includes("M")){options.month="numeric";}if(format.includes("DD")){options.day="2-digit";}else if(format.includes("D")){options.day="numeric";}if(format.includes("dddd")){options.weekday="long";}else if(format.includes("ddd")){options.weekday="short";}return options}function buildNumericDatePattern(localeStr,format){const detectionOptions=format&&format.startsWith("dateStyle:")?{dateStyle:format.slice("dateStyle:".length)}:{year:"numeric",month:"numeric",day:"numeric"};const formatter=new Intl.DateTimeFormat(localeStr,detectionOptions);const testDate=new Date(2020,0,15);const pattern=formatter.formatToParts(testDate).map(p=>({type:p.type,value:p.value}));const numericIndexes=pattern.map((p,i)=>p.type!=="literal"&&/^\d+$/.test(p.value)?i:-1).filter(i=>i!==-1);if(numericIndexes.length!==3){return null}const[firstNumericIndex,,lastNumericIndex]=numericIndexes;const escapeLiteral=value=>value.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/[ \u202f]/g,"[ \\u202f]");const partOrder=[];let core="";let leadingLiteral="";let trailingLiteral="";pattern.forEach((patternPart,i)=>{if(patternPart.type!=="literal"){core+="(\\d+)";partOrder.push(patternPart.type);return}const escaped=escapeLiteral(patternPart.value);if(i<firstNumericIndex){leadingLiteral+=escaped;}else if(i>lastNumericIndex){trailingLiteral+=escaped;}else {core+=escaped;}});const regexSource="^"+(leadingLiteral?`(?:${leadingLiteral})?`:"")+core+(trailingLiteral?`(?:${trailingLiteral})?`:"")+"$";return {regex:new RegExp(regexSource),partOrder}}function parseLocaleAwareDate(str,format,locale){const localeStr=locale||enUSLocaleCode;const cleaned=str.trim();if(!cleaned){return undefined}try{const pattern=buildNumericDatePattern(localeStr,format);if(!pattern){throw new Error("Not a numeric date format")}const match=cleaned.match(pattern.regex);if(!match){throw new Error("Not a numeric date format")}const dateComponents={};pattern.partOrder.forEach((type,i)=>{dateComponents[type]=parseInt(match[i+1],10);});if(!dateComponents.year||!dateComponents.month||!dateComponents.day||dateComponents.month<1||dateComponents.month>12||dateComponents.day<1||dateComponents.day>31||dateComponents.year<1e3||dateComponents.year>9999){throw new Error("Invalid date range")}return Temporal.PlainDate.from({year:dateComponents.year,month:dateComponents.month,day:dateComponents.day})}catch{return parseTextDate(cleaned,localeStr)}}function parseTextDate(str,locale){try{const months=getMonths(locale);const numbers=str.match(/\d+/g)??[];const lowerStr=str.toLowerCase();let monthIndex=-1;for(let i=0;i<months.length;i++){const[longName,shortName]=months[i];if(lowerStr.includes(longName.toLowerCase())||lowerStr.includes(shortName.toLowerCase())){monthIndex=i+1;break}}if(monthIndex===-1){return undefined}const now=Temporal.Now.plainDateISO();if(numbers.length>=2){const n1=numbers[0];const n2=numbers[1];if(n1===undefined||n2===undefined){return undefined}let day;let year;const num1=parseInt(n1,10);const num2=parseInt(n2,10);if(num1>31){year=num1;day=num2;}else if(num2>31||num2.toString().length===4){day=num1;year=num2;}else {day=num1;year=num2;}if(day<1||day>31||year<1e3||year>9999){return undefined}return Temporal.PlainDate.from({year,month:monthIndex,day})}if(numbers.length===1){const n0=numbers[0];if(n0===undefined){return undefined}const n=parseInt(n0,10);const isYear=n>=1e3&&n<=9999;const year=isYear?n:now.year;const day=isYear?1:n>=1&&n<=31?n:1;return Temporal.PlainDate.from({year,month:monthIndex,day})}return Temporal.PlainDate.from({year:now.year,month:monthIndex,day:1})}catch{return undefined}}function parseWithFormat(str,format,locale){if(!format){return undefined}if(format==="L"||format==="LL"||format.startsWith("dateStyle:")){return parseLocaleAwareDate(str,format,locale)}if(format==="M/D/YYYY"||format==="M-D-YYYY"||format==="MM/DD/YYYY"||format==="MM-DD-YYYY"){const separator=format.includes("/")?"/":"-";const parts=str.split(separator);if(parts.length===3){const month=parseInt(parts[0],10);const day=parseInt(parts[1],10);const year=parseInt(parts[2],10);if(isNaN(month)||isNaN(day)||isNaN(year)||month<1||month>12||day<1||day>31||year<1e3||year>9999){return undefined}try{return Temporal.PlainDate.from({year,month,day})}catch{return undefined}}}if(format==="MMMM D, YYYY"||format==="MMM D, YYYY"){try{const cleaned=str.trim();const localeStr=locale||enUSLocaleCode;const parts=cleaned.split(",");if(parts.length===2){const[monthDay,yearStr]=parts;const year=parseInt(yearStr.trim(),10);if(year<1e3||year>9999){return undefined}const months=getMonths(localeStr).map(m=>m[0]);const monthDayParts=monthDay.trim().split(" ");if(monthDayParts.length===2){const monthName=monthDayParts[0];const day=parseInt(monthDayParts[1],10);const monthIndex=months.findIndex(m=>m.toLowerCase()===monthName.toLowerCase()||m.slice(0,3).toLowerCase()===monthName.toLowerCase());if(monthIndex>=0&&!isNaN(day)&&!isNaN(year)){return Temporal.PlainDate.from({year,month:monthIndex+1,day})}}}return parseTextDate(cleaned,localeStr)}catch{return undefined}}return undefined}const startOfIsoWeek=date=>{const dayOfWeek=date.dayOfWeek;return date.subtract({days:dayOfWeek-1})};const startOfDay=date=>{const result=new Date(date);result.setHours(0,0,0,0);return result};const endOfDay=date=>{const result=new Date(date);result.setHours(23,59,59,999);return result};const TemporalLocaleUtils={formatDate,isTextFormatDate,normalizeDateStringForComparison,parseDate,parseDateToJsDate,startOfIsoWeek,startOfDay,endOfDay,temporalDateToJsDate,jsDateToTemporalDate,getModifiersForDay};
20
23
 
21
24
  function useDatePickerModifiers({selectedDateValue,minDate,maxDate}){return React.useMemo(()=>({selected:selectedDateValue,disabled:date=>{const temporalDate=TemporalLocaleUtils.jsDateToTemporalDate(date);return minDate&&Temporal.PlainDate.compare(temporalDate,minDate)<0||maxDate&&Temporal.PlainDate.compare(temporalDate,maxDate)>0||false}}),[selectedDateValue,minDate,maxDate])}
22
25
 
@@ -32,12 +35,26 @@ function useOverlayMonthFromInput({inputDrivenMonthRef,setDisplayMonth,setDispla
32
35
 
33
36
  function useSelectedDateSync({selectedDate,setCurrentDate,setDisplayMonthAndRefs}){const prevSelectedDateRef=React.useRef(null);React.useEffect(()=>{setCurrentDate(selectedDate);const key=selectedDate?.toString()??null;const willUpdateDisplayMonth=key!==prevSelectedDateRef.current;if(willUpdateDisplayMonth){prevSelectedDateRef.current=key;if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}}},[selectedDate,setCurrentDate,setDisplayMonthAndRefs]);}
34
37
 
35
- const DatePickerInput=React.forwardRef((props,ref)=>{const{value:propValue,onBlur,onClick,onFocus,onKeyDown,onChange,dateFormat,locale=enUSLocaleCode,modifiers,getModifiersForDay,parseDate,placeholder,testId,resetInvalidValueOnBlur=true,["aria-label"]:ariaLabel,...restProps}=props;const[value,setValue]=React.useState(propValue);const lastPropValueRef=React.useRef(propValue);const keepInvalidTextRef=React.useRef(false);const lastTypedTextFormatValueRef=React.useRef(null);const processModifiers=React.useCallback((date,value)=>{if(!getModifiersForDay||!modifiers){return {}}return getModifiersForDay(date,modifiers).reduce((obj,modifier)=>({...obj,[modifier]:true}),{})},[getModifiersForDay,modifiers]);const updateDate=React.useCallback((date,inputValue)=>{if(onChange){onChange(date,processModifiers(date,inputValue),inputValue||undefined);}},[onChange,processModifiers]);const updateDateAsInvalid=React.useCallback(()=>{if(onChange){onChange(null,{});}},[onChange]);const processDate=React.useCallback(inputValue=>{if(!inputValue||inputValue.trim()===""){return}if(!parseDate){return}const date=parseDate(inputValue,dateFormat,locale);if(!date){return}return date},[parseDate,dateFormat,locale]);const isValid=React.useCallback(()=>{const date=processDate(value);if(!date){return false}const modifiersResult=processModifiers(date,value);if(modifiersResult.disabled){return false}return true},[value,processDate,processModifiers]);const isTextFormat=TemporalLocaleUtils.isTextFormatDate(dateFormat);React.useEffect(()=>{const propValueChanged=lastPropValueRef.current!==propValue;lastPropValueRef.current=propValue;if(propValueChanged){const safeProp=propValue??"";const safeValue=value??"";const isLastTypedValue=lastTypedTextFormatValueRef.current!==null&&value===lastTypedTextFormatValueRef.current;const bothHaveContent=safeValue!==""&&safeProp!=="";const oneIsPrefixOfOther=safeProp.startsWith(safeValue)||safeValue.startsWith(safeProp);const skipSyncUserTyping=isTextFormat&&propValue!==value&&(isLastTypedValue||bothHaveContent&&oneIsPrefixOfOther);if(keepInvalidTextRef.current&&(!propValue||propValue.trim()==="")){keepInvalidTextRef.current=false;}else if(skipSyncUserTyping){return}else if(propValue===value||(propValue??"")===(value??"")){keepInvalidTextRef.current=false;lastTypedTextFormatValueRef.current=null;return}else {setValue(propValue);keepInvalidTextRef.current=false;lastTypedTextFormatValueRef.current=null;}}},[propValue,isTextFormat,value]);useOnMountEffect(()=>{const skipValidation=dateFormat==="LL"&&propValue;if(!skipValidation&&!isValid()){updateDateAsInvalid();}});const handleFocus=e=>{if(onFocus){onFocus(e);}};const pendingValidationRef=React.useRef(false);const validateInput=React.useCallback(()=>{lastTypedTextFormatValueRef.current=null;const date=processDate(value);if(date){const modifiersResult=processModifiers(date,value);if(!modifiersResult.disabled){updateDate(date,value);}else {if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDate(date,value);}else {setValue(propValue);}}}else if(value&&value.trim()!==""){if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDateAsInvalid();}else {setValue(propValue);}}else {setValue(propValue);}},[value,processDate,processModifiers,resetInvalidValueOnBlur,propValue,updateDate,updateDateAsInvalid]);const handleBlur=e=>{const movingToCalendar=e.relatedTarget instanceof HTMLElement&&e.relatedTarget.closest('[data-testid="date-picker-overlay"]')!==null;if(movingToCalendar){pendingValidationRef.current=true;if(onBlur){onBlur(e);}return}validateInput();if(onBlur){onBlur(e);}};const innerRef=React.useRef(null);const handleChange=newValue=>{setValue(newValue);const date=processDate(newValue);if(date){const modifiersResult=processModifiers(date,newValue);if(isTextFormat){lastTypedTextFormatValueRef.current=newValue;}if(!modifiersResult.disabled){updateDate(date,newValue);}else if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDate(date,newValue);}else {updateDate(date,newValue);}}else if(!resetInvalidValueOnBlur&&newValue&&newValue.trim()!==""){keepInvalidTextRef.current=true;updateDateAsInvalid();}};React.useImperativeHandle(ref,()=>{const inputElement=innerRef.current;if(!inputElement){return null}inputElement.validateInput=()=>{pendingValidationRef.current=false;validateInput();};return inputElement});return jsxs(View,{style:styles$1.container,onClick:e=>{if(!restProps.disabled&&onClick){onClick(e);}},children:[jsx(TextField,{ref:innerRef,...restProps,onBlur:handleBlur,onFocus:handleFocus,onKeyDown:onKeyDown,onChange:handleChange,disabled:restProps.disabled,placeholder:placeholder,value:value??"",testId:testId,"aria-label":ariaLabel,autoComplete:"off",type:"text",style:styles$1.textField}),jsx(PhosphorIcon,{icon:calendarIcon,color:restProps.disabled?semanticColor.core.foreground.disabled.default:semanticColor.core.foreground.instructive.default,size:"small",style:styles$1.icon})]})});const fieldPaddingInline=sizing.size_160;const iconSize=sizing.size_160;const fieldPaddingInlineEnd=fieldPaddingInline+iconSize+fieldPaddingInline;const styles$1=StyleSheet.create({container:{alignItems:"center",flexDirection:"row",justifyContent:"stretch"},icon:{pointerEvents:"none",position:"absolute",insetInlineEnd:fieldPaddingInline},textField:{width:"100%",paddingInlineStart:fieldPaddingInline,paddingInlineEnd:fieldPaddingInlineEnd}});
38
+ function adjustDateSegment(date,type,delta){if(type==="day"){const{daysInMonth}=date;const newDay=((date.day-1+delta)%daysInMonth+daysInMonth)%daysInMonth+1;return date.with({day:newDay})}if(type==="month"){const newMonth=((date.month-1+delta)%12+12)%12+1;return date.with({month:newMonth})}return date.with({year:date.year+delta})}
39
+
40
+ function findSegmentAtOffset(segments,offset){return segments.find(s=>offset>=s.start&&offset<=s.end)??null}
41
+
42
+ function getIntlOptionsForSegmentDetection(formatString){if(!formatString||formatString==="L"){return {year:"numeric",month:"numeric",day:"numeric"}}if(formatString==="dateStyle:short"||formatString==="dateStyle:medium"||formatString==="dateStyle:long"||formatString==="dateStyle:full"){const style=formatString.split(":")[1];return {dateStyle:style}}return null}
43
+
44
+ function segmentsFromFixedOrder(value,separator,order){const parts=value.split(separator);if(parts.length!==order.length){return null}if(!parts.every(part=>/^\d+$/.test(part))){return null}const segments=[];let offset=0;for(let i=0;i<parts.length;i++){const part=parts[i];segments.push({type:order[i],start:offset,end:offset+part.length});offset+=part.length+separator.length;}return segments}
45
+
46
+ function segmentsFromIntlParts(value,locale,options){const referenceDate=new Date(2020,2,15);let parts;try{parts=new Intl.DateTimeFormat(locale,options).formatToParts(referenceDate);}catch{return null}const segments=[];let offset=0;for(const part of parts){const segmentType=INTL_TYPE_TO_SEGMENT_TYPE[part.type];if(segmentType){const match=/^\d+/.exec(value.slice(offset));if(!match){return null}segments.push({type:segmentType,start:offset,end:offset+match[0].length});offset+=match[0].length;}else {if(value.slice(offset,offset+part.value.length)!==part.value){return null}offset+=part.value.length;}}if(offset!==value.length){return null}const types=new Set(segments.map(s=>s.type));if(types.size!==3){return null}return segments}
47
+
48
+ function getDateSegments(value,formatString,locale){if(!value){return null}if(formatString==="YYYY-MM-DD"){return segmentsFromFixedOrder(value,"-",["year","month","day"])}if(formatString&&SLASH_FORMAT_STRINGS.has(formatString)){return segmentsFromFixedOrder(value,"/",["month","day","year"])}const options=getIntlOptionsForSegmentDetection(formatString);if(!options){return null}return segmentsFromIntlParts(value,locale,options)}
49
+
50
+ function useDateSegmentArrowKeys({value,dateFormat,locale,parseDate,handleChange,innerRef}){const pendingSelectionRef=React.useRef(null);const isRtl=useDirectionDetection(innerRef)==="rtl";React.useEffect(()=>{if(pendingSelectionRef.current&&innerRef.current){const[start,end]=pendingSelectionRef.current;innerRef.current.setSelectionRange(start,end);pendingSelectionRef.current=null;}},[value,innerRef]);return React.useCallback(e=>{const key=e.key;const isVerticalKey=key==="ArrowUp"||key==="ArrowDown";const isHorizontalKey=key==="ArrowLeft"||key==="ArrowRight";if(!isVerticalKey&&!isHorizontalKey){return false}if(!value){return false}const segments=getDateSegments(value,dateFormat,locale);if(!segments){return false}const offset=e.currentTarget.selectionStart??0;const segment=findSegmentAtOffset(segments,offset);if(!segment){return false}if(isHorizontalKey){const movesToNextSegment=isRtl?key==="ArrowLeft":key==="ArrowRight";const currentIndex=segments.indexOf(segment);const targetSegment=segments[currentIndex+(movesToNextSegment?1:-1)]??segment;e.preventDefault();innerRef.current?.setSelectionRange(targetSegment.start,targetSegment.end);return true}if(!parseDate){return false}const jsDate=parseDate(value,dateFormat,locale);if(!jsDate){return false}const currentDate=TemporalLocaleUtils.jsDateToTemporalDate(jsDate);const delta=key==="ArrowUp"?1:-1;const adjustedDate=adjustDateSegment(currentDate,segment.type,delta);const newValue=TemporalLocaleUtils.formatDate(adjustedDate,dateFormat,locale);const newSegments=getDateSegments(newValue,dateFormat,locale);const newSegment=newSegments?.find(s=>s.type===segment.type);pendingSelectionRef.current=newSegment?[newSegment.start,newSegment.end]:null;e.preventDefault();handleChange(newValue);return true},[value,dateFormat,locale,parseDate,handleChange,isRtl,innerRef])}
51
+
52
+ const DEFAULT_CALENDAR_BUTTON_ARIA_LABEL="Toggle calendar";const DatePickerInput=React.forwardRef((props,ref)=>{const{value:propValue,onBlur,onFocus,onKeyDown,onChange,dateFormat,locale=enUSLocaleCode,modifiers,getModifiersForDay,parseDate,placeholder,testId,resetInvalidValueOnBlur=true,["aria-label"]:ariaLabel,expanded,onToggleOverlay,onCalendarButtonKeyDown,calendarButtonAriaLabel=DEFAULT_CALENDAR_BUTTON_ARIA_LABEL,calendarButtonRef,...restProps}=props;const[value,setValue]=React.useState(propValue);const lastPropValueRef=React.useRef(propValue);const keepInvalidTextRef=React.useRef(false);const lastTypedTextFormatValueRef=React.useRef(null);const processModifiers=React.useCallback((date,value)=>{if(!getModifiersForDay||!modifiers){return {}}return getModifiersForDay(date,modifiers).reduce((obj,modifier)=>({...obj,[modifier]:true}),{})},[getModifiersForDay,modifiers]);const updateDate=React.useCallback((date,inputValue)=>{if(onChange){onChange(date,processModifiers(date,inputValue),inputValue||undefined);}},[onChange,processModifiers]);const updateDateAsInvalid=React.useCallback(()=>{if(onChange){onChange(null,{});}},[onChange]);const processDate=React.useCallback(inputValue=>{if(!inputValue||inputValue.trim()===""){return}if(!parseDate){return}const date=parseDate(inputValue,dateFormat,locale);if(!date){return}return date},[parseDate,dateFormat,locale]);const isValid=React.useCallback(()=>{const date=processDate(value);if(!date){return false}const modifiersResult=processModifiers(date,value);if(modifiersResult.disabled){return false}return true},[value,processDate,processModifiers]);const isTextFormat=TemporalLocaleUtils.isTextFormatDate(dateFormat);React.useEffect(()=>{const propValueChanged=lastPropValueRef.current!==propValue;lastPropValueRef.current=propValue;if(propValueChanged){const safeProp=propValue??"";const safeValue=value??"";const isLastTypedValue=lastTypedTextFormatValueRef.current!==null&&value===lastTypedTextFormatValueRef.current;const bothHaveContent=safeValue!==""&&safeProp!=="";const oneIsPrefixOfOther=safeProp.startsWith(safeValue)||safeValue.startsWith(safeProp);const skipSyncUserTyping=isTextFormat&&propValue!==value&&(isLastTypedValue||bothHaveContent&&oneIsPrefixOfOther);if(keepInvalidTextRef.current&&(!propValue||propValue.trim()==="")){keepInvalidTextRef.current=false;}else if(skipSyncUserTyping){return}else if(propValue===value||(propValue??"")===(value??"")){keepInvalidTextRef.current=false;lastTypedTextFormatValueRef.current=null;return}else {setValue(propValue);keepInvalidTextRef.current=false;lastTypedTextFormatValueRef.current=null;}}},[propValue,isTextFormat,value]);useOnMountEffect(()=>{const skipValidation=dateFormat==="LL"&&propValue;if(!skipValidation&&!isValid()){updateDateAsInvalid();}});const handleFocus=e=>{if(onFocus){onFocus(e);}};const pendingValidationRef=React.useRef(false);const validateInput=React.useCallback(()=>{lastTypedTextFormatValueRef.current=null;const date=processDate(value);if(date){const modifiersResult=processModifiers(date,value);if(!modifiersResult.disabled){updateDate(date,value);}else {if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDate(date,value);}else {setValue(propValue);}}}else if(value&&value.trim()!==""){if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDateAsInvalid();}else {setValue(propValue);}}else {setValue(propValue);}},[value,processDate,processModifiers,resetInvalidValueOnBlur,propValue,updateDate,updateDateAsInvalid]);const handleBlur=e=>{const movingToCalendar=e.relatedTarget instanceof HTMLElement&&e.relatedTarget.closest('[data-testid="date-picker-overlay"]')!==null;if(movingToCalendar){pendingValidationRef.current=true;if(onBlur){onBlur(e);}return}validateInput();if(onBlur){onBlur(e);}};const innerRef=React.useRef(null);const handleChange=newValue=>{setValue(newValue);const date=processDate(newValue);if(date){const modifiersResult=processModifiers(date,newValue);if(isTextFormat){lastTypedTextFormatValueRef.current=newValue;}if(!modifiersResult.disabled){updateDate(date,newValue);}else if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDate(date,newValue);}else {updateDate(date,newValue);}}else if(!resetInvalidValueOnBlur&&newValue&&newValue.trim()!==""){keepInvalidTextRef.current=true;updateDateAsInvalid();}};const handleSegmentKeyDown=useDateSegmentArrowKeys({value,dateFormat,locale,parseDate,handleChange,innerRef});const handleInputKeyDown=e=>{if(!expanded&&handleSegmentKeyDown(e)){return}onKeyDown?.(e);};React.useImperativeHandle(ref,()=>{const inputElement=innerRef.current;if(!inputElement){return null}inputElement.validateInput=()=>{pendingValidationRef.current=false;validateInput();};return inputElement});return jsxs(View,{style:styles$1.container,children:[jsx(TextField,{ref:innerRef,...restProps,onBlur:handleBlur,onFocus:handleFocus,onKeyDown:handleInputKeyDown,onChange:handleChange,disabled:restProps.disabled,placeholder:placeholder,value:value??"",testId:testId,"aria-label":ariaLabel,autoComplete:"off",type:"text",style:styles$1.textField}),jsx(IconButton,{ref:calendarButtonRef,icon:calendarIcon,size:"small",kind:"tertiary",actionType:"neutral",disabled:restProps.disabled,"aria-label":calendarButtonAriaLabel,"aria-expanded":expanded,"aria-haspopup":"grid",onClick:()=>onToggleOverlay(),onKeyDown:onCalendarButtonKeyDown,style:styles$1.icon})]})});const fieldPaddingInline=sizing.size_160;const calendarButtonSize=sizing.size_320;const fieldPaddingInlineEnd=`calc(${fieldPaddingInline} + ${calendarButtonSize} + ${fieldPaddingInline} / 2)`;const styles$1=StyleSheet.create({container:{alignItems:"center",flexDirection:"row",justifyContent:"stretch"},icon:{margin:0,position:"absolute",insetInlineEnd:fieldPaddingInline},textField:{width:"100%",paddingInlineStart:fieldPaddingInline,paddingInlineEnd:fieldPaddingInlineEnd}});
36
53
 
37
54
  function FocusManager(props){const{children,referenceElement,onStartFocused,onEndFocused}=props;const rootNodeRef=React.useRef(null);const focusableElementsRef=React.useRef([]);const focusableElementsInsideRef=React.useRef([]);const nextFocusableElementRef=React.useRef(null);const getFocusableElements=React.useCallback(()=>{return findFocusableNodes(document)},[]);const getReferenceIndex=React.useCallback(()=>{if(!referenceElement){return -1}return focusableElementsRef.current.indexOf(referenceElement)},[referenceElement]);const getNextFocusableElement=React.useCallback(()=>{const referenceIndex=getReferenceIndex();if(referenceIndex>=0){const nextElementIndex=referenceIndex<focusableElementsRef.current.length-1?referenceIndex+1:0;return focusableElementsRef.current[nextElementIndex]}return undefined},[getReferenceIndex]);React.useEffect(()=>{focusableElementsRef.current=getFocusableElements();nextFocusableElementRef.current=getNextFocusableElement();const handleKeydownReferenceElement=e=>{if(e.key==="Tab"&&!e.shiftKey){if(rootNodeRef.current){focusableElementsInsideRef.current=findFocusableNodes(rootNodeRef.current);}if(focusableElementsInsideRef.current.length>0){e.preventDefault();focusableElementsInsideRef.current[0]?.focus();}}};const handleKeydownNextFocusableElement=e=>{if(e.key==="Tab"&&e.shiftKey){if(rootNodeRef.current){focusableElementsInsideRef.current=findFocusableNodes(rootNodeRef.current);}if(focusableElementsInsideRef.current.length>0){e.preventDefault();const lastIndex=focusableElementsInsideRef.current.length-1;focusableElementsInsideRef.current[lastIndex]?.focus();}}};if(referenceElement){referenceElement.addEventListener("keydown",handleKeydownReferenceElement,true);}if(nextFocusableElementRef.current){nextFocusableElementRef.current.addEventListener("keydown",handleKeydownNextFocusableElement,true);}return ()=>{if(referenceElement){referenceElement.removeEventListener("keydown",handleKeydownReferenceElement,true);}if(nextFocusableElementRef.current){nextFocusableElementRef.current.removeEventListener("keydown",handleKeydownNextFocusableElement,true);}}},[referenceElement,getNextFocusableElement,getFocusableElements]);const setComponentRootNode=React.useCallback(node=>{if(!node){return}rootNodeRef.current=node;focusableElementsInsideRef.current=findFocusableNodes(node);},[]);const handleFocusPreviousFocusableElement=React.useCallback(()=>{if(referenceElement){referenceElement.focus();}if(onStartFocused){onStartFocused();}},[referenceElement,onStartFocused]);const handleFocusNextFocusableElement=React.useCallback(()=>{if(nextFocusableElementRef.current){nextFocusableElementRef.current.focus();}if(onEndFocused){onEndFocused();}},[onEndFocused]);return jsxs(React.Fragment,{children:[jsx("div",{tabIndex:0,"data-testid":"focus-sentinel-prev",onFocus:handleFocusPreviousFocusableElement,style:{position:"fixed"}}),jsx("div",{"data-testid":"date-picker-overlay",ref:setComponentRootNode,children:children}),jsx("div",{tabIndex:0,"data-testid":"focus-sentinel-next",onFocus:handleFocusNextFocusableElement,style:{position:"fixed"}})]})}
38
55
 
39
- const DEFAULT_STYLE={background:semanticColor.core.background.base.default,borderRadius:border.radius.radius_040,border:`solid ${border.width.thin} ${semanticColor.core.border.neutral.subtle}`,boxShadow:boxShadow.mid};const BASE_CONTAINER_STYLES={fontFamily:font.family.sans,padding:sizing.size_100};const OUT_OF_BOUNDARIES_STYLES={visibility:"hidden"};const DatePickerOverlay=({children,referenceElement,onClose,dir="ltr",style=DEFAULT_STYLE})=>{if(!referenceElement){return null}const placement=dir==="rtl"?"bottom-end":"bottom-start";const modalHost=maybeGetPortalMountedModalHostElement(referenceElement)||document.querySelector("body");if(!modalHost){return null}return createPortal(jsx(FocusManager,{referenceElement:referenceElement,onEndFocused:onClose,children:jsx(Popper,{referenceElement:referenceElement,placement:placement,strategy:"fixed",modifiers:[{name:"preventOverflow",options:{rootBoundary:"viewport"}}],children:({placement,ref,style:popperStyle,isReferenceHidden,hasPopperEscaped})=>{const isTestEnvironment=typeof window!=="undefined"&&window.navigator.userAgent.includes("jsdom");const outOfBoundaries=!isTestEnvironment&&(isReferenceHidden||hasPopperEscaped);const combinedStyles={...BASE_CONTAINER_STYLES,...popperStyle,...style,...outOfBoundaries&&OUT_OF_BOUNDARIES_STYLES};return jsx("div",{ref:ref,style:combinedStyles,"data-placement":placement,children:children})}})}),modalHost)};
56
+ const DEFAULT_CALENDAR_GRID_REGION_ARIA_LABEL="Date picker calendar";const DEFAULT_STYLE={background:semanticColor.core.background.base.default,borderRadius:border.radius.radius_040,border:`solid ${border.width.thin} ${semanticColor.core.border.neutral.subtle}`,boxShadow:boxShadow.mid};const BASE_CONTAINER_STYLES={fontFamily:font.family.sans,padding:sizing.size_100};const OUT_OF_BOUNDARIES_STYLES={visibility:"hidden"};const DatePickerOverlay=({children,referenceElement,focusReferenceElement,onClose,dir="ltr",style=DEFAULT_STYLE,calendarGridRegionAriaLabel=DEFAULT_CALENDAR_GRID_REGION_ARIA_LABEL})=>{if(!referenceElement){return null}const placement=dir==="rtl"?"bottom-end":"bottom-start";const modalHost=maybeGetPortalMountedModalHostElement(referenceElement)||document.querySelector("body");if(!modalHost){return null}return createPortal(jsx(FocusManager,{referenceElement:focusReferenceElement??referenceElement,onEndFocused:onClose,children:jsx(Popper,{referenceElement:referenceElement,placement:placement,strategy:"fixed",modifiers:[{name:"preventOverflow",options:{rootBoundary:"viewport"}}],children:({placement,ref,style:popperStyle,isReferenceHidden,hasPopperEscaped})=>{const isTestEnvironment=typeof window!=="undefined"&&window.navigator.userAgent.includes("jsdom");const outOfBoundaries=!isTestEnvironment&&(isReferenceHidden||hasPopperEscaped);const combinedStyles={...BASE_CONTAINER_STYLES,...popperStyle,...style,...outOfBoundaries&&OUT_OF_BOUNDARIES_STYLES};return jsx("div",{"aria-label":calendarGridRegionAriaLabel,ref:ref,role:"region",style:combinedStyles,"data-placement":placement,children:children})}})}),modalHost)};
40
57
 
41
- const customRootStyle={"--rdp-accent-color":semanticColor.core.border.instructive.default};const DatePicker=props=>{const{locale,updateDate,dateFormat,disabled,id,maxDate,minDate,inputAriaLabel,placeholder,selectedDate,style,closeOnSelect=true,resetInvalidValueOnBlur=true,footer}=props;const[showOverlay,setShowOverlay]=React.useState(false);const[currentDate,setCurrentDate]=React.useState(selectedDate);const datePickerInputRef=React.useRef(null);const datePickerRef=React.useRef(null);const refWrapper=React.useRef(null);const skipNextOpenRef=React.useRef(false);const{handleEscapeKeyDown}=useEscapeKeyupCapture();const{displayMonth,setDisplayMonth,displayMonthRef,inputDrivenMonthRef,setDisplayMonthAndRefs}=useDisplayMonth({selectedDate});const open=React.useCallback(()=>{if(skipNextOpenRef.current){skipNextOpenRef.current=false;return}if(!disabled){if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}else {displayMonthRef.current=displayMonthRef.current??displayMonth;}setShowOverlay(true);}},[disabled,displayMonth,displayMonthRef,selectedDate,setDisplayMonthAndRefs,skipNextOpenRef]);const{handleInputChange,clearInputDrivenMonth}=useOverlayMonthFromInput({inputDrivenMonthRef,setDisplayMonth,setDisplayMonthAndRefs,setCurrentDate,updateDate,dateFormat,localeCode:locale?.code});const close=React.useCallback(()=>{clearInputDrivenMonth();if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}else {setDisplayMonthAndRefs(null);}setShowOverlay(false);datePickerInputRef.current?.validateInput?.();},[selectedDate,setDisplayMonthAndRefs,clearInputDrivenMonth,datePickerInputRef]);useCloseOnOutsideClick({refWrapper,datePickerRef,showOverlay,closeOnSelect,close});useSelectedDateSync({selectedDate,setCurrentDate,setDisplayMonthAndRefs});const computedLocale=locale??enUS;const selectedDateValue=currentDate?TemporalLocaleUtils.temporalDateToJsDate(currentDate):undefined;const modifiers=useDatePickerModifiers({selectedDateValue,minDate,maxDate});const formatDateForInput=useFormatDateForInput({dateFormat,locale:computedLocale});const dir=refWrapper.current?.closest("[dir]")?.getAttribute("dir")||"ltr";const handleMonthChange=React.useCallback(newMonth=>{clearInputDrivenMonth();setDisplayMonthAndRefs(newMonth);},[clearInputDrivenMonth,setDisplayMonthAndRefs]);const isLeavingDropdown=e=>{const dayPickerCalendar=datePickerRef.current;if(!dayPickerCalendar){return true}if(e.relatedTarget instanceof Node){return !dayPickerCalendar.contains(e.relatedTarget)}return true};const handleInputBlur=e=>{if(isLeavingDropdown(e)){close();}};const onEscapeCloseOverlay=React.useCallback(()=>{skipNextOpenRef.current=true;close();datePickerInputRef.current?.focus();},[close,skipNextOpenRef,datePickerInputRef]);const handleKeyDown=e=>{if(e.key==="Escape"){if(showOverlay){handleEscapeKeyDown(e,onEscapeCloseOverlay);}}if(e.key==="ArrowDown"&&!showOverlay){e.preventDefault();skipNextOpenRef.current=false;open();}if(e.key==="Enter"){e.preventDefault();if(showOverlay){if(closeOnSelect){close();}}else {skipNextOpenRef.current=false;open();}}};const RootWithEsc=React.useCallback(props=>{const{onKeyDown,rootRef:_,...rest}=props;return jsx("div",{...rest,tabIndex:-1,onKeyDown:e=>{onKeyDown?.(e);if(e.key==="Escape"){handleEscapeKeyDown(e,onEscapeCloseOverlay);}}})},[handleEscapeKeyDown,onEscapeCloseOverlay]);const dayPickerComponents=React.useMemo(()=>({Root:RootWithEsc}),[RootWithEsc]);const handleDayClick=React.useCallback((date,{disabled})=>{if(disabled||!date){return}datePickerInputRef.current?.focus();const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(date);setCurrentDate(wrappedDate);const monthDate=new Date(date);clearInputDrivenMonth();setDisplayMonthAndRefs(monthDate);updateDate(wrappedDate);setShowOverlay(!closeOnSelect);},[updateDate,closeOnSelect,setDisplayMonthAndRefs,datePickerInputRef,clearInputDrivenMonth]);const renderInput=inputModifiers=>{const selectedDateAsValue=formatDateForInput(currentDate);return jsx(DatePickerInput,{onBlur:handleInputBlur,onFocus:open,onClick:open,onChange:handleInputChange,onKeyDown:handleKeyDown,"aria-label":inputAriaLabel,disabled:disabled,id:id,placeholder:placeholder,value:selectedDateAsValue,ref:datePickerInputRef,dateFormat:dateFormat,locale:computedLocale.code,parseDate:TemporalLocaleUtils.parseDateToJsDate,getModifiersForDay:TemporalLocaleUtils.getModifiersForDay,modifiers:inputModifiers,resetInvalidValueOnBlur:resetInvalidValueOnBlur,testId:id&&`${id}-input`})};const maybeRenderFooter=()=>{if(!footer){return null}return jsx(View,{testId:"date-picker-footer",style:styles.footer,children:footer({close})})};const minDateToShow=minDate&&selectedDateValue?Temporal.PlainDate.compare(minDate,currentDate)<0?TemporalLocaleUtils.temporalDateToJsDate(minDate):selectedDateValue:minDate?TemporalLocaleUtils.temporalDateToJsDate(minDate):undefined;const dayPickerEndMonth=React.useMemo(()=>maxDate?TemporalLocaleUtils.temporalDateToJsDate(maxDate):undefined,[maxDate]);const dayPickerStyles=React.useMemo(()=>({root:{...customRootStyle},nav:{width:"auto"}}),[]);const inputDrivenMonth=inputDrivenMonthRef.current;const isInputDriven=inputDrivenMonth!=null;const selectedDateAsJs=selectedDate!=null?TemporalLocaleUtils.temporalDateToJsDate(selectedDate):undefined;const baseMonth=displayMonthRef.current??(showOverlay&&selectedDateAsJs?selectedDateAsJs:undefined)??displayMonth??selectedDateValue??new Date;const firstOfBaseMonth=new Date(baseMonth.getFullYear(),baseMonth.getMonth(),1);const pickerKey=isInputDriven?`input-${inputDrivenMonth.getTime()}`:`picker-${baseMonth.getTime()}`;const inputDrivenMonthMs=inputDrivenMonth?.getTime();const firstOfBaseMonthMs=firstOfBaseMonth.getTime();const dayPickerMonthProps=React.useMemo(()=>{if(isInputDriven&&inputDrivenMonthMs!=null){const d=new Date(inputDrivenMonthMs);return {month:new Date(d.getFullYear(),d.getMonth(),1),onMonthChange:handleMonthChange}}return {defaultMonth:new Date(firstOfBaseMonthMs)}},[isInputDriven,inputDrivenMonthMs,firstOfBaseMonthMs,handleMonthChange]);return jsxs(View,{style:style,ref:refWrapper,children:[renderInput(modifiers),showOverlay&&jsx(DatePickerOverlay,{referenceElement:datePickerInputRef.current,onClose:close,dir:dir==="rtl"?"rtl":"ltr",children:jsxs(View,{ref:datePickerRef,children:[jsx(DayPicker,{mode:"single",selected:selectedDateValue,...dayPickerMonthProps,startMonth:minDateToShow??undefined,endMonth:dayPickerEndMonth,modifiers:modifiers,onDayClick:handleDayClick,components:dayPickerComponents,locale:computedLocale,dir:dir,styles:dayPickerStyles},pickerKey),maybeRenderFooter()]})})]})};DatePicker.defaultProps={closeOnSelect:true};const styles=StyleSheet.create({footer:{margin:sizing.size_120,marginBlockStart:0}});
58
+ const customRootStyle={"--rdp-accent-color":semanticColor.core.border.instructive.default,"--rdp-today-color":semanticColor.core.foreground.instructive.default};const dayButtonFocusStyles=StyleSheet.create({focus:focusStyles.focus});const dayPickerClassNames={[UI.DayButton]:`${getDefaultClassNames()[UI.DayButton]} ${css(dayButtonFocusStyles.focus)}`};const DatePicker=props=>{const{locale,updateDate,dateFormat,disabled,id,maxDate,minDate,inputAriaLabel,calendarButtonAriaLabel,calendarGridRegionAriaLabel,placeholder,selectedDate,style,closeOnSelect=true,resetInvalidValueOnBlur=true,footer}=props;const[showOverlay,setShowOverlay]=React.useState(false);const[currentDate,setCurrentDate]=React.useState(selectedDate);const datePickerInputRef=React.useRef(null);const calendarButtonRef=React.useRef(null);const datePickerRef=React.useRef(null);const refWrapper=React.useRef(null);const skipNextOpenRef=React.useRef(false);const movingFocusIntoOverlayRef=React.useRef(false);const{handleEscapeKeyDown}=useEscapeKeyupCapture();const{displayMonth,setDisplayMonth,displayMonthRef,inputDrivenMonthRef,setDisplayMonthAndRefs}=useDisplayMonth({selectedDate});const open=React.useCallback(()=>{if(skipNextOpenRef.current){skipNextOpenRef.current=false;return}if(!disabled){if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}else {displayMonthRef.current=displayMonthRef.current??displayMonth;}setShowOverlay(true);}},[disabled,displayMonth,displayMonthRef,selectedDate,setDisplayMonthAndRefs,skipNextOpenRef]);const{handleInputChange,clearInputDrivenMonth}=useOverlayMonthFromInput({inputDrivenMonthRef,setDisplayMonth,setDisplayMonthAndRefs,setCurrentDate,updateDate,dateFormat,localeCode:locale?.code});const close=React.useCallback(()=>{clearInputDrivenMonth();if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}else {setDisplayMonthAndRefs(null);}setShowOverlay(false);datePickerInputRef.current?.validateInput?.();},[selectedDate,setDisplayMonthAndRefs,clearInputDrivenMonth,datePickerInputRef]);const handleToggleOverlay=React.useCallback(()=>{if(disabled){return}if(showOverlay){close();}else {skipNextOpenRef.current=false;open();}},[disabled,showOverlay,close,open,skipNextOpenRef]);const focusIntoOverlay=React.useCallback(()=>{const overlayRoot=datePickerRef.current;if(!overlayRoot){return}const grid=overlayRoot.querySelector('[role="grid"]');const rovingTarget=grid?.querySelector('[tabindex="0"]');const target=rovingTarget??findFocusableNodes(grid??overlayRoot)[0];if(target){movingFocusIntoOverlayRef.current=true;target.focus();}},[datePickerRef]);useCloseOnOutsideClick({refWrapper,datePickerRef,showOverlay,closeOnSelect,close});useSelectedDateSync({selectedDate,setCurrentDate,setDisplayMonthAndRefs});const computedLocale=locale??enUS;const selectedDateValue=currentDate?TemporalLocaleUtils.temporalDateToJsDate(currentDate):undefined;const modifiers=useDatePickerModifiers({selectedDateValue,minDate,maxDate});const formatDateForInput=useFormatDateForInput({dateFormat,locale:computedLocale});const dir=refWrapper.current?.closest("[dir]")?.getAttribute("dir")||"ltr";const handleMonthChange=React.useCallback(newMonth=>{clearInputDrivenMonth();setDisplayMonthAndRefs(newMonth);},[clearInputDrivenMonth,setDisplayMonthAndRefs]);const isLeavingDropdown=e=>{const dayPickerCalendar=datePickerRef.current;if(!(e.relatedTarget instanceof Node)){return true}if(dayPickerCalendar?.contains(e.relatedTarget)){return false}const calendarButton=calendarButtonRef.current;if(calendarButton?.contains(e.relatedTarget)){return false}return true};const handleInputBlur=e=>{if(movingFocusIntoOverlayRef.current){movingFocusIntoOverlayRef.current=false;return}if(isLeavingDropdown(e)){close();}};const onEscapeCloseOverlay=React.useCallback(()=>{skipNextOpenRef.current=true;close();calendarButtonRef.current?.focus();},[close,skipNextOpenRef]);const handleKeyDown=e=>{if(!showOverlay){return}if(e.key==="Escape"){handleEscapeKeyDown(e,onEscapeCloseOverlay);}if(e.key==="ArrowDown"){e.preventDefault();focusIntoOverlay();}if(e.key==="Enter"){e.preventDefault();if(closeOnSelect){close();}}};const handleCalendarButtonKeyDown=React.useCallback(e=>{if(e.key==="ArrowDown"&&showOverlay){e.preventDefault();focusIntoOverlay();}else if(e.key==="Escape"&&showOverlay){handleEscapeKeyDown(e,onEscapeCloseOverlay);}},[showOverlay,focusIntoOverlay,handleEscapeKeyDown,onEscapeCloseOverlay]);const RootWithEsc=React.useCallback(props=>{const{onKeyDown,rootRef:_,...rest}=props;return jsx("div",{...rest,tabIndex:-1,onKeyDown:e=>{onKeyDown?.(e);if(e.key==="Escape"){handleEscapeKeyDown(e,onEscapeCloseOverlay);}}})},[handleEscapeKeyDown,onEscapeCloseOverlay]);const dayPickerComponents=React.useMemo(()=>({Root:RootWithEsc}),[RootWithEsc]);const handleDayClick=React.useCallback((date,{disabled})=>{if(disabled||!date){return}datePickerInputRef.current?.focus();const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(date);setCurrentDate(wrappedDate);const monthDate=new Date(date);clearInputDrivenMonth();setDisplayMonthAndRefs(monthDate);updateDate(wrappedDate);setShowOverlay(!closeOnSelect);},[updateDate,closeOnSelect,setDisplayMonthAndRefs,datePickerInputRef,clearInputDrivenMonth]);const renderInput=inputModifiers=>{const selectedDateAsValue=formatDateForInput(currentDate);return jsx(DatePickerInput,{onBlur:handleInputBlur,onChange:handleInputChange,onKeyDown:handleKeyDown,"aria-label":inputAriaLabel,disabled:disabled,id:id,placeholder:placeholder,value:selectedDateAsValue,ref:datePickerInputRef,dateFormat:dateFormat,locale:computedLocale.code,parseDate:TemporalLocaleUtils.parseDateToJsDate,getModifiersForDay:TemporalLocaleUtils.getModifiersForDay,modifiers:inputModifiers,resetInvalidValueOnBlur:resetInvalidValueOnBlur,testId:id&&`${id}-input`,expanded:showOverlay,onToggleOverlay:handleToggleOverlay,onCalendarButtonKeyDown:handleCalendarButtonKeyDown,calendarButtonAriaLabel:calendarButtonAriaLabel,calendarButtonRef:calendarButtonRef})};const maybeRenderFooter=()=>{if(!footer){return null}return jsx(View,{testId:"date-picker-footer",style:styles.footer,children:footer({close})})};const minDateToShow=minDate&&selectedDateValue?Temporal.PlainDate.compare(minDate,currentDate)<0?TemporalLocaleUtils.temporalDateToJsDate(minDate):selectedDateValue:minDate?TemporalLocaleUtils.temporalDateToJsDate(minDate):undefined;const dayPickerEndMonth=React.useMemo(()=>maxDate?TemporalLocaleUtils.temporalDateToJsDate(maxDate):undefined,[maxDate]);const dayPickerStyles=React.useMemo(()=>({root:{...customRootStyle},nav:{width:"auto"}}),[]);const dayPickerModifiersStyles=React.useMemo(()=>({selected:{fontWeight:font.weight.medium},today:{fontWeight:font.weight.bold}}),[]);const inputDrivenMonth=inputDrivenMonthRef.current;const isInputDriven=inputDrivenMonth!=null;const selectedDateAsJs=selectedDate!=null?TemporalLocaleUtils.temporalDateToJsDate(selectedDate):undefined;const baseMonth=displayMonthRef.current??(showOverlay&&selectedDateAsJs?selectedDateAsJs:undefined)??displayMonth??selectedDateValue??new Date;const firstOfBaseMonth=new Date(baseMonth.getFullYear(),baseMonth.getMonth(),1);const pickerKey=isInputDriven?`input-${inputDrivenMonth.getTime()}`:`picker-${baseMonth.getTime()}`;const inputDrivenMonthMs=inputDrivenMonth?.getTime();const firstOfBaseMonthMs=firstOfBaseMonth.getTime();const dayPickerMonthProps=React.useMemo(()=>{if(isInputDriven&&inputDrivenMonthMs!=null){const d=new Date(inputDrivenMonthMs);return {month:new Date(d.getFullYear(),d.getMonth(),1),onMonthChange:handleMonthChange}}return {defaultMonth:new Date(firstOfBaseMonthMs)}},[isInputDriven,inputDrivenMonthMs,firstOfBaseMonthMs,handleMonthChange]);return jsxs(View,{style:style,ref:refWrapper,children:[renderInput(modifiers),showOverlay&&jsx(DatePickerOverlay,{referenceElement:datePickerInputRef.current,focusReferenceElement:calendarButtonRef.current??undefined,onClose:close,dir:dir==="rtl"?"rtl":"ltr",calendarGridRegionAriaLabel:calendarGridRegionAriaLabel,children:jsxs(View,{ref:datePickerRef,children:[jsx(DayPicker,{mode:"single",selected:selectedDateValue,...dayPickerMonthProps,startMonth:minDateToShow??undefined,endMonth:dayPickerEndMonth,modifiers:modifiers,onDayClick:handleDayClick,components:dayPickerComponents,locale:computedLocale,dir:dir,styles:dayPickerStyles,modifiersStyles:dayPickerModifiersStyles,classNames:dayPickerClassNames},pickerKey),maybeRenderFooter()]})})]})};DatePicker.defaultProps={closeOnSelect:true};const styles=StyleSheet.create({footer:{margin:sizing.size_120,marginBlockStart:0}});
42
59
 
43
60
  export { DatePicker, TemporalLocaleUtils };
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ type Params = {
3
+ value: string | null | undefined;
4
+ dateFormat?: string;
5
+ locale: string;
6
+ parseDate?: (value: string | Date, format: string | null | undefined, locale?: string | null | undefined) => Date | null | undefined;
7
+ handleChange: (newValue: string) => void;
8
+ innerRef: React.RefObject<HTMLInputElement | null>;
9
+ };
10
+ /**
11
+ * ArrowUp/ArrowDown increment/decrement the day/month/year segment under the
12
+ * caret; ArrowLeft/ArrowRight move the selection to the adjacent segment.
13
+ * Matches native `<input type="date">`/`type="time">` behavior. Only handles
14
+ * numeric date formats (see `getDateSegments`). ArrowUp/ArrowDown also
15
+ * require the current value to be a valid, parseable date.
16
+ *
17
+ * @param params.value - The current text shown in the input.
18
+ * @param params.dateFormat - The `dateFormat` prop value used to render `value`.
19
+ * @param params.locale - The locale used to render `value`.
20
+ * @param params.parseDate - Parses `value` into a `Date`, same as the parent
21
+ * `DatePicker`'s `parseDate` prop.
22
+ * @param params.handleChange - Called with the newly formatted value, same
23
+ * as if the user had typed it.
24
+ * @param params.innerRef - A ref to the underlying `<input>`, used to
25
+ * reposition the caret after an edit.
26
+ * @returns A keydown handler that returns `true` if it handled the key (the
27
+ * caller should stop there) or `false` otherwise.
28
+ */
29
+ export declare function useDateSegmentArrowKeys({ value, dateFormat, locale, parseDate, handleChange, innerRef, }: Params): (e: React.KeyboardEvent<HTMLInputElement>) => boolean;
30
+ export {};
package/dist/index.js CHANGED
@@ -10,8 +10,9 @@ var reactDayPicker = require('react-day-picker');
10
10
  var locale = require('react-day-picker/locale');
11
11
  var wonderBlocksCore = require('@khanacademy/wonder-blocks-core');
12
12
  var wonderBlocksTokens = require('@khanacademy/wonder-blocks-tokens');
13
+ var wonderBlocksStyles = require('@khanacademy/wonder-blocks-styles');
13
14
  var wonderBlocksForm = require('@khanacademy/wonder-blocks-form');
14
- var wonderBlocksIcon = require('@khanacademy/wonder-blocks-icon');
15
+ var IconButton = require('@khanacademy/wonder-blocks-icon-button');
15
16
  var calendarIcon = require('@phosphor-icons/core/bold/calendar-blank-bold.svg');
16
17
  var reactDom = require('react-dom');
17
18
  var reactPopper = require('react-popper');
@@ -39,11 +40,14 @@ function _interopNamespace(e) {
39
40
  }
40
41
 
41
42
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
43
+ var IconButton__default = /*#__PURE__*/_interopDefaultLegacy(IconButton);
42
44
  var calendarIcon__default = /*#__PURE__*/_interopDefaultLegacy(calendarIcon);
43
45
 
44
46
  function useCloseOnOutsideClick({refWrapper,datePickerRef,showOverlay,closeOnSelect,close}){React__namespace.useEffect(()=>{const handleClick=e=>{const target=e.target;const thisElement=refWrapper.current;const dayPickerCalendar=datePickerRef.current;const isElement=target instanceof Element;const inThisElement=isElement&&thisElement?.contains(target);const inCalendar=isElement&&dayPickerCalendar?.contains(target);const inPortal=isElement&&target.closest("[data-placement]")!==null;const shouldClose=showOverlay&&closeOnSelect&&thisElement&&!inThisElement&&!inCalendar&&!inPortal;if(shouldClose){close();}};document.addEventListener("mouseup",handleClick);return ()=>{document.removeEventListener("mouseup",handleClick);}},[refWrapper,datePickerRef,showOverlay,closeOnSelect,close]);}
45
47
 
46
- const enUSLocaleCode="en-US";const TEXT_FORMAT_STRINGS=["LL","MMMM D, YYYY","MMM D, YYYY"];function isTextFormatDate(formatString){return formatString!=null&&TEXT_FORMAT_STRINGS.includes(formatString)}function normalizeDateStringForComparison(s){return s.trim().toLowerCase().replace(/\s+/g," ").replace(/[,.\u202f]/g," ").replace(/\s+/g," ").trim()}function formatDate(date,formatString,locale){const localeCode=typeof locale==="string"?locale:locale?.code??enUSLocaleCode;if(!formatString){return date.toLocaleString(localeCode,{year:"numeric",month:"numeric",day:"numeric"})}if(formatString==="L"){return date.toLocaleString(localeCode,{year:"numeric",month:"numeric",day:"numeric"})}if(formatString==="LL"){return date.toLocaleString(localeCode,{dateStyle:"long"})}if(formatString==="dateStyle:short"||formatString==="dateStyle:medium"||formatString==="dateStyle:long"||formatString==="dateStyle:full"){const style=formatString.split(":")[1];return date.toLocaleString(localeCode,{dateStyle:style})}if(formatString==="YYYY-MM-DD"){return date.toString()}if(formatString==="MMMM D, YYYY"||formatString==="MMM D, YYYY"){try{const monthFormat=formatString==="MMMM D, YYYY"?"long":"short";const monthName=date.toLocaleString(localeCode,{month:monthFormat});return `${monthName} ${date.day}, ${date.year}`}catch(error){return date.toString()}}if(formatString==="MM/DD/YYYY"||formatString==="M/D/YYYY"||formatString==="DD/MM/YYYY"){const shouldPad=formatString.includes("MM")||formatString.includes("DD");const month=shouldPad?String(date.month).padStart(2,"0"):String(date.month);const day=shouldPad?String(date.day).padStart(2,"0"):String(date.day);return `${month}/${day}/${date.year}`}try{const options=getOptionsForFormat(formatString);return date.toLocaleString(localeCode,options)}catch(error){console.warn(`Failed to format date with format "${formatString}" and locale "${localeCode}". Falling back to ISO format.`,error);return date.toString()}}function parseDate(str,formatString,locale){if(!str||str.trim()===""){return undefined}try{return temporalPolyfill.Temporal.PlainDate.from(str)}catch{}const format=formatString||"L";try{const parsed=parseWithFormat(str,format,locale);if(parsed){return parsed}}catch{}return undefined}const getModifiersForDay=(day,modifiers)=>{const matchedModifiers=[];for(const[modifierName,matcher]of Object.entries(modifiers)){if(!matcher){continue}if(typeof matcher==="function"){if(matcher(day)){matchedModifiers.push(modifierName);}}else if(matcher instanceof Date){if(day.getFullYear()===matcher.getFullYear()&&day.getMonth()===matcher.getMonth()&&day.getDate()===matcher.getDate()){matchedModifiers.push(modifierName);}}}return matchedModifiers};function temporalDateToJsDate(date){return new Date(date.year,date.month-1,date.day)}function jsDateToTemporalDate(date){return temporalPolyfill.Temporal.PlainDate.from({year:date.getFullYear(),month:date.getMonth()+1,day:date.getDate()})}function parseDateToJsDate(value,formatString,locale){if(value instanceof Date){return value}const temporalDate=parseDate(value,formatString,locale||undefined);if(temporalDate){const formatted=formatDate(temporalDate,formatString,locale||undefined);if(formatted===value){return temporalDateToJsDate(temporalDate)}const normalizedFormatted=formatted.replace(/\b0(\d)\b/g,"$1");const normalizedValue=value.replace(/\b0(\d)\b/g,"$1");if(normalizedFormatted===normalizedValue){return temporalDateToJsDate(temporalDate)}if(value===temporalDate.toString()){return temporalDateToJsDate(temporalDate)}const isTextFormat=isTextFormatDate(formatString);if(isTextFormat){const normalizedFormatted=formatted.replace(/\s+/g," ").trim().toLowerCase();const normalizedValue=value.replace(/\s+/g," ").trim().toLowerCase();if(normalizedFormatted===normalizedValue){return temporalDateToJsDate(temporalDate)}if(normalizedFormatted.startsWith(normalizedValue)||normalizedValue.startsWith(normalizedFormatted)){return temporalDateToJsDate(temporalDate)}return undefined}return undefined}return undefined}function getMonths(locale){const format=new Intl.DateTimeFormat(locale||enUSLocaleCode,{month:"long"});const formatShort=new Intl.DateTimeFormat(locale||enUSLocaleCode,{month:"short"});const months=[];for(let i=0;i<12;i++){const date=new Date(2021,i,15);months.push([format.format(date),formatShort.format(date)]);}return months}function getOptionsForFormat(format){const options={};if(format.includes("YYYY")){options.year="numeric";}else if(format.includes("YY")){options.year="2-digit";}if(format.includes("MMMM")){options.month="long";}else if(format.includes("MMM")){options.month="short";}else if(format.includes("MM")){options.month="2-digit";}else if(format.includes("M")){options.month="numeric";}if(format.includes("DD")){options.day="2-digit";}else if(format.includes("D")){options.day="numeric";}if(format.includes("dddd")){options.weekday="long";}else if(format.includes("ddd")){options.weekday="short";}return options}function parseLocaleAwareDate(str,locale){const localeStr=locale||enUSLocaleCode;const cleaned=str.trim();if(!cleaned){return undefined}try{const formatter=new Intl.DateTimeFormat(localeStr,{dateStyle:"short"});const testDate=new Date(2020,0,15);const parts=formatter.formatToParts(testDate);const pattern=parts.map(p=>({type:p.type,value:p.value}));const separators=pattern.filter(p=>p.type==="literal").map(p=>p.value);const inputParts=cleaned.split(new RegExp(`[${separators.map(s=>`\\${s}`).join("")}]`));if(inputParts.length!==3){throw new Error("Not a numeric date format")}const dateComponents={};let partIndex=0;for(const patternPart of pattern){if(patternPart.type==="literal"){continue}const value=parseInt(inputParts[partIndex],10);if(isNaN(value)){throw new Error("Not a numeric date format")}dateComponents[patternPart.type]=value;partIndex++;}if(!dateComponents.year||!dateComponents.month||!dateComponents.day||dateComponents.month<1||dateComponents.month>12||dateComponents.day<1||dateComponents.day>31||dateComponents.year<1e3||dateComponents.year>9999){throw new Error("Invalid date range")}return temporalPolyfill.Temporal.PlainDate.from({year:dateComponents.year,month:dateComponents.month,day:dateComponents.day})}catch{return parseTextDate(cleaned,localeStr)}}function parseTextDate(str,locale){try{const months=getMonths(locale);const numbers=str.match(/\d+/g)??[];const lowerStr=str.toLowerCase();let monthIndex=-1;for(let i=0;i<months.length;i++){const[longName,shortName]=months[i];if(lowerStr.includes(longName.toLowerCase())||lowerStr.includes(shortName.toLowerCase())){monthIndex=i+1;break}}if(monthIndex===-1){return undefined}const now=temporalPolyfill.Temporal.Now.plainDateISO();if(numbers.length>=2){const n1=numbers[0];const n2=numbers[1];if(n1===undefined||n2===undefined){return undefined}let day;let year;const num1=parseInt(n1,10);const num2=parseInt(n2,10);if(num1>31){year=num1;day=num2;}else if(num2>31||num2.toString().length===4){day=num1;year=num2;}else {day=num1;year=num2;}if(day<1||day>31||year<1e3||year>9999){return undefined}return temporalPolyfill.Temporal.PlainDate.from({year,month:monthIndex,day})}if(numbers.length===1){const n0=numbers[0];if(n0===undefined){return undefined}const n=parseInt(n0,10);const isYear=n>=1e3&&n<=9999;const year=isYear?n:now.year;const day=isYear?1:n>=1&&n<=31?n:1;return temporalPolyfill.Temporal.PlainDate.from({year,month:monthIndex,day})}return temporalPolyfill.Temporal.PlainDate.from({year:now.year,month:monthIndex,day:1})}catch{return undefined}}function parseWithFormat(str,format,locale){if(!format){return undefined}if(format==="L"||format==="LL"||format.startsWith("dateStyle:")){return parseLocaleAwareDate(str,locale)}if(format==="M/D/YYYY"||format==="M-D-YYYY"||format==="MM/DD/YYYY"||format==="MM-DD-YYYY"){const separator=format.includes("/")?"/":"-";const parts=str.split(separator);if(parts.length===3){const month=parseInt(parts[0],10);const day=parseInt(parts[1],10);const year=parseInt(parts[2],10);if(isNaN(month)||isNaN(day)||isNaN(year)||month<1||month>12||day<1||day>31||year<1e3||year>9999){return undefined}try{return temporalPolyfill.Temporal.PlainDate.from({year,month,day})}catch{return undefined}}}if(format==="MMMM D, YYYY"||format==="MMM D, YYYY"){try{const cleaned=str.trim();const localeStr=locale||enUSLocaleCode;const parts=cleaned.split(",");if(parts.length===2){const[monthDay,yearStr]=parts;const year=parseInt(yearStr.trim(),10);if(year<1e3||year>9999){return undefined}const months=getMonths(localeStr).map(m=>m[0]);const monthDayParts=monthDay.trim().split(" ");if(monthDayParts.length===2){const monthName=monthDayParts[0];const day=parseInt(monthDayParts[1],10);const monthIndex=months.findIndex(m=>m.toLowerCase()===monthName.toLowerCase()||m.slice(0,3).toLowerCase()===monthName.toLowerCase());if(monthIndex>=0&&!isNaN(day)&&!isNaN(year)){return temporalPolyfill.Temporal.PlainDate.from({year,month:monthIndex+1,day})}}}return parseTextDate(cleaned,localeStr)}catch{return undefined}}return undefined}const startOfIsoWeek=date=>{const dayOfWeek=date.dayOfWeek;return date.subtract({days:dayOfWeek-1})};const startOfDay=date=>{const result=new Date(date);result.setHours(0,0,0,0);return result};const endOfDay=date=>{const result=new Date(date);result.setHours(23,59,59,999);return result};const TemporalLocaleUtils={formatDate,isTextFormatDate,normalizeDateStringForComparison,parseDate,parseDateToJsDate,startOfIsoWeek,startOfDay,endOfDay,temporalDateToJsDate,jsDateToTemporalDate,getModifiersForDay};
48
+ const enUSLocaleCode="en-US";const TEXT_FORMAT_STRINGS=["LL","MMMM D, YYYY","MMM D, YYYY"];const SLASH_FORMAT_STRINGS=new Set(["MM/DD/YYYY","M/D/YYYY","DD/MM/YYYY"]);const INTL_TYPE_TO_SEGMENT_TYPE={day:"day",month:"month",year:"year"};
49
+
50
+ function isTextFormatDate(formatString){return formatString!=null&&TEXT_FORMAT_STRINGS.includes(formatString)}function normalizeDateStringForComparison(s){return s.trim().toLowerCase().replace(/\s+/g," ").replace(/[,.\u202f]/g," ").replace(/\s+/g," ").trim()}function formatDate(date,formatString,locale){const localeCode=typeof locale==="string"?locale:locale?.code??enUSLocaleCode;if(!formatString){return date.toLocaleString(localeCode,{year:"numeric",month:"numeric",day:"numeric"})}if(formatString==="L"){return date.toLocaleString(localeCode,{year:"numeric",month:"numeric",day:"numeric"})}if(formatString==="LL"){return date.toLocaleString(localeCode,{dateStyle:"long"})}if(formatString==="dateStyle:short"||formatString==="dateStyle:medium"||formatString==="dateStyle:long"||formatString==="dateStyle:full"){const style=formatString.split(":")[1];return date.toLocaleString(localeCode,{dateStyle:style})}if(formatString==="YYYY-MM-DD"){return date.toString()}if(formatString==="MMMM D, YYYY"||formatString==="MMM D, YYYY"){try{const monthFormat=formatString==="MMMM D, YYYY"?"long":"short";const monthName=date.toLocaleString(localeCode,{month:monthFormat});return `${monthName} ${date.day}, ${date.year}`}catch(error){return date.toString()}}if(formatString==="MM/DD/YYYY"||formatString==="M/D/YYYY"||formatString==="DD/MM/YYYY"){const shouldPad=formatString.includes("MM")||formatString.includes("DD");const month=shouldPad?String(date.month).padStart(2,"0"):String(date.month);const day=shouldPad?String(date.day).padStart(2,"0"):String(date.day);return `${month}/${day}/${date.year}`}try{const options=getOptionsForFormat(formatString);return date.toLocaleString(localeCode,options)}catch(error){console.warn(`Failed to format date with format "${formatString}" and locale "${localeCode}". Falling back to ISO format.`,error);return date.toString()}}function parseDate(str,formatString,locale){if(!str||str.trim()===""){return undefined}try{return temporalPolyfill.Temporal.PlainDate.from(str)}catch{}const format=formatString||"L";try{const parsed=parseWithFormat(str,format,locale);if(parsed){return parsed}}catch{}return undefined}const getModifiersForDay=(day,modifiers)=>{const matchedModifiers=[];for(const[modifierName,matcher]of Object.entries(modifiers)){if(!matcher){continue}if(typeof matcher==="function"){if(matcher(day)){matchedModifiers.push(modifierName);}}else if(matcher instanceof Date){if(day.getFullYear()===matcher.getFullYear()&&day.getMonth()===matcher.getMonth()&&day.getDate()===matcher.getDate()){matchedModifiers.push(modifierName);}}}return matchedModifiers};function temporalDateToJsDate(date){return new Date(date.year,date.month-1,date.day)}function jsDateToTemporalDate(date){return temporalPolyfill.Temporal.PlainDate.from({year:date.getFullYear(),month:date.getMonth()+1,day:date.getDate()})}function parseDateToJsDate(value,formatString,locale){if(value instanceof Date){return value}const temporalDate=parseDate(value,formatString,locale||undefined);if(temporalDate){const formatted=formatDate(temporalDate,formatString,locale||undefined);if(formatted===value){return temporalDateToJsDate(temporalDate)}const normalizedFormatted=formatted.replace(/\b0(\d)\b/g,"$1");const normalizedValue=value.replace(/\b0(\d)\b/g,"$1");if(normalizedFormatted===normalizedValue){return temporalDateToJsDate(temporalDate)}if(value===temporalDate.toString()){return temporalDateToJsDate(temporalDate)}const isTextFormat=isTextFormatDate(formatString);if(isTextFormat){const normalizedFormatted=formatted.replace(/\s+/g," ").trim().toLowerCase();const normalizedValue=value.replace(/\s+/g," ").trim().toLowerCase();if(normalizedFormatted===normalizedValue){return temporalDateToJsDate(temporalDate)}if(normalizedFormatted.startsWith(normalizedValue)||normalizedValue.startsWith(normalizedFormatted)){return temporalDateToJsDate(temporalDate)}return undefined}return undefined}return undefined}function getMonths(locale){const format=new Intl.DateTimeFormat(locale||enUSLocaleCode,{month:"long"});const formatShort=new Intl.DateTimeFormat(locale||enUSLocaleCode,{month:"short"});const months=[];for(let i=0;i<12;i++){const date=new Date(2021,i,15);months.push([format.format(date),formatShort.format(date)]);}return months}function getOptionsForFormat(format){const options={};if(format.includes("YYYY")){options.year="numeric";}else if(format.includes("YY")){options.year="2-digit";}if(format.includes("MMMM")){options.month="long";}else if(format.includes("MMM")){options.month="short";}else if(format.includes("MM")){options.month="2-digit";}else if(format.includes("M")){options.month="numeric";}if(format.includes("DD")){options.day="2-digit";}else if(format.includes("D")){options.day="numeric";}if(format.includes("dddd")){options.weekday="long";}else if(format.includes("ddd")){options.weekday="short";}return options}function buildNumericDatePattern(localeStr,format){const detectionOptions=format&&format.startsWith("dateStyle:")?{dateStyle:format.slice("dateStyle:".length)}:{year:"numeric",month:"numeric",day:"numeric"};const formatter=new Intl.DateTimeFormat(localeStr,detectionOptions);const testDate=new Date(2020,0,15);const pattern=formatter.formatToParts(testDate).map(p=>({type:p.type,value:p.value}));const numericIndexes=pattern.map((p,i)=>p.type!=="literal"&&/^\d+$/.test(p.value)?i:-1).filter(i=>i!==-1);if(numericIndexes.length!==3){return null}const[firstNumericIndex,,lastNumericIndex]=numericIndexes;const escapeLiteral=value=>value.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/[ \u202f]/g,"[ \\u202f]");const partOrder=[];let core="";let leadingLiteral="";let trailingLiteral="";pattern.forEach((patternPart,i)=>{if(patternPart.type!=="literal"){core+="(\\d+)";partOrder.push(patternPart.type);return}const escaped=escapeLiteral(patternPart.value);if(i<firstNumericIndex){leadingLiteral+=escaped;}else if(i>lastNumericIndex){trailingLiteral+=escaped;}else {core+=escaped;}});const regexSource="^"+(leadingLiteral?`(?:${leadingLiteral})?`:"")+core+(trailingLiteral?`(?:${trailingLiteral})?`:"")+"$";return {regex:new RegExp(regexSource),partOrder}}function parseLocaleAwareDate(str,format,locale){const localeStr=locale||enUSLocaleCode;const cleaned=str.trim();if(!cleaned){return undefined}try{const pattern=buildNumericDatePattern(localeStr,format);if(!pattern){throw new Error("Not a numeric date format")}const match=cleaned.match(pattern.regex);if(!match){throw new Error("Not a numeric date format")}const dateComponents={};pattern.partOrder.forEach((type,i)=>{dateComponents[type]=parseInt(match[i+1],10);});if(!dateComponents.year||!dateComponents.month||!dateComponents.day||dateComponents.month<1||dateComponents.month>12||dateComponents.day<1||dateComponents.day>31||dateComponents.year<1e3||dateComponents.year>9999){throw new Error("Invalid date range")}return temporalPolyfill.Temporal.PlainDate.from({year:dateComponents.year,month:dateComponents.month,day:dateComponents.day})}catch{return parseTextDate(cleaned,localeStr)}}function parseTextDate(str,locale){try{const months=getMonths(locale);const numbers=str.match(/\d+/g)??[];const lowerStr=str.toLowerCase();let monthIndex=-1;for(let i=0;i<months.length;i++){const[longName,shortName]=months[i];if(lowerStr.includes(longName.toLowerCase())||lowerStr.includes(shortName.toLowerCase())){monthIndex=i+1;break}}if(monthIndex===-1){return undefined}const now=temporalPolyfill.Temporal.Now.plainDateISO();if(numbers.length>=2){const n1=numbers[0];const n2=numbers[1];if(n1===undefined||n2===undefined){return undefined}let day;let year;const num1=parseInt(n1,10);const num2=parseInt(n2,10);if(num1>31){year=num1;day=num2;}else if(num2>31||num2.toString().length===4){day=num1;year=num2;}else {day=num1;year=num2;}if(day<1||day>31||year<1e3||year>9999){return undefined}return temporalPolyfill.Temporal.PlainDate.from({year,month:monthIndex,day})}if(numbers.length===1){const n0=numbers[0];if(n0===undefined){return undefined}const n=parseInt(n0,10);const isYear=n>=1e3&&n<=9999;const year=isYear?n:now.year;const day=isYear?1:n>=1&&n<=31?n:1;return temporalPolyfill.Temporal.PlainDate.from({year,month:monthIndex,day})}return temporalPolyfill.Temporal.PlainDate.from({year:now.year,month:monthIndex,day:1})}catch{return undefined}}function parseWithFormat(str,format,locale){if(!format){return undefined}if(format==="L"||format==="LL"||format.startsWith("dateStyle:")){return parseLocaleAwareDate(str,format,locale)}if(format==="M/D/YYYY"||format==="M-D-YYYY"||format==="MM/DD/YYYY"||format==="MM-DD-YYYY"){const separator=format.includes("/")?"/":"-";const parts=str.split(separator);if(parts.length===3){const month=parseInt(parts[0],10);const day=parseInt(parts[1],10);const year=parseInt(parts[2],10);if(isNaN(month)||isNaN(day)||isNaN(year)||month<1||month>12||day<1||day>31||year<1e3||year>9999){return undefined}try{return temporalPolyfill.Temporal.PlainDate.from({year,month,day})}catch{return undefined}}}if(format==="MMMM D, YYYY"||format==="MMM D, YYYY"){try{const cleaned=str.trim();const localeStr=locale||enUSLocaleCode;const parts=cleaned.split(",");if(parts.length===2){const[monthDay,yearStr]=parts;const year=parseInt(yearStr.trim(),10);if(year<1e3||year>9999){return undefined}const months=getMonths(localeStr).map(m=>m[0]);const monthDayParts=monthDay.trim().split(" ");if(monthDayParts.length===2){const monthName=monthDayParts[0];const day=parseInt(monthDayParts[1],10);const monthIndex=months.findIndex(m=>m.toLowerCase()===monthName.toLowerCase()||m.slice(0,3).toLowerCase()===monthName.toLowerCase());if(monthIndex>=0&&!isNaN(day)&&!isNaN(year)){return temporalPolyfill.Temporal.PlainDate.from({year,month:monthIndex+1,day})}}}return parseTextDate(cleaned,localeStr)}catch{return undefined}}return undefined}const startOfIsoWeek=date=>{const dayOfWeek=date.dayOfWeek;return date.subtract({days:dayOfWeek-1})};const startOfDay=date=>{const result=new Date(date);result.setHours(0,0,0,0);return result};const endOfDay=date=>{const result=new Date(date);result.setHours(23,59,59,999);return result};const TemporalLocaleUtils={formatDate,isTextFormatDate,normalizeDateStringForComparison,parseDate,parseDateToJsDate,startOfIsoWeek,startOfDay,endOfDay,temporalDateToJsDate,jsDateToTemporalDate,getModifiersForDay};
47
51
 
48
52
  function useDatePickerModifiers({selectedDateValue,minDate,maxDate}){return React__namespace.useMemo(()=>({selected:selectedDateValue,disabled:date=>{const temporalDate=TemporalLocaleUtils.jsDateToTemporalDate(date);return minDate&&temporalPolyfill.Temporal.PlainDate.compare(temporalDate,minDate)<0||maxDate&&temporalPolyfill.Temporal.PlainDate.compare(temporalDate,maxDate)>0||false}}),[selectedDateValue,minDate,maxDate])}
49
53
 
@@ -59,13 +63,27 @@ function useOverlayMonthFromInput({inputDrivenMonthRef,setDisplayMonth,setDispla
59
63
 
60
64
  function useSelectedDateSync({selectedDate,setCurrentDate,setDisplayMonthAndRefs}){const prevSelectedDateRef=React__namespace.useRef(null);React__namespace.useEffect(()=>{setCurrentDate(selectedDate);const key=selectedDate?.toString()??null;const willUpdateDisplayMonth=key!==prevSelectedDateRef.current;if(willUpdateDisplayMonth){prevSelectedDateRef.current=key;if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}}},[selectedDate,setCurrentDate,setDisplayMonthAndRefs]);}
61
65
 
62
- const DatePickerInput=React__namespace.forwardRef((props,ref)=>{const{value:propValue,onBlur,onClick,onFocus,onKeyDown,onChange,dateFormat,locale=enUSLocaleCode,modifiers,getModifiersForDay,parseDate,placeholder,testId,resetInvalidValueOnBlur=true,["aria-label"]:ariaLabel,...restProps}=props;const[value,setValue]=React__namespace.useState(propValue);const lastPropValueRef=React__namespace.useRef(propValue);const keepInvalidTextRef=React__namespace.useRef(false);const lastTypedTextFormatValueRef=React__namespace.useRef(null);const processModifiers=React__namespace.useCallback((date,value)=>{if(!getModifiersForDay||!modifiers){return {}}return getModifiersForDay(date,modifiers).reduce((obj,modifier)=>({...obj,[modifier]:true}),{})},[getModifiersForDay,modifiers]);const updateDate=React__namespace.useCallback((date,inputValue)=>{if(onChange){onChange(date,processModifiers(date,inputValue),inputValue||undefined);}},[onChange,processModifiers]);const updateDateAsInvalid=React__namespace.useCallback(()=>{if(onChange){onChange(null,{});}},[onChange]);const processDate=React__namespace.useCallback(inputValue=>{if(!inputValue||inputValue.trim()===""){return}if(!parseDate){return}const date=parseDate(inputValue,dateFormat,locale);if(!date){return}return date},[parseDate,dateFormat,locale]);const isValid=React__namespace.useCallback(()=>{const date=processDate(value);if(!date){return false}const modifiersResult=processModifiers(date,value);if(modifiersResult.disabled){return false}return true},[value,processDate,processModifiers]);const isTextFormat=TemporalLocaleUtils.isTextFormatDate(dateFormat);React__namespace.useEffect(()=>{const propValueChanged=lastPropValueRef.current!==propValue;lastPropValueRef.current=propValue;if(propValueChanged){const safeProp=propValue??"";const safeValue=value??"";const isLastTypedValue=lastTypedTextFormatValueRef.current!==null&&value===lastTypedTextFormatValueRef.current;const bothHaveContent=safeValue!==""&&safeProp!=="";const oneIsPrefixOfOther=safeProp.startsWith(safeValue)||safeValue.startsWith(safeProp);const skipSyncUserTyping=isTextFormat&&propValue!==value&&(isLastTypedValue||bothHaveContent&&oneIsPrefixOfOther);if(keepInvalidTextRef.current&&(!propValue||propValue.trim()==="")){keepInvalidTextRef.current=false;}else if(skipSyncUserTyping){return}else if(propValue===value||(propValue??"")===(value??"")){keepInvalidTextRef.current=false;lastTypedTextFormatValueRef.current=null;return}else {setValue(propValue);keepInvalidTextRef.current=false;lastTypedTextFormatValueRef.current=null;}}},[propValue,isTextFormat,value]);wonderBlocksCore.useOnMountEffect(()=>{const skipValidation=dateFormat==="LL"&&propValue;if(!skipValidation&&!isValid()){updateDateAsInvalid();}});const handleFocus=e=>{if(onFocus){onFocus(e);}};const pendingValidationRef=React__namespace.useRef(false);const validateInput=React__namespace.useCallback(()=>{lastTypedTextFormatValueRef.current=null;const date=processDate(value);if(date){const modifiersResult=processModifiers(date,value);if(!modifiersResult.disabled){updateDate(date,value);}else {if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDate(date,value);}else {setValue(propValue);}}}else if(value&&value.trim()!==""){if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDateAsInvalid();}else {setValue(propValue);}}else {setValue(propValue);}},[value,processDate,processModifiers,resetInvalidValueOnBlur,propValue,updateDate,updateDateAsInvalid]);const handleBlur=e=>{const movingToCalendar=e.relatedTarget instanceof HTMLElement&&e.relatedTarget.closest('[data-testid="date-picker-overlay"]')!==null;if(movingToCalendar){pendingValidationRef.current=true;if(onBlur){onBlur(e);}return}validateInput();if(onBlur){onBlur(e);}};const innerRef=React__namespace.useRef(null);const handleChange=newValue=>{setValue(newValue);const date=processDate(newValue);if(date){const modifiersResult=processModifiers(date,newValue);if(isTextFormat){lastTypedTextFormatValueRef.current=newValue;}if(!modifiersResult.disabled){updateDate(date,newValue);}else if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDate(date,newValue);}else {updateDate(date,newValue);}}else if(!resetInvalidValueOnBlur&&newValue&&newValue.trim()!==""){keepInvalidTextRef.current=true;updateDateAsInvalid();}};React__namespace.useImperativeHandle(ref,()=>{const inputElement=innerRef.current;if(!inputElement){return null}inputElement.validateInput=()=>{pendingValidationRef.current=false;validateInput();};return inputElement});return jsxRuntime.jsxs(wonderBlocksCore.View,{style:styles$1.container,onClick:e=>{if(!restProps.disabled&&onClick){onClick(e);}},children:[jsxRuntime.jsx(wonderBlocksForm.TextField,{ref:innerRef,...restProps,onBlur:handleBlur,onFocus:handleFocus,onKeyDown:onKeyDown,onChange:handleChange,disabled:restProps.disabled,placeholder:placeholder,value:value??"",testId:testId,"aria-label":ariaLabel,autoComplete:"off",type:"text",style:styles$1.textField}),jsxRuntime.jsx(wonderBlocksIcon.PhosphorIcon,{icon:calendarIcon__default["default"],color:restProps.disabled?wonderBlocksTokens.semanticColor.core.foreground.disabled.default:wonderBlocksTokens.semanticColor.core.foreground.instructive.default,size:"small",style:styles$1.icon})]})});const fieldPaddingInline=wonderBlocksTokens.sizing.size_160;const iconSize=wonderBlocksTokens.sizing.size_160;const fieldPaddingInlineEnd=fieldPaddingInline+iconSize+fieldPaddingInline;const styles$1=aphrodite.StyleSheet.create({container:{alignItems:"center",flexDirection:"row",justifyContent:"stretch"},icon:{pointerEvents:"none",position:"absolute",insetInlineEnd:fieldPaddingInline},textField:{width:"100%",paddingInlineStart:fieldPaddingInline,paddingInlineEnd:fieldPaddingInlineEnd}});
66
+ function adjustDateSegment(date,type,delta){if(type==="day"){const{daysInMonth}=date;const newDay=((date.day-1+delta)%daysInMonth+daysInMonth)%daysInMonth+1;return date.with({day:newDay})}if(type==="month"){const newMonth=((date.month-1+delta)%12+12)%12+1;return date.with({month:newMonth})}return date.with({year:date.year+delta})}
67
+
68
+ function findSegmentAtOffset(segments,offset){return segments.find(s=>offset>=s.start&&offset<=s.end)??null}
69
+
70
+ function getIntlOptionsForSegmentDetection(formatString){if(!formatString||formatString==="L"){return {year:"numeric",month:"numeric",day:"numeric"}}if(formatString==="dateStyle:short"||formatString==="dateStyle:medium"||formatString==="dateStyle:long"||formatString==="dateStyle:full"){const style=formatString.split(":")[1];return {dateStyle:style}}return null}
71
+
72
+ function segmentsFromFixedOrder(value,separator,order){const parts=value.split(separator);if(parts.length!==order.length){return null}if(!parts.every(part=>/^\d+$/.test(part))){return null}const segments=[];let offset=0;for(let i=0;i<parts.length;i++){const part=parts[i];segments.push({type:order[i],start:offset,end:offset+part.length});offset+=part.length+separator.length;}return segments}
73
+
74
+ function segmentsFromIntlParts(value,locale,options){const referenceDate=new Date(2020,2,15);let parts;try{parts=new Intl.DateTimeFormat(locale,options).formatToParts(referenceDate);}catch{return null}const segments=[];let offset=0;for(const part of parts){const segmentType=INTL_TYPE_TO_SEGMENT_TYPE[part.type];if(segmentType){const match=/^\d+/.exec(value.slice(offset));if(!match){return null}segments.push({type:segmentType,start:offset,end:offset+match[0].length});offset+=match[0].length;}else {if(value.slice(offset,offset+part.value.length)!==part.value){return null}offset+=part.value.length;}}if(offset!==value.length){return null}const types=new Set(segments.map(s=>s.type));if(types.size!==3){return null}return segments}
75
+
76
+ function getDateSegments(value,formatString,locale){if(!value){return null}if(formatString==="YYYY-MM-DD"){return segmentsFromFixedOrder(value,"-",["year","month","day"])}if(formatString&&SLASH_FORMAT_STRINGS.has(formatString)){return segmentsFromFixedOrder(value,"/",["month","day","year"])}const options=getIntlOptionsForSegmentDetection(formatString);if(!options){return null}return segmentsFromIntlParts(value,locale,options)}
77
+
78
+ function useDateSegmentArrowKeys({value,dateFormat,locale,parseDate,handleChange,innerRef}){const pendingSelectionRef=React__namespace.useRef(null);const isRtl=wonderBlocksCore.useDirectionDetection(innerRef)==="rtl";React__namespace.useEffect(()=>{if(pendingSelectionRef.current&&innerRef.current){const[start,end]=pendingSelectionRef.current;innerRef.current.setSelectionRange(start,end);pendingSelectionRef.current=null;}},[value,innerRef]);return React__namespace.useCallback(e=>{const key=e.key;const isVerticalKey=key==="ArrowUp"||key==="ArrowDown";const isHorizontalKey=key==="ArrowLeft"||key==="ArrowRight";if(!isVerticalKey&&!isHorizontalKey){return false}if(!value){return false}const segments=getDateSegments(value,dateFormat,locale);if(!segments){return false}const offset=e.currentTarget.selectionStart??0;const segment=findSegmentAtOffset(segments,offset);if(!segment){return false}if(isHorizontalKey){const movesToNextSegment=isRtl?key==="ArrowLeft":key==="ArrowRight";const currentIndex=segments.indexOf(segment);const targetSegment=segments[currentIndex+(movesToNextSegment?1:-1)]??segment;e.preventDefault();innerRef.current?.setSelectionRange(targetSegment.start,targetSegment.end);return true}if(!parseDate){return false}const jsDate=parseDate(value,dateFormat,locale);if(!jsDate){return false}const currentDate=TemporalLocaleUtils.jsDateToTemporalDate(jsDate);const delta=key==="ArrowUp"?1:-1;const adjustedDate=adjustDateSegment(currentDate,segment.type,delta);const newValue=TemporalLocaleUtils.formatDate(adjustedDate,dateFormat,locale);const newSegments=getDateSegments(newValue,dateFormat,locale);const newSegment=newSegments?.find(s=>s.type===segment.type);pendingSelectionRef.current=newSegment?[newSegment.start,newSegment.end]:null;e.preventDefault();handleChange(newValue);return true},[value,dateFormat,locale,parseDate,handleChange,isRtl,innerRef])}
79
+
80
+ const DEFAULT_CALENDAR_BUTTON_ARIA_LABEL="Toggle calendar";const DatePickerInput=React__namespace.forwardRef((props,ref)=>{const{value:propValue,onBlur,onFocus,onKeyDown,onChange,dateFormat,locale=enUSLocaleCode,modifiers,getModifiersForDay,parseDate,placeholder,testId,resetInvalidValueOnBlur=true,["aria-label"]:ariaLabel,expanded,onToggleOverlay,onCalendarButtonKeyDown,calendarButtonAriaLabel=DEFAULT_CALENDAR_BUTTON_ARIA_LABEL,calendarButtonRef,...restProps}=props;const[value,setValue]=React__namespace.useState(propValue);const lastPropValueRef=React__namespace.useRef(propValue);const keepInvalidTextRef=React__namespace.useRef(false);const lastTypedTextFormatValueRef=React__namespace.useRef(null);const processModifiers=React__namespace.useCallback((date,value)=>{if(!getModifiersForDay||!modifiers){return {}}return getModifiersForDay(date,modifiers).reduce((obj,modifier)=>({...obj,[modifier]:true}),{})},[getModifiersForDay,modifiers]);const updateDate=React__namespace.useCallback((date,inputValue)=>{if(onChange){onChange(date,processModifiers(date,inputValue),inputValue||undefined);}},[onChange,processModifiers]);const updateDateAsInvalid=React__namespace.useCallback(()=>{if(onChange){onChange(null,{});}},[onChange]);const processDate=React__namespace.useCallback(inputValue=>{if(!inputValue||inputValue.trim()===""){return}if(!parseDate){return}const date=parseDate(inputValue,dateFormat,locale);if(!date){return}return date},[parseDate,dateFormat,locale]);const isValid=React__namespace.useCallback(()=>{const date=processDate(value);if(!date){return false}const modifiersResult=processModifiers(date,value);if(modifiersResult.disabled){return false}return true},[value,processDate,processModifiers]);const isTextFormat=TemporalLocaleUtils.isTextFormatDate(dateFormat);React__namespace.useEffect(()=>{const propValueChanged=lastPropValueRef.current!==propValue;lastPropValueRef.current=propValue;if(propValueChanged){const safeProp=propValue??"";const safeValue=value??"";const isLastTypedValue=lastTypedTextFormatValueRef.current!==null&&value===lastTypedTextFormatValueRef.current;const bothHaveContent=safeValue!==""&&safeProp!=="";const oneIsPrefixOfOther=safeProp.startsWith(safeValue)||safeValue.startsWith(safeProp);const skipSyncUserTyping=isTextFormat&&propValue!==value&&(isLastTypedValue||bothHaveContent&&oneIsPrefixOfOther);if(keepInvalidTextRef.current&&(!propValue||propValue.trim()==="")){keepInvalidTextRef.current=false;}else if(skipSyncUserTyping){return}else if(propValue===value||(propValue??"")===(value??"")){keepInvalidTextRef.current=false;lastTypedTextFormatValueRef.current=null;return}else {setValue(propValue);keepInvalidTextRef.current=false;lastTypedTextFormatValueRef.current=null;}}},[propValue,isTextFormat,value]);wonderBlocksCore.useOnMountEffect(()=>{const skipValidation=dateFormat==="LL"&&propValue;if(!skipValidation&&!isValid()){updateDateAsInvalid();}});const handleFocus=e=>{if(onFocus){onFocus(e);}};const pendingValidationRef=React__namespace.useRef(false);const validateInput=React__namespace.useCallback(()=>{lastTypedTextFormatValueRef.current=null;const date=processDate(value);if(date){const modifiersResult=processModifiers(date,value);if(!modifiersResult.disabled){updateDate(date,value);}else {if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDate(date,value);}else {setValue(propValue);}}}else if(value&&value.trim()!==""){if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDateAsInvalid();}else {setValue(propValue);}}else {setValue(propValue);}},[value,processDate,processModifiers,resetInvalidValueOnBlur,propValue,updateDate,updateDateAsInvalid]);const handleBlur=e=>{const movingToCalendar=e.relatedTarget instanceof HTMLElement&&e.relatedTarget.closest('[data-testid="date-picker-overlay"]')!==null;if(movingToCalendar){pendingValidationRef.current=true;if(onBlur){onBlur(e);}return}validateInput();if(onBlur){onBlur(e);}};const innerRef=React__namespace.useRef(null);const handleChange=newValue=>{setValue(newValue);const date=processDate(newValue);if(date){const modifiersResult=processModifiers(date,newValue);if(isTextFormat){lastTypedTextFormatValueRef.current=newValue;}if(!modifiersResult.disabled){updateDate(date,newValue);}else if(!resetInvalidValueOnBlur){keepInvalidTextRef.current=true;updateDate(date,newValue);}else {updateDate(date,newValue);}}else if(!resetInvalidValueOnBlur&&newValue&&newValue.trim()!==""){keepInvalidTextRef.current=true;updateDateAsInvalid();}};const handleSegmentKeyDown=useDateSegmentArrowKeys({value,dateFormat,locale,parseDate,handleChange,innerRef});const handleInputKeyDown=e=>{if(!expanded&&handleSegmentKeyDown(e)){return}onKeyDown?.(e);};React__namespace.useImperativeHandle(ref,()=>{const inputElement=innerRef.current;if(!inputElement){return null}inputElement.validateInput=()=>{pendingValidationRef.current=false;validateInput();};return inputElement});return jsxRuntime.jsxs(wonderBlocksCore.View,{style:styles$1.container,children:[jsxRuntime.jsx(wonderBlocksForm.TextField,{ref:innerRef,...restProps,onBlur:handleBlur,onFocus:handleFocus,onKeyDown:handleInputKeyDown,onChange:handleChange,disabled:restProps.disabled,placeholder:placeholder,value:value??"",testId:testId,"aria-label":ariaLabel,autoComplete:"off",type:"text",style:styles$1.textField}),jsxRuntime.jsx(IconButton__default["default"],{ref:calendarButtonRef,icon:calendarIcon__default["default"],size:"small",kind:"tertiary",actionType:"neutral",disabled:restProps.disabled,"aria-label":calendarButtonAriaLabel,"aria-expanded":expanded,"aria-haspopup":"grid",onClick:()=>onToggleOverlay(),onKeyDown:onCalendarButtonKeyDown,style:styles$1.icon})]})});const fieldPaddingInline=wonderBlocksTokens.sizing.size_160;const calendarButtonSize=wonderBlocksTokens.sizing.size_320;const fieldPaddingInlineEnd=`calc(${fieldPaddingInline} + ${calendarButtonSize} + ${fieldPaddingInline} / 2)`;const styles$1=aphrodite.StyleSheet.create({container:{alignItems:"center",flexDirection:"row",justifyContent:"stretch"},icon:{margin:0,position:"absolute",insetInlineEnd:fieldPaddingInline},textField:{width:"100%",paddingInlineStart:fieldPaddingInline,paddingInlineEnd:fieldPaddingInlineEnd}});
63
81
 
64
82
  function FocusManager(props){const{children,referenceElement,onStartFocused,onEndFocused}=props;const rootNodeRef=React__namespace.useRef(null);const focusableElementsRef=React__namespace.useRef([]);const focusableElementsInsideRef=React__namespace.useRef([]);const nextFocusableElementRef=React__namespace.useRef(null);const getFocusableElements=React__namespace.useCallback(()=>{return wonderBlocksCore.findFocusableNodes(document)},[]);const getReferenceIndex=React__namespace.useCallback(()=>{if(!referenceElement){return -1}return focusableElementsRef.current.indexOf(referenceElement)},[referenceElement]);const getNextFocusableElement=React__namespace.useCallback(()=>{const referenceIndex=getReferenceIndex();if(referenceIndex>=0){const nextElementIndex=referenceIndex<focusableElementsRef.current.length-1?referenceIndex+1:0;return focusableElementsRef.current[nextElementIndex]}return undefined},[getReferenceIndex]);React__namespace.useEffect(()=>{focusableElementsRef.current=getFocusableElements();nextFocusableElementRef.current=getNextFocusableElement();const handleKeydownReferenceElement=e=>{if(e.key==="Tab"&&!e.shiftKey){if(rootNodeRef.current){focusableElementsInsideRef.current=wonderBlocksCore.findFocusableNodes(rootNodeRef.current);}if(focusableElementsInsideRef.current.length>0){e.preventDefault();focusableElementsInsideRef.current[0]?.focus();}}};const handleKeydownNextFocusableElement=e=>{if(e.key==="Tab"&&e.shiftKey){if(rootNodeRef.current){focusableElementsInsideRef.current=wonderBlocksCore.findFocusableNodes(rootNodeRef.current);}if(focusableElementsInsideRef.current.length>0){e.preventDefault();const lastIndex=focusableElementsInsideRef.current.length-1;focusableElementsInsideRef.current[lastIndex]?.focus();}}};if(referenceElement){referenceElement.addEventListener("keydown",handleKeydownReferenceElement,true);}if(nextFocusableElementRef.current){nextFocusableElementRef.current.addEventListener("keydown",handleKeydownNextFocusableElement,true);}return ()=>{if(referenceElement){referenceElement.removeEventListener("keydown",handleKeydownReferenceElement,true);}if(nextFocusableElementRef.current){nextFocusableElementRef.current.removeEventListener("keydown",handleKeydownNextFocusableElement,true);}}},[referenceElement,getNextFocusableElement,getFocusableElements]);const setComponentRootNode=React__namespace.useCallback(node=>{if(!node){return}rootNodeRef.current=node;focusableElementsInsideRef.current=wonderBlocksCore.findFocusableNodes(node);},[]);const handleFocusPreviousFocusableElement=React__namespace.useCallback(()=>{if(referenceElement){referenceElement.focus();}if(onStartFocused){onStartFocused();}},[referenceElement,onStartFocused]);const handleFocusNextFocusableElement=React__namespace.useCallback(()=>{if(nextFocusableElementRef.current){nextFocusableElementRef.current.focus();}if(onEndFocused){onEndFocused();}},[onEndFocused]);return jsxRuntime.jsxs(React__namespace.Fragment,{children:[jsxRuntime.jsx("div",{tabIndex:0,"data-testid":"focus-sentinel-prev",onFocus:handleFocusPreviousFocusableElement,style:{position:"fixed"}}),jsxRuntime.jsx("div",{"data-testid":"date-picker-overlay",ref:setComponentRootNode,children:children}),jsxRuntime.jsx("div",{tabIndex:0,"data-testid":"focus-sentinel-next",onFocus:handleFocusNextFocusableElement,style:{position:"fixed"}})]})}
65
83
 
66
- const DEFAULT_STYLE={background:wonderBlocksTokens.semanticColor.core.background.base.default,borderRadius:wonderBlocksTokens.border.radius.radius_040,border:`solid ${wonderBlocksTokens.border.width.thin} ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`,boxShadow:wonderBlocksTokens.boxShadow.mid};const BASE_CONTAINER_STYLES={fontFamily:wonderBlocksTokens.font.family.sans,padding:wonderBlocksTokens.sizing.size_100};const OUT_OF_BOUNDARIES_STYLES={visibility:"hidden"};const DatePickerOverlay=({children,referenceElement,onClose,dir="ltr",style=DEFAULT_STYLE})=>{if(!referenceElement){return null}const placement=dir==="rtl"?"bottom-end":"bottom-start";const modalHost=wonderBlocksModal.maybeGetPortalMountedModalHostElement(referenceElement)||document.querySelector("body");if(!modalHost){return null}return reactDom.createPortal(jsxRuntime.jsx(FocusManager,{referenceElement:referenceElement,onEndFocused:onClose,children:jsxRuntime.jsx(reactPopper.Popper,{referenceElement:referenceElement,placement:placement,strategy:"fixed",modifiers:[{name:"preventOverflow",options:{rootBoundary:"viewport"}}],children:({placement,ref,style:popperStyle,isReferenceHidden,hasPopperEscaped})=>{const isTestEnvironment=typeof window!=="undefined"&&window.navigator.userAgent.includes("jsdom");const outOfBoundaries=!isTestEnvironment&&(isReferenceHidden||hasPopperEscaped);const combinedStyles={...BASE_CONTAINER_STYLES,...popperStyle,...style,...outOfBoundaries&&OUT_OF_BOUNDARIES_STYLES};return jsxRuntime.jsx("div",{ref:ref,style:combinedStyles,"data-placement":placement,children:children})}})}),modalHost)};
84
+ const DEFAULT_CALENDAR_GRID_REGION_ARIA_LABEL="Date picker calendar";const DEFAULT_STYLE={background:wonderBlocksTokens.semanticColor.core.background.base.default,borderRadius:wonderBlocksTokens.border.radius.radius_040,border:`solid ${wonderBlocksTokens.border.width.thin} ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`,boxShadow:wonderBlocksTokens.boxShadow.mid};const BASE_CONTAINER_STYLES={fontFamily:wonderBlocksTokens.font.family.sans,padding:wonderBlocksTokens.sizing.size_100};const OUT_OF_BOUNDARIES_STYLES={visibility:"hidden"};const DatePickerOverlay=({children,referenceElement,focusReferenceElement,onClose,dir="ltr",style=DEFAULT_STYLE,calendarGridRegionAriaLabel=DEFAULT_CALENDAR_GRID_REGION_ARIA_LABEL})=>{if(!referenceElement){return null}const placement=dir==="rtl"?"bottom-end":"bottom-start";const modalHost=wonderBlocksModal.maybeGetPortalMountedModalHostElement(referenceElement)||document.querySelector("body");if(!modalHost){return null}return reactDom.createPortal(jsxRuntime.jsx(FocusManager,{referenceElement:focusReferenceElement??referenceElement,onEndFocused:onClose,children:jsxRuntime.jsx(reactPopper.Popper,{referenceElement:referenceElement,placement:placement,strategy:"fixed",modifiers:[{name:"preventOverflow",options:{rootBoundary:"viewport"}}],children:({placement,ref,style:popperStyle,isReferenceHidden,hasPopperEscaped})=>{const isTestEnvironment=typeof window!=="undefined"&&window.navigator.userAgent.includes("jsdom");const outOfBoundaries=!isTestEnvironment&&(isReferenceHidden||hasPopperEscaped);const combinedStyles={...BASE_CONTAINER_STYLES,...popperStyle,...style,...outOfBoundaries&&OUT_OF_BOUNDARIES_STYLES};return jsxRuntime.jsx("div",{"aria-label":calendarGridRegionAriaLabel,ref:ref,role:"region",style:combinedStyles,"data-placement":placement,children:children})}})}),modalHost)};
67
85
 
68
- const customRootStyle={"--rdp-accent-color":wonderBlocksTokens.semanticColor.core.border.instructive.default};const DatePicker=props=>{const{locale: locale$1,updateDate,dateFormat,disabled,id,maxDate,minDate,inputAriaLabel,placeholder,selectedDate,style,closeOnSelect=true,resetInvalidValueOnBlur=true,footer}=props;const[showOverlay,setShowOverlay]=React__namespace.useState(false);const[currentDate,setCurrentDate]=React__namespace.useState(selectedDate);const datePickerInputRef=React__namespace.useRef(null);const datePickerRef=React__namespace.useRef(null);const refWrapper=React__namespace.useRef(null);const skipNextOpenRef=React__namespace.useRef(false);const{handleEscapeKeyDown}=useEscapeKeyupCapture();const{displayMonth,setDisplayMonth,displayMonthRef,inputDrivenMonthRef,setDisplayMonthAndRefs}=useDisplayMonth({selectedDate});const open=React__namespace.useCallback(()=>{if(skipNextOpenRef.current){skipNextOpenRef.current=false;return}if(!disabled){if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}else {displayMonthRef.current=displayMonthRef.current??displayMonth;}setShowOverlay(true);}},[disabled,displayMonth,displayMonthRef,selectedDate,setDisplayMonthAndRefs,skipNextOpenRef]);const{handleInputChange,clearInputDrivenMonth}=useOverlayMonthFromInput({inputDrivenMonthRef,setDisplayMonth,setDisplayMonthAndRefs,setCurrentDate,updateDate,dateFormat,localeCode:locale$1?.code});const close=React__namespace.useCallback(()=>{clearInputDrivenMonth();if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}else {setDisplayMonthAndRefs(null);}setShowOverlay(false);datePickerInputRef.current?.validateInput?.();},[selectedDate,setDisplayMonthAndRefs,clearInputDrivenMonth,datePickerInputRef]);useCloseOnOutsideClick({refWrapper,datePickerRef,showOverlay,closeOnSelect,close});useSelectedDateSync({selectedDate,setCurrentDate,setDisplayMonthAndRefs});const computedLocale=locale$1??locale.enUS;const selectedDateValue=currentDate?TemporalLocaleUtils.temporalDateToJsDate(currentDate):undefined;const modifiers=useDatePickerModifiers({selectedDateValue,minDate,maxDate});const formatDateForInput=useFormatDateForInput({dateFormat,locale:computedLocale});const dir=refWrapper.current?.closest("[dir]")?.getAttribute("dir")||"ltr";const handleMonthChange=React__namespace.useCallback(newMonth=>{clearInputDrivenMonth();setDisplayMonthAndRefs(newMonth);},[clearInputDrivenMonth,setDisplayMonthAndRefs]);const isLeavingDropdown=e=>{const dayPickerCalendar=datePickerRef.current;if(!dayPickerCalendar){return true}if(e.relatedTarget instanceof Node){return !dayPickerCalendar.contains(e.relatedTarget)}return true};const handleInputBlur=e=>{if(isLeavingDropdown(e)){close();}};const onEscapeCloseOverlay=React__namespace.useCallback(()=>{skipNextOpenRef.current=true;close();datePickerInputRef.current?.focus();},[close,skipNextOpenRef,datePickerInputRef]);const handleKeyDown=e=>{if(e.key==="Escape"){if(showOverlay){handleEscapeKeyDown(e,onEscapeCloseOverlay);}}if(e.key==="ArrowDown"&&!showOverlay){e.preventDefault();skipNextOpenRef.current=false;open();}if(e.key==="Enter"){e.preventDefault();if(showOverlay){if(closeOnSelect){close();}}else {skipNextOpenRef.current=false;open();}}};const RootWithEsc=React__namespace.useCallback(props=>{const{onKeyDown,rootRef:_,...rest}=props;return jsxRuntime.jsx("div",{...rest,tabIndex:-1,onKeyDown:e=>{onKeyDown?.(e);if(e.key==="Escape"){handleEscapeKeyDown(e,onEscapeCloseOverlay);}}})},[handleEscapeKeyDown,onEscapeCloseOverlay]);const dayPickerComponents=React__namespace.useMemo(()=>({Root:RootWithEsc}),[RootWithEsc]);const handleDayClick=React__namespace.useCallback((date,{disabled})=>{if(disabled||!date){return}datePickerInputRef.current?.focus();const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(date);setCurrentDate(wrappedDate);const monthDate=new Date(date);clearInputDrivenMonth();setDisplayMonthAndRefs(monthDate);updateDate(wrappedDate);setShowOverlay(!closeOnSelect);},[updateDate,closeOnSelect,setDisplayMonthAndRefs,datePickerInputRef,clearInputDrivenMonth]);const renderInput=inputModifiers=>{const selectedDateAsValue=formatDateForInput(currentDate);return jsxRuntime.jsx(DatePickerInput,{onBlur:handleInputBlur,onFocus:open,onClick:open,onChange:handleInputChange,onKeyDown:handleKeyDown,"aria-label":inputAriaLabel,disabled:disabled,id:id,placeholder:placeholder,value:selectedDateAsValue,ref:datePickerInputRef,dateFormat:dateFormat,locale:computedLocale.code,parseDate:TemporalLocaleUtils.parseDateToJsDate,getModifiersForDay:TemporalLocaleUtils.getModifiersForDay,modifiers:inputModifiers,resetInvalidValueOnBlur:resetInvalidValueOnBlur,testId:id&&`${id}-input`})};const maybeRenderFooter=()=>{if(!footer){return null}return jsxRuntime.jsx(wonderBlocksCore.View,{testId:"date-picker-footer",style:styles.footer,children:footer({close})})};const minDateToShow=minDate&&selectedDateValue?temporalPolyfill.Temporal.PlainDate.compare(minDate,currentDate)<0?TemporalLocaleUtils.temporalDateToJsDate(minDate):selectedDateValue:minDate?TemporalLocaleUtils.temporalDateToJsDate(minDate):undefined;const dayPickerEndMonth=React__namespace.useMemo(()=>maxDate?TemporalLocaleUtils.temporalDateToJsDate(maxDate):undefined,[maxDate]);const dayPickerStyles=React__namespace.useMemo(()=>({root:{...customRootStyle},nav:{width:"auto"}}),[]);const inputDrivenMonth=inputDrivenMonthRef.current;const isInputDriven=inputDrivenMonth!=null;const selectedDateAsJs=selectedDate!=null?TemporalLocaleUtils.temporalDateToJsDate(selectedDate):undefined;const baseMonth=displayMonthRef.current??(showOverlay&&selectedDateAsJs?selectedDateAsJs:undefined)??displayMonth??selectedDateValue??new Date;const firstOfBaseMonth=new Date(baseMonth.getFullYear(),baseMonth.getMonth(),1);const pickerKey=isInputDriven?`input-${inputDrivenMonth.getTime()}`:`picker-${baseMonth.getTime()}`;const inputDrivenMonthMs=inputDrivenMonth?.getTime();const firstOfBaseMonthMs=firstOfBaseMonth.getTime();const dayPickerMonthProps=React__namespace.useMemo(()=>{if(isInputDriven&&inputDrivenMonthMs!=null){const d=new Date(inputDrivenMonthMs);return {month:new Date(d.getFullYear(),d.getMonth(),1),onMonthChange:handleMonthChange}}return {defaultMonth:new Date(firstOfBaseMonthMs)}},[isInputDriven,inputDrivenMonthMs,firstOfBaseMonthMs,handleMonthChange]);return jsxRuntime.jsxs(wonderBlocksCore.View,{style:style,ref:refWrapper,children:[renderInput(modifiers),showOverlay&&jsxRuntime.jsx(DatePickerOverlay,{referenceElement:datePickerInputRef.current,onClose:close,dir:dir==="rtl"?"rtl":"ltr",children:jsxRuntime.jsxs(wonderBlocksCore.View,{ref:datePickerRef,children:[jsxRuntime.jsx(reactDayPicker.DayPicker,{mode:"single",selected:selectedDateValue,...dayPickerMonthProps,startMonth:minDateToShow??undefined,endMonth:dayPickerEndMonth,modifiers:modifiers,onDayClick:handleDayClick,components:dayPickerComponents,locale:computedLocale,dir:dir,styles:dayPickerStyles},pickerKey),maybeRenderFooter()]})})]})};DatePicker.defaultProps={closeOnSelect:true};const styles=aphrodite.StyleSheet.create({footer:{margin:wonderBlocksTokens.sizing.size_120,marginBlockStart:0}});
86
+ const customRootStyle={"--rdp-accent-color":wonderBlocksTokens.semanticColor.core.border.instructive.default,"--rdp-today-color":wonderBlocksTokens.semanticColor.core.foreground.instructive.default};const dayButtonFocusStyles=aphrodite.StyleSheet.create({focus:wonderBlocksStyles.focusStyles.focus});const dayPickerClassNames={[reactDayPicker.UI.DayButton]:`${reactDayPicker.getDefaultClassNames()[reactDayPicker.UI.DayButton]} ${aphrodite.css(dayButtonFocusStyles.focus)}`};const DatePicker=props=>{const{locale: locale$1,updateDate,dateFormat,disabled,id,maxDate,minDate,inputAriaLabel,calendarButtonAriaLabel,calendarGridRegionAriaLabel,placeholder,selectedDate,style,closeOnSelect=true,resetInvalidValueOnBlur=true,footer}=props;const[showOverlay,setShowOverlay]=React__namespace.useState(false);const[currentDate,setCurrentDate]=React__namespace.useState(selectedDate);const datePickerInputRef=React__namespace.useRef(null);const calendarButtonRef=React__namespace.useRef(null);const datePickerRef=React__namespace.useRef(null);const refWrapper=React__namespace.useRef(null);const skipNextOpenRef=React__namespace.useRef(false);const movingFocusIntoOverlayRef=React__namespace.useRef(false);const{handleEscapeKeyDown}=useEscapeKeyupCapture();const{displayMonth,setDisplayMonth,displayMonthRef,inputDrivenMonthRef,setDisplayMonthAndRefs}=useDisplayMonth({selectedDate});const open=React__namespace.useCallback(()=>{if(skipNextOpenRef.current){skipNextOpenRef.current=false;return}if(!disabled){if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}else {displayMonthRef.current=displayMonthRef.current??displayMonth;}setShowOverlay(true);}},[disabled,displayMonth,displayMonthRef,selectedDate,setDisplayMonthAndRefs,skipNextOpenRef]);const{handleInputChange,clearInputDrivenMonth}=useOverlayMonthFromInput({inputDrivenMonthRef,setDisplayMonth,setDisplayMonthAndRefs,setCurrentDate,updateDate,dateFormat,localeCode:locale$1?.code});const close=React__namespace.useCallback(()=>{clearInputDrivenMonth();if(selectedDate!=null){const jsDate=TemporalLocaleUtils.temporalDateToJsDate(selectedDate);setDisplayMonthAndRefs(jsDate);}else {setDisplayMonthAndRefs(null);}setShowOverlay(false);datePickerInputRef.current?.validateInput?.();},[selectedDate,setDisplayMonthAndRefs,clearInputDrivenMonth,datePickerInputRef]);const handleToggleOverlay=React__namespace.useCallback(()=>{if(disabled){return}if(showOverlay){close();}else {skipNextOpenRef.current=false;open();}},[disabled,showOverlay,close,open,skipNextOpenRef]);const focusIntoOverlay=React__namespace.useCallback(()=>{const overlayRoot=datePickerRef.current;if(!overlayRoot){return}const grid=overlayRoot.querySelector('[role="grid"]');const rovingTarget=grid?.querySelector('[tabindex="0"]');const target=rovingTarget??wonderBlocksCore.findFocusableNodes(grid??overlayRoot)[0];if(target){movingFocusIntoOverlayRef.current=true;target.focus();}},[datePickerRef]);useCloseOnOutsideClick({refWrapper,datePickerRef,showOverlay,closeOnSelect,close});useSelectedDateSync({selectedDate,setCurrentDate,setDisplayMonthAndRefs});const computedLocale=locale$1??locale.enUS;const selectedDateValue=currentDate?TemporalLocaleUtils.temporalDateToJsDate(currentDate):undefined;const modifiers=useDatePickerModifiers({selectedDateValue,minDate,maxDate});const formatDateForInput=useFormatDateForInput({dateFormat,locale:computedLocale});const dir=refWrapper.current?.closest("[dir]")?.getAttribute("dir")||"ltr";const handleMonthChange=React__namespace.useCallback(newMonth=>{clearInputDrivenMonth();setDisplayMonthAndRefs(newMonth);},[clearInputDrivenMonth,setDisplayMonthAndRefs]);const isLeavingDropdown=e=>{const dayPickerCalendar=datePickerRef.current;if(!(e.relatedTarget instanceof Node)){return true}if(dayPickerCalendar?.contains(e.relatedTarget)){return false}const calendarButton=calendarButtonRef.current;if(calendarButton?.contains(e.relatedTarget)){return false}return true};const handleInputBlur=e=>{if(movingFocusIntoOverlayRef.current){movingFocusIntoOverlayRef.current=false;return}if(isLeavingDropdown(e)){close();}};const onEscapeCloseOverlay=React__namespace.useCallback(()=>{skipNextOpenRef.current=true;close();calendarButtonRef.current?.focus();},[close,skipNextOpenRef]);const handleKeyDown=e=>{if(!showOverlay){return}if(e.key==="Escape"){handleEscapeKeyDown(e,onEscapeCloseOverlay);}if(e.key==="ArrowDown"){e.preventDefault();focusIntoOverlay();}if(e.key==="Enter"){e.preventDefault();if(closeOnSelect){close();}}};const handleCalendarButtonKeyDown=React__namespace.useCallback(e=>{if(e.key==="ArrowDown"&&showOverlay){e.preventDefault();focusIntoOverlay();}else if(e.key==="Escape"&&showOverlay){handleEscapeKeyDown(e,onEscapeCloseOverlay);}},[showOverlay,focusIntoOverlay,handleEscapeKeyDown,onEscapeCloseOverlay]);const RootWithEsc=React__namespace.useCallback(props=>{const{onKeyDown,rootRef:_,...rest}=props;return jsxRuntime.jsx("div",{...rest,tabIndex:-1,onKeyDown:e=>{onKeyDown?.(e);if(e.key==="Escape"){handleEscapeKeyDown(e,onEscapeCloseOverlay);}}})},[handleEscapeKeyDown,onEscapeCloseOverlay]);const dayPickerComponents=React__namespace.useMemo(()=>({Root:RootWithEsc}),[RootWithEsc]);const handleDayClick=React__namespace.useCallback((date,{disabled})=>{if(disabled||!date){return}datePickerInputRef.current?.focus();const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(date);setCurrentDate(wrappedDate);const monthDate=new Date(date);clearInputDrivenMonth();setDisplayMonthAndRefs(monthDate);updateDate(wrappedDate);setShowOverlay(!closeOnSelect);},[updateDate,closeOnSelect,setDisplayMonthAndRefs,datePickerInputRef,clearInputDrivenMonth]);const renderInput=inputModifiers=>{const selectedDateAsValue=formatDateForInput(currentDate);return jsxRuntime.jsx(DatePickerInput,{onBlur:handleInputBlur,onChange:handleInputChange,onKeyDown:handleKeyDown,"aria-label":inputAriaLabel,disabled:disabled,id:id,placeholder:placeholder,value:selectedDateAsValue,ref:datePickerInputRef,dateFormat:dateFormat,locale:computedLocale.code,parseDate:TemporalLocaleUtils.parseDateToJsDate,getModifiersForDay:TemporalLocaleUtils.getModifiersForDay,modifiers:inputModifiers,resetInvalidValueOnBlur:resetInvalidValueOnBlur,testId:id&&`${id}-input`,expanded:showOverlay,onToggleOverlay:handleToggleOverlay,onCalendarButtonKeyDown:handleCalendarButtonKeyDown,calendarButtonAriaLabel:calendarButtonAriaLabel,calendarButtonRef:calendarButtonRef})};const maybeRenderFooter=()=>{if(!footer){return null}return jsxRuntime.jsx(wonderBlocksCore.View,{testId:"date-picker-footer",style:styles.footer,children:footer({close})})};const minDateToShow=minDate&&selectedDateValue?temporalPolyfill.Temporal.PlainDate.compare(minDate,currentDate)<0?TemporalLocaleUtils.temporalDateToJsDate(minDate):selectedDateValue:minDate?TemporalLocaleUtils.temporalDateToJsDate(minDate):undefined;const dayPickerEndMonth=React__namespace.useMemo(()=>maxDate?TemporalLocaleUtils.temporalDateToJsDate(maxDate):undefined,[maxDate]);const dayPickerStyles=React__namespace.useMemo(()=>({root:{...customRootStyle},nav:{width:"auto"}}),[]);const dayPickerModifiersStyles=React__namespace.useMemo(()=>({selected:{fontWeight:wonderBlocksTokens.font.weight.medium},today:{fontWeight:wonderBlocksTokens.font.weight.bold}}),[]);const inputDrivenMonth=inputDrivenMonthRef.current;const isInputDriven=inputDrivenMonth!=null;const selectedDateAsJs=selectedDate!=null?TemporalLocaleUtils.temporalDateToJsDate(selectedDate):undefined;const baseMonth=displayMonthRef.current??(showOverlay&&selectedDateAsJs?selectedDateAsJs:undefined)??displayMonth??selectedDateValue??new Date;const firstOfBaseMonth=new Date(baseMonth.getFullYear(),baseMonth.getMonth(),1);const pickerKey=isInputDriven?`input-${inputDrivenMonth.getTime()}`:`picker-${baseMonth.getTime()}`;const inputDrivenMonthMs=inputDrivenMonth?.getTime();const firstOfBaseMonthMs=firstOfBaseMonth.getTime();const dayPickerMonthProps=React__namespace.useMemo(()=>{if(isInputDriven&&inputDrivenMonthMs!=null){const d=new Date(inputDrivenMonthMs);return {month:new Date(d.getFullYear(),d.getMonth(),1),onMonthChange:handleMonthChange}}return {defaultMonth:new Date(firstOfBaseMonthMs)}},[isInputDriven,inputDrivenMonthMs,firstOfBaseMonthMs,handleMonthChange]);return jsxRuntime.jsxs(wonderBlocksCore.View,{style:style,ref:refWrapper,children:[renderInput(modifiers),showOverlay&&jsxRuntime.jsx(DatePickerOverlay,{referenceElement:datePickerInputRef.current,focusReferenceElement:calendarButtonRef.current??undefined,onClose:close,dir:dir==="rtl"?"rtl":"ltr",calendarGridRegionAriaLabel:calendarGridRegionAriaLabel,children:jsxRuntime.jsxs(wonderBlocksCore.View,{ref:datePickerRef,children:[jsxRuntime.jsx(reactDayPicker.DayPicker,{mode:"single",selected:selectedDateValue,...dayPickerMonthProps,startMonth:minDateToShow??undefined,endMonth:dayPickerEndMonth,modifiers:modifiers,onDayClick:handleDayClick,components:dayPickerComponents,locale:computedLocale,dir:dir,styles:dayPickerStyles,modifiersStyles:dayPickerModifiersStyles,classNames:dayPickerClassNames},pickerKey),maybeRenderFooter()]})})]})};DatePicker.defaultProps={closeOnSelect:true};const styles=aphrodite.StyleSheet.create({footer:{margin:wonderBlocksTokens.sizing.size_120,marginBlockStart:0}});
69
87
 
70
88
  exports.DatePicker = DatePicker;
71
89
  exports.TemporalLocaleUtils = TemporalLocaleUtils;
@@ -0,0 +1,13 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import type { DateSegmentType } from "./types";
3
+ /**
4
+ * Adjusts a single date segment by `delta`, wrapping within that field's own
5
+ * valid range without cascading into adjacent fields (a simple "spinner"
6
+ * model, matching native date/time input behavior).
7
+ *
8
+ * @param date - The date to adjust.
9
+ * @param type - Which segment to adjust.
10
+ * @param delta - `1` to increment, `-1` to decrement.
11
+ * @returns A new date with that segment adjusted.
12
+ */
13
+ export declare function adjustDateSegment(date: Temporal.PlainDate, type: DateSegmentType, delta: 1 | -1): Temporal.PlainDate;
@@ -0,0 +1,6 @@
1
+ import type { DateSegmentType } from "./types";
2
+ export declare const enUSLocaleCode = "en-US";
3
+ /** Date format strings that use month names (e.g. "January") and need special handling for partial input and commit detection. */
4
+ export declare const TEXT_FORMAT_STRINGS: readonly ["LL", "MMMM D, YYYY", "MMM D, YYYY"];
5
+ export declare const SLASH_FORMAT_STRINGS: Set<string>;
6
+ export declare const INTL_TYPE_TO_SEGMENT_TYPE: Partial<Record<Intl.DateTimeFormatPartTypes, DateSegmentType>>;
@@ -0,0 +1,10 @@
1
+ import type { DateSegment } from "./types";
2
+ /**
3
+ * Find which segment (if any) contains a given caret/selection offset.
4
+ *
5
+ * @param segments - The segments to search, as returned by `getDateSegments`.
6
+ * @param offset - A character offset into the same string the segments were
7
+ * computed from (e.g. the input's `selectionStart`).
8
+ * @returns The matching segment, or `null` if none contains `offset`.
9
+ */
10
+ export declare function findSegmentAtOffset(segments: ReadonlyArray<DateSegment>, offset: number): DateSegment | null;
@@ -0,0 +1,16 @@
1
+ import type { DateSegment } from "./types";
2
+ /**
3
+ * Compute the character ranges of the day/month/year segments in `value`,
4
+ * as actually rendered by `formatDate` for this `formatString`/`locale`
5
+ * combination.
6
+ *
7
+ * @param value - The formatted date string to segment (as currently shown
8
+ * in the input).
9
+ * @param formatString - The `dateFormat` prop value used to render `value`.
10
+ * @param locale - The locale used to render `value`.
11
+ * @returns The computed segments, or `null` when segmentation isn't reliable
12
+ * for arrow-key editing -- e.g. a text format that spells out the month
13
+ * name, or any other format/locale combination whose day/month/year parts
14
+ * aren't plain numbers. Callers should treat `null` as "do nothing."
15
+ */
16
+ export declare function getDateSegments(value: string, formatString: string | null | undefined, locale: string): Array<DateSegment> | null;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Map a locale-dependent `dateFormat` string to the `Intl.DateTimeFormat`
3
+ * options that reproduce it, so its actual segment order/separators can be
4
+ * discovered via `formatToParts`.
5
+ *
6
+ * @param formatString - The `dateFormat` prop value (`undefined`/"L" or a
7
+ * "dateStyle:*" string).
8
+ * @returns The equivalent `Intl.DateTimeFormatOptions`, or `null` if
9
+ * `formatString` isn't one of the locale-dependent formats this handles.
10
+ */
11
+ export declare function getIntlOptionsForSegmentDetection(formatString: string | null | undefined): Intl.DateTimeFormatOptions | null;
@@ -0,0 +1,13 @@
1
+ import type { DateSegment, DateSegmentType } from "./types";
2
+ /**
3
+ * Split `value` on `separator` into segments in a known, fixed `order`.
4
+ * Used for date formats whose segment order and separator don't depend on
5
+ * locale (e.g. "YYYY-MM-DD", "MM/DD/YYYY").
6
+ *
7
+ * @param value - The formatted date string to segment.
8
+ * @param separator - The literal separator between segments (e.g. "/", "-").
9
+ * @param order - The segment type for each part, in the order they appear.
10
+ * @returns The computed segments, or `null` if `value` doesn't split into
11
+ * exactly `order.length` all-numeric parts.
12
+ */
13
+ export declare function segmentsFromFixedOrder(value: string, separator: string, order: ReadonlyArray<DateSegmentType>): Array<DateSegment> | null;
@@ -0,0 +1,14 @@
1
+ import type { DateSegment } from "./types";
2
+ /**
3
+ * Discover a locale's actual segment order and literal separators (via a
4
+ * reference date formatted with `options`) and walks `value` using that same
5
+ * structure to compute its segments.
6
+ *
7
+ * @param value - The formatted date string to segment.
8
+ * @param locale - The locale to format the reference date with.
9
+ * @param options - The `Intl.DateTimeFormatOptions` that produced `value`.
10
+ * @returns The computed segments, or `null` if `value` doesn't match the
11
+ * locale's expected layout, or any day/month/year part isn't purely numeric
12
+ * (e.g. a spelled-out month name).
13
+ */
14
+ export declare function segmentsFromIntlParts(value: string, locale: string, options: Intl.DateTimeFormatOptions): Array<DateSegment> | null;
@@ -1,7 +1,8 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import type { Locale } from "react-day-picker/locale";
3
- import { CustomModifiers } from "./types";
4
- export declare const enUSLocaleCode = "en-US";
3
+ import { enUSLocaleCode } from "./constants";
4
+ import type { CustomModifiers, NumericDatePattern } from "./types";
5
+ export { enUSLocaleCode };
5
6
  /**
6
7
  * True if the format displays the month as text (LL, MMMM D YYYY, MMM D YYYY).
7
8
  * Used to decide when to treat input as "complete" vs partial and when to sync overlay month from typing.
@@ -91,6 +92,17 @@ export declare function jsDateToTemporalDate(date: Date): Temporal.PlainDate;
91
92
  * parseDateToJsDate("2026-01-28", "MM/DD/YYYY") // ✗ Returns undefined (wrong format)
92
93
  */
93
94
  export declare function parseDateToJsDate(value: string | Date, formatString: string | null | undefined, locale?: string | null | undefined): Date | null | undefined;
95
+ /**
96
+ * Builds a regex (and its day/month/year capture order) for a locale's
97
+ * numeric date pattern, detected via the same Intl.DateTimeFormat options
98
+ * `formatDate` used to produce the value this will parse -- different
99
+ * skeletons can use different separators for the same locale. Numeric parts
100
+ * become capture groups; a literal between two of them is required
101
+ * (disambiguates field boundaries), while a leading/trailing literal (e.g.
102
+ * an era marker) is optional. Returns null if the pattern isn't purely
103
+ * numeric (e.g. a locale whose short format spells out the month).
104
+ */
105
+ export declare function buildNumericDatePattern(localeStr: string, format: string | null | undefined): NumericDatePattern | null;
94
106
  /**
95
107
  * Get the start of the ISO week (Monday) for a given date.
96
108
  * ISO weeks start on Monday (dayOfWeek = 1) and end on Sunday (dayOfWeek = 7).
@@ -1,2 +1,20 @@
1
1
  import { type Matcher } from "react-day-picker";
2
2
  export type CustomModifiers = Record<string, Matcher | Matcher[]>;
3
+ export type DateSegmentType = "day" | "month" | "year";
4
+ export type DateSegment = {
5
+ type: DateSegmentType;
6
+ /** Character offset into the formatted string (inclusive). */
7
+ start: number;
8
+ /** Character offset into the formatted string (exclusive). */
9
+ end: number;
10
+ };
11
+ /** One part of an Intl.DateTimeFormat pattern (see `buildNumericDatePattern`). */
12
+ export type DatePatternPart = {
13
+ type: DateSegmentType | "literal";
14
+ value: string;
15
+ };
16
+ /** A regex (and its day/month/year capture order) for a locale's numeric date pattern. */
17
+ export type NumericDatePattern = {
18
+ regex: RegExp;
19
+ partOrder: Array<DateSegmentType>;
20
+ };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Date picker component for Wonder Blocks.",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "1.0.21",
6
+ "version": "1.1.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -28,10 +28,10 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "react-day-picker": "^9.11.1",
31
- "@khanacademy/wonder-blocks-core": "12.4.4",
32
- "@khanacademy/wonder-blocks-form": "7.6.11",
33
- "@khanacademy/wonder-blocks-icon": "6.0.0",
34
- "@khanacademy/wonder-blocks-modal": "8.7.12",
31
+ "@khanacademy/wonder-blocks-core": "12.5.0",
32
+ "@khanacademy/wonder-blocks-form": "7.6.12",
33
+ "@khanacademy/wonder-blocks-icon-button": "11.5.0",
34
+ "@khanacademy/wonder-blocks-modal": "8.8.1",
35
35
  "@khanacademy/wonder-blocks-styles": "0.2.53",
36
36
  "@khanacademy/wonder-blocks-tokens": "17.3.0"
37
37
  },