@khanacademy/wonder-blocks-date-picker 1.1.6 → 1.2.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,31 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-date-picker
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6bd5d0c: The default `aria-label`s for the DatePicker calendar button and calendar region now read translated strings from `WonderBlocksConfigProvider`. The `calendarButtonAriaLabel` and `calendarGridRegionAriaLabel` props override the built-in translations.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [6bd5d0c]
|
|
12
|
+
- Updated dependencies [8e11e83]
|
|
13
|
+
- Updated dependencies [8e11e83]
|
|
14
|
+
- @khanacademy/wonder-blocks-config@0.5.0
|
|
15
|
+
- @khanacademy/wonder-blocks-modal@8.10.0
|
|
16
|
+
- @khanacademy/wonder-blocks-form@7.7.3
|
|
17
|
+
|
|
18
|
+
## 1.1.7
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [cbdc909]
|
|
23
|
+
- @khanacademy/wonder-blocks-tokens@18.1.0
|
|
24
|
+
- @khanacademy/wonder-blocks-form@7.7.2
|
|
25
|
+
- @khanacademy/wonder-blocks-icon-button@12.0.2
|
|
26
|
+
- @khanacademy/wonder-blocks-modal@8.9.2
|
|
27
|
+
- @khanacademy/wonder-blocks-styles@0.4.1
|
|
28
|
+
|
|
3
29
|
## 1.1.6
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
|
@@ -42,7 +42,8 @@ interface Props {
|
|
|
42
42
|
*/
|
|
43
43
|
onCalendarButtonKeyDown?: (e: React.KeyboardEvent) => unknown;
|
|
44
44
|
/**
|
|
45
|
-
* The aria-label for the calendar toggle button.
|
|
45
|
+
* The aria-label for the calendar toggle button. Overrides the
|
|
46
|
+
* translated string from `WonderBlocksConfigProvider`.
|
|
46
47
|
*/
|
|
47
48
|
calendarButtonAriaLabel?: string;
|
|
48
49
|
/**
|
|
@@ -34,7 +34,9 @@ interface Props {
|
|
|
34
34
|
*/
|
|
35
35
|
style?: StyleType;
|
|
36
36
|
/**
|
|
37
|
-
* The aria-label for the calendar grid region.
|
|
37
|
+
* The aria-label for the calendar grid region. Overrides the translated
|
|
38
|
+
* string from `WonderBlocksConfigProvider` ("Date picker calendar" in
|
|
39
|
+
* English).
|
|
38
40
|
*/
|
|
39
41
|
calendarGridRegionAriaLabel?: string;
|
|
40
42
|
}
|
|
@@ -70,11 +70,14 @@ interface Props {
|
|
|
70
70
|
inputAriaLabel?: string;
|
|
71
71
|
/**
|
|
72
72
|
* The aria-label for the calendar toggle button that opens/closes the
|
|
73
|
-
* calendar overlay.
|
|
73
|
+
* calendar overlay. Overrides the translated string from
|
|
74
|
+
* `WonderBlocksConfigProvider` ("Toggle calendar" in English).
|
|
74
75
|
*/
|
|
75
76
|
calendarButtonAriaLabel?: string;
|
|
76
77
|
/**
|
|
77
|
-
* The aria-label for the calendar grid region.
|
|
78
|
+
* The aria-label for the calendar grid region. Overrides the translated
|
|
79
|
+
* string from `WonderBlocksConfigProvider` ("Date picker calendar" in
|
|
80
|
+
* English).
|
|
78
81
|
*/
|
|
79
82
|
calendarGridRegionAriaLabel?: string;
|
|
80
83
|
/**
|
package/dist/es/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { enUS } from 'react-day-picker/locale';
|
|
|
7
7
|
import { useDirectionDetection, useOnMountEffect, View, findFocusableNodes } from '@khanacademy/wonder-blocks-core';
|
|
8
8
|
import { sizing, semanticColor, border, boxShadow, font } from '@khanacademy/wonder-blocks-tokens';
|
|
9
9
|
import { focusStyles } from '@khanacademy/wonder-blocks-styles';
|
|
10
|
+
import { useWonderBlocksI18n } from '@khanacademy/wonder-blocks-config';
|
|
10
11
|
import { TextField } from '@khanacademy/wonder-blocks-form';
|
|
11
12
|
import IconButton from '@khanacademy/wonder-blocks-icon-button';
|
|
12
13
|
import calendarIcon from '@phosphor-icons/core/bold/calendar-blank-bold.svg';
|
|
@@ -49,11 +50,11 @@ function getDateSegments(value,formatString,locale){if(!value){return null}if(fo
|
|
|
49
50
|
|
|
50
51
|
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
|
|
|
52
|
-
const
|
|
53
|
+
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,calendarButtonRef,...restProps}=props;const{strings}=useWonderBlocksI18n();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||strings.iconAltToggleCalendar,"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}});
|
|
53
54
|
|
|
54
55
|
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"}})]})}
|
|
55
56
|
|
|
56
|
-
const
|
|
57
|
+
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})=>{const{strings}=useWonderBlocksI18n();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||strings.datePickerCalendar,ref:ref,role:"region",style:combinedStyles,"data-placement":placement,children:children})}})}),modalHost)};
|
|
57
58
|
|
|
58
59
|
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}});
|
|
59
60
|
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ 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
13
|
var wonderBlocksStyles = require('@khanacademy/wonder-blocks-styles');
|
|
14
|
+
var wonderBlocksConfig = require('@khanacademy/wonder-blocks-config');
|
|
14
15
|
var wonderBlocksForm = require('@khanacademy/wonder-blocks-form');
|
|
15
16
|
var IconButton = require('@khanacademy/wonder-blocks-icon-button');
|
|
16
17
|
var calendarIcon = require('@phosphor-icons/core/bold/calendar-blank-bold.svg');
|
|
@@ -77,11 +78,11 @@ function getDateSegments(value,formatString,locale){if(!value){return null}if(fo
|
|
|
77
78
|
|
|
78
79
|
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
|
|
|
80
|
-
const
|
|
81
|
+
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,calendarButtonRef,...restProps}=props;const{strings}=wonderBlocksConfig.useWonderBlocksI18n();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||strings.iconAltToggleCalendar,"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}});
|
|
81
82
|
|
|
82
83
|
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"}})]})}
|
|
83
84
|
|
|
84
|
-
const
|
|
85
|
+
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})=>{const{strings}=wonderBlocksConfig.useWonderBlocksI18n();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||strings.datePickerCalendar,ref:ref,role:"region",style:combinedStyles,"data-placement":placement,children:children})}})}),modalHost)};
|
|
85
86
|
|
|
86
87
|
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}});
|
|
87
88
|
|
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.
|
|
6
|
+
"version": "1.2.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -28,12 +28,13 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"react-day-picker": "^9.11.1",
|
|
31
|
+
"@khanacademy/wonder-blocks-config": "0.5.0",
|
|
31
32
|
"@khanacademy/wonder-blocks-core": "12.5.0",
|
|
32
|
-
"@khanacademy/wonder-blocks-form": "7.7.
|
|
33
|
-
"@khanacademy/wonder-blocks-icon-button": "12.0.
|
|
34
|
-
"@khanacademy/wonder-blocks-modal": "8.
|
|
35
|
-
"@khanacademy/wonder-blocks-styles": "0.4.
|
|
36
|
-
"@khanacademy/wonder-blocks-tokens": "18.
|
|
33
|
+
"@khanacademy/wonder-blocks-form": "7.7.3",
|
|
34
|
+
"@khanacademy/wonder-blocks-icon-button": "12.0.2",
|
|
35
|
+
"@khanacademy/wonder-blocks-modal": "8.10.0",
|
|
36
|
+
"@khanacademy/wonder-blocks-styles": "0.4.1",
|
|
37
|
+
"@khanacademy/wonder-blocks-tokens": "18.1.0"
|
|
37
38
|
},
|
|
38
39
|
"peerDependencies": {
|
|
39
40
|
"@phosphor-icons/core": "^2.0.2",
|