@khanacademy/wonder-blocks-date-picker 0.1.2 → 1.0.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 +19 -0
- package/dist/components/date-picker-input.d.ts +6 -3
- package/dist/components/date-picker-overlay.d.ts +6 -1
- package/dist/components/date-picker.d.ts +6 -3
- package/dist/es/index.js +20 -4
- package/dist/hooks/use-close-on-outside-click.d.ts +19 -0
- package/dist/hooks/use-date-picker-modifiers.d.ts +14 -0
- package/dist/hooks/use-display-month.d.ts +21 -0
- package/dist/hooks/use-escape-keyup-capture.d.ts +15 -0
- package/dist/hooks/use-format-date-for-input.d.ts +12 -0
- package/dist/hooks/use-overlay-month-from-input.d.ts +26 -0
- package/dist/hooks/use-selected-date-sync.d.ts +17 -0
- package/dist/index.js +20 -4
- package/dist/util/date-picker-helpers.d.ts +6 -0
- package/dist/util/temporal-locale-utils.d.ts +17 -4
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-date-picker
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 76f6e41: Date picker bugfixes: change dateFormat to string formats only, adjust inline padding, remove default width, fix key handling so Escape will only close date picker overlay and not a parent modal
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- 9fd7228: Adjusts input validation for text-based date formats, improves focus management of next/previous month buttons
|
|
12
|
+
|
|
13
|
+
## 0.1.3
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 12e04c3: Update component documentation for the `disabled` props to include details about `aria-disabled` being set internally to keep an element focusable while in a disabled state
|
|
18
|
+
- Updated dependencies [12e04c3]
|
|
19
|
+
- @khanacademy/wonder-blocks-form@7.5.4
|
|
20
|
+
- @khanacademy/wonder-blocks-modal@8.5.15
|
|
21
|
+
|
|
3
22
|
## 0.1.2
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -7,6 +7,9 @@ interface Props {
|
|
|
7
7
|
value: string | null | undefined;
|
|
8
8
|
/**
|
|
9
9
|
* Whether the input element is disabled.
|
|
10
|
+
*
|
|
11
|
+
* Internally, the `aria-disabled` attribute will be set so that the
|
|
12
|
+
* element remains focusable and will be included in the tab order.
|
|
10
13
|
*/
|
|
11
14
|
disabled?: boolean;
|
|
12
15
|
/**
|
|
@@ -36,11 +39,11 @@ interface Props {
|
|
|
36
39
|
*
|
|
37
40
|
* NOTE: `value` will be null if an invalid date has been entered.
|
|
38
41
|
*/
|
|
39
|
-
onChange?: (value: Date | null | undefined, modifiers: Partial<CustomModifiers
|
|
42
|
+
onChange?: (value: Date | null | undefined, modifiers: Partial<CustomModifiers>, inputValue?: string) => unknown;
|
|
40
43
|
/**
|
|
41
44
|
* Used to format the value as a valid Date.
|
|
42
45
|
*/
|
|
43
|
-
dateFormat?:
|
|
46
|
+
dateFormat?: string;
|
|
44
47
|
/**
|
|
45
48
|
* The locale associated to the current Date.
|
|
46
49
|
*/
|
|
@@ -73,7 +76,7 @@ interface Props {
|
|
|
73
76
|
* parseDate("2024-12-25", "YYYY-MM-DD", "en-US") // => Date object
|
|
74
77
|
* parseDate("invalid", "YYYY-MM-DD", "en-US") // => null
|
|
75
78
|
*/
|
|
76
|
-
parseDate?: (value: string | Date, format:
|
|
79
|
+
parseDate?: (value: string | Date, format: string | null | undefined, locale?: string | null | undefined) => Date | null | undefined;
|
|
77
80
|
/**
|
|
78
81
|
* The placeholder assigned to the date field
|
|
79
82
|
*/
|
|
@@ -13,6 +13,11 @@ interface Props {
|
|
|
13
13
|
* The reference element used to position the popper.
|
|
14
14
|
*/
|
|
15
15
|
referenceElement: HTMLElement | null | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Text direction: when "rtl", the overlay is positioned at the end (e.g. bottom-end)
|
|
18
|
+
* so it aligns with the input in RTL layout. Defaults to "ltr" (bottom-start).
|
|
19
|
+
*/
|
|
20
|
+
dir?: "ltr" | "rtl";
|
|
16
21
|
/**
|
|
17
22
|
* Styles that will be applied to the children.
|
|
18
23
|
*/
|
|
@@ -26,5 +31,5 @@ interface Props {
|
|
|
26
31
|
* calendar popup in the current view. This includes using it inside a normal
|
|
27
32
|
* page or inside a Modal component.
|
|
28
33
|
*/
|
|
29
|
-
declare const DatePickerOverlay: ({ children, referenceElement, onClose, style, }: Props) => React.ReactElement | null;
|
|
34
|
+
declare const DatePickerOverlay: ({ children, referenceElement, onClose, dir, style, }: Props) => React.ReactElement | null;
|
|
30
35
|
export default DatePickerOverlay;
|
|
@@ -31,10 +31,10 @@ interface Props {
|
|
|
31
31
|
updateDate: (arg1?: Temporal.PlainDate | null | undefined) => any;
|
|
32
32
|
/**
|
|
33
33
|
* Used to format the value as a valid Date.
|
|
34
|
-
*
|
|
34
|
+
* When nullish (undefined or omitted), defaults to locale-aware short date (same as "L").
|
|
35
35
|
*
|
|
36
36
|
* Supported formats:
|
|
37
|
-
* - **undefined
|
|
37
|
+
* - **undefined** (omit or pass undefined): Locale-aware short date (same as "L")
|
|
38
38
|
* - **"L"**: Locale-aware short date (e.g., "1/20/2026" in en-US, "20.01.2026" in de-DE, "20/01/2026" in bg)
|
|
39
39
|
* - **"LL"**: Locale-aware long date (e.g., "January 20, 2026" in en-US, "20 de enero de 2026" in es)
|
|
40
40
|
* - Supports manual text editing using locale-specific month names
|
|
@@ -42,9 +42,12 @@ interface Props {
|
|
|
42
42
|
* - **"MMMM D, YYYY"**: Text format (e.g., "January 20, 2026") - month name localized but US order
|
|
43
43
|
* - **"dateStyle:short|medium|long|full"**: Explicit Intl.DateTimeFormat dateStyle values
|
|
44
44
|
*/
|
|
45
|
-
dateFormat?:
|
|
45
|
+
dateFormat?: string;
|
|
46
46
|
/**
|
|
47
47
|
* Whether the DatePicker component is disabled.
|
|
48
|
+
*
|
|
49
|
+
* Internally, the `aria-disabled` attribute will be set so that the
|
|
50
|
+
* element remains focusable and will be included in the tab order.
|
|
48
51
|
*/
|
|
49
52
|
disabled?: boolean;
|
|
50
53
|
/**
|
package/dist/es/index.js
CHANGED
|
@@ -14,14 +14,30 @@ import { Popper } from 'react-popper';
|
|
|
14
14
|
import { maybeGetPortalMountedModalHostElement } from '@khanacademy/wonder-blocks-modal';
|
|
15
15
|
import 'react-day-picker/style.css';
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
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
18
|
|
|
19
|
-
const
|
|
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
|
+
|
|
21
|
+
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
|
+
|
|
23
|
+
function useDisplayMonth({selectedDate}){const initialMonth=React.useMemo(()=>selectedDate!=null?TemporalLocaleUtils.temporalDateToJsDate(selectedDate):undefined,[selectedDate]);const[displayMonth,setDisplayMonth]=React.useState(initialMonth);const displayMonthRef=React.useRef(undefined);const inputDrivenMonthRef=React.useRef(null);const setDisplayMonthAndRefs=React.useCallback(month=>{if(month!==null){displayMonthRef.current=month;setDisplayMonth(month);}else {if(displayMonthRef.current!=null){setDisplayMonth(displayMonthRef.current);}displayMonthRef.current=undefined;}},[]);return {displayMonth,setDisplayMonth,displayMonthRef,inputDrivenMonthRef,setDisplayMonthAndRefs}}
|
|
24
|
+
|
|
25
|
+
function useEscapeKeyupCapture(){const handledEscapeRef=React.useRef(false);React.useEffect(()=>{const handleKeyup=e=>{if(e.key==="Escape"&&handledEscapeRef.current){e.stopPropagation();handledEscapeRef.current=false;}};window.addEventListener("keyup",handleKeyup,true);return ()=>{window.removeEventListener("keyup",handleKeyup,true);}},[]);const handleEscapeKeyDown=React.useCallback((e,onHandled)=>{e.stopPropagation();handledEscapeRef.current=true;onHandled?.();},[]);return {handleEscapeKeyDown}}
|
|
26
|
+
|
|
27
|
+
function useFormatDateForInput({dateFormat,locale}){const localeCode=locale?.code??"en-US";return React.useCallback(date=>{if(date==null){return ""}return TemporalLocaleUtils.formatDate(date,dateFormat,localeCode)},[dateFormat,localeCode])}
|
|
28
|
+
|
|
29
|
+
function isTextFormatCommitComplete(inputValue,dateFromInput,dateFormat,localeCode){if(typeof inputValue!=="string"||inputValue.trim()===""){return false}const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(dateFromInput);const reparsed=TemporalLocaleUtils.parseDate(inputValue,dateFormat,localeCode??undefined);const sameDate=reparsed!=null&&Temporal.PlainDate.compare(reparsed,wrappedDate)===0;const formatted=TemporalLocaleUtils.formatDate(wrappedDate,dateFormat,localeCode??undefined);if(typeof formatted!=="string"){return false}const inputMatchesDisplay=TemporalLocaleUtils.normalizeDateStringForComparison(formatted)===TemporalLocaleUtils.normalizeDateStringForComparison(inputValue);return sameDate&&inputMatchesDisplay}
|
|
30
|
+
|
|
31
|
+
function useOverlayMonthFromInput({inputDrivenMonthRef,setDisplayMonth,setDisplayMonthAndRefs,setCurrentDate,updateDate,dateFormat,localeCode}){const clearInputDrivenMonth=React.useCallback(()=>{inputDrivenMonthRef.current=null;},[inputDrivenMonthRef]);const handleInputChange=React.useCallback((dateFromInput,modifiers,inputValue)=>{if(!dateFromInput){setCurrentDate(null);updateDate(null);return}const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(dateFromInput);const monthDate=new Date(dateFromInput);setDisplayMonth(monthDate);const isDisabled=typeof modifiers.disabled==="function"?modifiers.disabled(dateFromInput):modifiers.disabled;if(isDisabled){inputDrivenMonthRef.current=monthDate;updateDate(wrappedDate);return}const isTextFormat=TemporalLocaleUtils.isTextFormatDate(dateFormat);if(isTextFormat&&inputValue){const isCompleteDate=isTextFormatCommitComplete(inputValue,dateFromInput,dateFormat,localeCode);if(isCompleteDate){inputDrivenMonthRef.current=null;setDisplayMonthAndRefs(monthDate);setCurrentDate(wrappedDate);updateDate(wrappedDate);}else {inputDrivenMonthRef.current=monthDate;}}else {inputDrivenMonthRef.current=null;setDisplayMonthAndRefs(monthDate);setCurrentDate(wrappedDate);updateDate(wrappedDate);}},[inputDrivenMonthRef,setDisplayMonth,setDisplayMonthAndRefs,setCurrentDate,updateDate,dateFormat,localeCode]);return {handleInputChange,clearInputDrivenMonth}}
|
|
32
|
+
|
|
33
|
+
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
|
+
|
|
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}});
|
|
20
36
|
|
|
21
37
|
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"}})]})}
|
|
22
38
|
|
|
23
|
-
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,style=DEFAULT_STYLE})=>{if(!referenceElement){return null}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:
|
|
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)};
|
|
24
40
|
|
|
25
|
-
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
|
|
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}});
|
|
26
42
|
|
|
27
43
|
export { DatePicker, TemporalLocaleUtils };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
type RefsForOutsideClick = {
|
|
3
|
+
/** Wrapper that contains the input (and is the "inside" boundary). */
|
|
4
|
+
refWrapper: React.RefObject<HTMLDivElement | null>;
|
|
5
|
+
/** Element that contains the calendar (may be inside a portal). */
|
|
6
|
+
datePickerRef: React.RefObject<HTMLElement | null>;
|
|
7
|
+
};
|
|
8
|
+
type Params = RefsForOutsideClick & {
|
|
9
|
+
showOverlay: boolean;
|
|
10
|
+
closeOnSelect: boolean;
|
|
11
|
+
close: () => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Subscribes to document mouseup and closes the overlay when the click is outside
|
|
15
|
+
* the wrapper and outside the calendar (including portaled overlay). Clear boundary:
|
|
16
|
+
* only handles outside-click-to-close; does not manage overlay state.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useCloseOnOutsideClick({ refWrapper, datePickerRef, showOverlay, closeOnSelect, close, }: Params): void;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import type { CustomModifiers } from "../util/types";
|
|
3
|
+
type Params = {
|
|
4
|
+
/** Selected date as JS Date for react-day-picker, or undefined if none. */
|
|
5
|
+
selectedDateValue: Date | undefined;
|
|
6
|
+
minDate?: Temporal.PlainDate | null;
|
|
7
|
+
maxDate?: Temporal.PlainDate | null;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Pure derivation: returns the modifiers object for react-day-picker (selected day
|
|
11
|
+
* and disabled function based on min/max). No refs, no side effects.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useDatePickerModifiers({ selectedDateValue, minDate, maxDate, }: Params): Partial<CustomModifiers>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Temporal } from "temporal-polyfill";
|
|
3
|
+
type SetDisplayMonthAndRefs = (month: Date | null) => void;
|
|
4
|
+
type Params = {
|
|
5
|
+
/** Initial display month when selectedDate is set; used for sync when selectedDate changes. */
|
|
6
|
+
selectedDate: Temporal.PlainDate | null | undefined;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Manages which month the calendar overlay shows: state, refs for synchronous
|
|
10
|
+
* reads (open/close/first render), and input-driven month when user is typing.
|
|
11
|
+
* Exposes setDisplayMonthAndRefs for open/close/input/day-click to call.
|
|
12
|
+
* Big but cohesive; open/close/input/day-click refactor to call this API.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useDisplayMonth({ selectedDate }: Params): {
|
|
15
|
+
displayMonth: Date | undefined;
|
|
16
|
+
setDisplayMonth: React.Dispatch<React.SetStateAction<Date | undefined>>;
|
|
17
|
+
displayMonthRef: React.MutableRefObject<Date | undefined>;
|
|
18
|
+
inputDrivenMonthRef: React.MutableRefObject<Date | null>;
|
|
19
|
+
setDisplayMonthAndRefs: SetDisplayMonthAndRefs;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type HandleEscapeKeyDown = (e: React.KeyboardEvent | KeyboardEvent, onHandled?: () => void) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Provides a single Escape keydown handler that:
|
|
5
|
+
* 1. Stops keydown propagation
|
|
6
|
+
* 2. Marks Escape as "handled" so a keyup listener can stop keyup propagation (e.g. so a parent modal doesn't also close)
|
|
7
|
+
* 3. Runs your callback (e.g. close overlay, focus input)
|
|
8
|
+
*
|
|
9
|
+
* Call this from your keydown handlers (input, calendar root, etc.) when the user presses Escape.
|
|
10
|
+
* The hook registers a keyup listener in the capture phase; when it sees Escape and you've already
|
|
11
|
+
* called handleEscapeKeyDown for that keypress, it stops keyup propagation and clears the internal state.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useEscapeKeyupCapture(): {
|
|
14
|
+
handleEscapeKeyDown: HandleEscapeKeyDown;
|
|
15
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import type { Locale } from "react-day-picker/locale";
|
|
3
|
+
type Params = {
|
|
4
|
+
dateFormat?: string;
|
|
5
|
+
locale?: Locale;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Returns a stable callback that formats the current date for the input field.
|
|
9
|
+
* Uses dateFormat and locale; returns "" for null/undefined date.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useFormatDateForInput({ dateFormat, locale, }: Params): (date: Temporal.PlainDate | null | undefined) => string;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import type { CustomModifiers } from "../util/types";
|
|
4
|
+
type SetDisplayMonthAndRefs = (month: Date | null) => void;
|
|
5
|
+
export type HandleInputChange = (dateFromInput: Date | null | undefined, modifiers: Partial<CustomModifiers>, inputValue?: string) => void;
|
|
6
|
+
type Params = {
|
|
7
|
+
inputDrivenMonthRef: React.MutableRefObject<Date | null>;
|
|
8
|
+
setDisplayMonth: React.Dispatch<React.SetStateAction<Date | undefined>>;
|
|
9
|
+
setDisplayMonthAndRefs: SetDisplayMonthAndRefs;
|
|
10
|
+
setCurrentDate: React.Dispatch<React.SetStateAction<Temporal.PlainDate | null | undefined>>;
|
|
11
|
+
updateDate: (date: Temporal.PlainDate | null) => void;
|
|
12
|
+
dateFormat: string | undefined;
|
|
13
|
+
localeCode: string | undefined;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Encapsulates "input-driven overlay month" logic: while the user types, the
|
|
17
|
+
* calendar month follows the input; when they commit a date (or we clear),
|
|
18
|
+
* the overlay month is no longer driven by input so prev/next buttons work.
|
|
19
|
+
* Returns handleInputChange (for the input's onChange) and clearInputDrivenMonth
|
|
20
|
+
* (call when closing, changing month via nav, or selecting a day).
|
|
21
|
+
*/
|
|
22
|
+
export declare function useOverlayMonthFromInput({ inputDrivenMonthRef, setDisplayMonth, setDisplayMonthAndRefs, setCurrentDate, updateDate, dateFormat, localeCode, }: Params): {
|
|
23
|
+
handleInputChange: HandleInputChange;
|
|
24
|
+
clearInputDrivenMonth: () => void;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
type SetDisplayMonthAndRefs = (month: Date | null) => void;
|
|
4
|
+
type Params = {
|
|
5
|
+
selectedDate: Temporal.PlainDate | null | undefined;
|
|
6
|
+
setCurrentDate: React.Dispatch<React.SetStateAction<Temporal.PlainDate | null | undefined>>;
|
|
7
|
+
setDisplayMonthAndRefs: SetDisplayMonthAndRefs;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Single effect: keeps currentDate in sync with selectedDate prop.
|
|
11
|
+
* When selectedDate identity changes, also updates display month so the calendar
|
|
12
|
+
* shows the right month. Uses prevSelectedDateRef so displayMonth is only
|
|
13
|
+
* updated when selectedDate actually changed (not overwritten by typing or by
|
|
14
|
+
* using the calendar’s previous/next month buttons).
|
|
15
|
+
*/
|
|
16
|
+
export declare function useSelectedDateSync({ selectedDate, setCurrentDate, setDisplayMonthAndRefs, }: Params): void;
|
|
17
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -41,15 +41,31 @@ function _interopNamespace(e) {
|
|
|
41
41
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
42
42
|
var calendarIcon__default = /*#__PURE__*/_interopDefaultLegacy(calendarIcon);
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
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
45
|
|
|
46
|
-
const
|
|
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};
|
|
47
|
+
|
|
48
|
+
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
|
+
|
|
50
|
+
function useDisplayMonth({selectedDate}){const initialMonth=React__namespace.useMemo(()=>selectedDate!=null?TemporalLocaleUtils.temporalDateToJsDate(selectedDate):undefined,[selectedDate]);const[displayMonth,setDisplayMonth]=React__namespace.useState(initialMonth);const displayMonthRef=React__namespace.useRef(undefined);const inputDrivenMonthRef=React__namespace.useRef(null);const setDisplayMonthAndRefs=React__namespace.useCallback(month=>{if(month!==null){displayMonthRef.current=month;setDisplayMonth(month);}else {if(displayMonthRef.current!=null){setDisplayMonth(displayMonthRef.current);}displayMonthRef.current=undefined;}},[]);return {displayMonth,setDisplayMonth,displayMonthRef,inputDrivenMonthRef,setDisplayMonthAndRefs}}
|
|
51
|
+
|
|
52
|
+
function useEscapeKeyupCapture(){const handledEscapeRef=React__namespace.useRef(false);React__namespace.useEffect(()=>{const handleKeyup=e=>{if(e.key==="Escape"&&handledEscapeRef.current){e.stopPropagation();handledEscapeRef.current=false;}};window.addEventListener("keyup",handleKeyup,true);return ()=>{window.removeEventListener("keyup",handleKeyup,true);}},[]);const handleEscapeKeyDown=React__namespace.useCallback((e,onHandled)=>{e.stopPropagation();handledEscapeRef.current=true;onHandled?.();},[]);return {handleEscapeKeyDown}}
|
|
53
|
+
|
|
54
|
+
function useFormatDateForInput({dateFormat,locale}){const localeCode=locale?.code??"en-US";return React__namespace.useCallback(date=>{if(date==null){return ""}return TemporalLocaleUtils.formatDate(date,dateFormat,localeCode)},[dateFormat,localeCode])}
|
|
55
|
+
|
|
56
|
+
function isTextFormatCommitComplete(inputValue,dateFromInput,dateFormat,localeCode){if(typeof inputValue!=="string"||inputValue.trim()===""){return false}const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(dateFromInput);const reparsed=TemporalLocaleUtils.parseDate(inputValue,dateFormat,localeCode??undefined);const sameDate=reparsed!=null&&temporalPolyfill.Temporal.PlainDate.compare(reparsed,wrappedDate)===0;const formatted=TemporalLocaleUtils.formatDate(wrappedDate,dateFormat,localeCode??undefined);if(typeof formatted!=="string"){return false}const inputMatchesDisplay=TemporalLocaleUtils.normalizeDateStringForComparison(formatted)===TemporalLocaleUtils.normalizeDateStringForComparison(inputValue);return sameDate&&inputMatchesDisplay}
|
|
57
|
+
|
|
58
|
+
function useOverlayMonthFromInput({inputDrivenMonthRef,setDisplayMonth,setDisplayMonthAndRefs,setCurrentDate,updateDate,dateFormat,localeCode}){const clearInputDrivenMonth=React__namespace.useCallback(()=>{inputDrivenMonthRef.current=null;},[inputDrivenMonthRef]);const handleInputChange=React__namespace.useCallback((dateFromInput,modifiers,inputValue)=>{if(!dateFromInput){setCurrentDate(null);updateDate(null);return}const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(dateFromInput);const monthDate=new Date(dateFromInput);setDisplayMonth(monthDate);const isDisabled=typeof modifiers.disabled==="function"?modifiers.disabled(dateFromInput):modifiers.disabled;if(isDisabled){inputDrivenMonthRef.current=monthDate;updateDate(wrappedDate);return}const isTextFormat=TemporalLocaleUtils.isTextFormatDate(dateFormat);if(isTextFormat&&inputValue){const isCompleteDate=isTextFormatCommitComplete(inputValue,dateFromInput,dateFormat,localeCode);if(isCompleteDate){inputDrivenMonthRef.current=null;setDisplayMonthAndRefs(monthDate);setCurrentDate(wrappedDate);updateDate(wrappedDate);}else {inputDrivenMonthRef.current=monthDate;}}else {inputDrivenMonthRef.current=null;setDisplayMonthAndRefs(monthDate);setCurrentDate(wrappedDate);updateDate(wrappedDate);}},[inputDrivenMonthRef,setDisplayMonth,setDisplayMonthAndRefs,setCurrentDate,updateDate,dateFormat,localeCode]);return {handleInputChange,clearInputDrivenMonth}}
|
|
59
|
+
|
|
60
|
+
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
|
+
|
|
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}});
|
|
47
63
|
|
|
48
64
|
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"}})]})}
|
|
49
65
|
|
|
50
|
-
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,style=DEFAULT_STYLE})=>{if(!referenceElement){return null}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:
|
|
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)};
|
|
51
67
|
|
|
52
|
-
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
|
|
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}});
|
|
53
69
|
|
|
54
70
|
exports.DatePicker = DatePicker;
|
|
55
71
|
exports.TemporalLocaleUtils = TemporalLocaleUtils;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true when the user has finished typing a full date in a text format
|
|
3
|
+
* (LL, MMMM D YYYY, MMM D YYYY): reparsed date matches, and normalized input
|
|
4
|
+
* matches the formatted date.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isTextFormatCommitComplete(inputValue: string, dateFromInput: Date, dateFormat: string | null | undefined, localeCode?: string | null): boolean;
|
|
@@ -2,6 +2,18 @@ import { Temporal } from "temporal-polyfill";
|
|
|
2
2
|
import type { Locale } from "react-day-picker/locale";
|
|
3
3
|
import { CustomModifiers } from "./types";
|
|
4
4
|
export declare const enUSLocaleCode = "en-US";
|
|
5
|
+
/**
|
|
6
|
+
* True if the format displays the month as text (LL, MMMM D YYYY, MMM D YYYY).
|
|
7
|
+
* Used to decide when to treat input as "complete" vs partial and when to sync overlay month from typing.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isTextFormatDate(formatString: string | null | undefined): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Normalize a date string for comparison (e.g. round-trip or commit checks).
|
|
12
|
+
* Trims, lowercases, collapses whitespace, and replaces common punctuation
|
|
13
|
+
* (comma, period, narrow no-break space) with space so minor formatting
|
|
14
|
+
* differences don't invalidate the input.
|
|
15
|
+
*/
|
|
16
|
+
export declare function normalizeDateStringForComparison(s: string): string;
|
|
5
17
|
/**
|
|
6
18
|
* Utility functions for working with Temporal dates in react-day-picker.
|
|
7
19
|
* Uses Intl.DateTimeFormat for locale-aware date formatting and parsing.
|
|
@@ -49,12 +61,11 @@ export declare const enUSLocaleCode = "en-US";
|
|
|
49
61
|
* automatically falls back to ISO 8601 format and logs a warning to the console.
|
|
50
62
|
* This ensures the function never throws errors and always returns a valid date string.
|
|
51
63
|
*/
|
|
52
|
-
export declare function formatDate(date: Temporal.PlainDate,
|
|
64
|
+
export declare function formatDate(date: Temporal.PlainDate, formatString: string | null | undefined, locale?: Locale | string): string;
|
|
53
65
|
/**
|
|
54
66
|
* Parse a date string into a Temporal.PlainDate.
|
|
55
|
-
* Attempts multiple formats if an array is provided.
|
|
56
67
|
*/
|
|
57
|
-
export declare function parseDate(str: string,
|
|
68
|
+
export declare function parseDate(str: string, formatString: string | null | undefined, locale?: string): Temporal.PlainDate | undefined;
|
|
58
69
|
export declare const getModifiersForDay: (day: Date, modifiers: Partial<CustomModifiers>) => Array<string>;
|
|
59
70
|
/**
|
|
60
71
|
* Convert a Temporal.PlainDate to a JavaScript Date object.
|
|
@@ -79,7 +90,7 @@ export declare function jsDateToTemporalDate(date: Date): Temporal.PlainDate;
|
|
|
79
90
|
* parseDateToJsDate("1/28", "MM/DD/YYYY") // ✗ Returns undefined (incomplete)
|
|
80
91
|
* parseDateToJsDate("2026-01-28", "MM/DD/YYYY") // ✗ Returns undefined (wrong format)
|
|
81
92
|
*/
|
|
82
|
-
export declare function parseDateToJsDate(value: string | Date,
|
|
93
|
+
export declare function parseDateToJsDate(value: string | Date, formatString: string | null | undefined, locale?: string | null | undefined): Date | null | undefined;
|
|
83
94
|
/**
|
|
84
95
|
* Get the start of the ISO week (Monday) for a given date.
|
|
85
96
|
* ISO weeks start on Monday (dayOfWeek = 1) and end on Sunday (dayOfWeek = 7).
|
|
@@ -105,6 +116,8 @@ export declare const endOfDay: (date: Date) => Date;
|
|
|
105
116
|
*/
|
|
106
117
|
export declare const TemporalLocaleUtils: {
|
|
107
118
|
formatDate: typeof formatDate;
|
|
119
|
+
isTextFormatDate: typeof isTextFormatDate;
|
|
120
|
+
normalizeDateStringForComparison: typeof normalizeDateStringForComparison;
|
|
108
121
|
parseDate: typeof parseDate;
|
|
109
122
|
parseDateToJsDate: typeof parseDateToJsDate;
|
|
110
123
|
startOfIsoWeek: (date: Temporal.PlainDate) => Temporal.PlainDate;
|
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": "0.
|
|
6
|
+
"version": "1.0.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -29,16 +29,17 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"react-day-picker": "^9.11.1",
|
|
31
31
|
"@khanacademy/wonder-blocks-core": "12.4.3",
|
|
32
|
+
"@khanacademy/wonder-blocks-form": "7.5.4",
|
|
32
33
|
"@khanacademy/wonder-blocks-icon": "5.3.7",
|
|
33
|
-
"@khanacademy/wonder-blocks-
|
|
34
|
-
"@khanacademy/wonder-blocks-
|
|
35
|
-
"@khanacademy/wonder-blocks-tokens": "15.0.0"
|
|
36
|
-
"@khanacademy/wonder-blocks-styles": "0.2.38"
|
|
34
|
+
"@khanacademy/wonder-blocks-modal": "8.5.15",
|
|
35
|
+
"@khanacademy/wonder-blocks-styles": "0.2.38",
|
|
36
|
+
"@khanacademy/wonder-blocks-tokens": "15.0.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@phosphor-icons/core": "^2.0.2",
|
|
40
40
|
"@popperjs/core": "^2.10.1",
|
|
41
41
|
"aphrodite": "^1.2.5",
|
|
42
|
+
"date-fns": "^4.1.0",
|
|
42
43
|
"react": "18.2.0",
|
|
43
44
|
"react-dom": "18.2.0",
|
|
44
45
|
"react-popper": "^2.3.0",
|