@khanacademy/wonder-blocks-date-picker 0.0.0-PR2904-20251212184846 → 0.0.0-PR2904-20251212210336
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 +6 -6
- package/dist/components/date-picker.d.ts +0 -7
- package/dist/es/index.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-date-picker
|
|
2
2
|
|
|
3
|
-
## 0.0.0-PR2904-
|
|
3
|
+
## 0.0.0-PR2904-20251212210336
|
|
4
4
|
|
|
5
5
|
### Minor Changes
|
|
6
6
|
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
### Patch Changes
|
|
10
10
|
|
|
11
|
-
-
|
|
11
|
+
- 5845312: Finalizes support for DatePicker Locales
|
|
12
12
|
- Updated dependencies [19e4c20]
|
|
13
|
-
- @khanacademy/wonder-blocks-core@0.0.0-PR2904-
|
|
14
|
-
- @khanacademy/wonder-blocks-form@0.0.0-PR2904-
|
|
15
|
-
- @khanacademy/wonder-blocks-icon@0.0.0-PR2904-
|
|
16
|
-
- @khanacademy/wonder-blocks-modal@0.0.0-PR2904-
|
|
13
|
+
- @khanacademy/wonder-blocks-core@0.0.0-PR2904-20251212210336
|
|
14
|
+
- @khanacademy/wonder-blocks-form@0.0.0-PR2904-20251212210336
|
|
15
|
+
- @khanacademy/wonder-blocks-icon@0.0.0-PR2904-20251212210336
|
|
16
|
+
- @khanacademy/wonder-blocks-modal@0.0.0-PR2904-20251212210336
|
|
17
17
|
- @khanacademy/wonder-blocks-styles@0.2.37
|
|
18
18
|
- @khanacademy/wonder-blocks-tokens@14.1.3
|
|
@@ -9,13 +9,6 @@ interface Props {
|
|
|
9
9
|
* If not provided, it will fall back to enUS.
|
|
10
10
|
*/
|
|
11
11
|
locale?: Locale;
|
|
12
|
-
/**
|
|
13
|
-
* Optional writing direction to pass in as a prop.
|
|
14
|
-
|
|
15
|
-
* Useful for overlays that render outside of normal tree with `dir="rtl"`,
|
|
16
|
-
* such as in consumer stories.
|
|
17
|
-
*/
|
|
18
|
-
dir?: "ltr" | "rtl";
|
|
19
12
|
/**
|
|
20
13
|
* When the selected date changes, this callback is passsed a Temporal object
|
|
21
14
|
* for midnight on the selected date, set to the user's local time zone.
|
package/dist/es/index.js
CHANGED
|
@@ -17,10 +17,10 @@ const enUSLocaleCode="en-US";function formatDate(date,format,locale=enUSLocaleCo
|
|
|
17
17
|
|
|
18
18
|
const DatePickerInput=React.forwardRef((props,ref)=>{const{value:propValue,onBlur,onClick,onFocus,onKeyDown,onChange,dateFormat,locale=enUSLocaleCode,modifiers,getModifiersForDay,parseDate,placeholder,testId,["aria-label"]:ariaLabel,...restProps}=props;const[value,setValue]=React.useState(propValue);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,value)=>{if(onChange){onChange(date,processModifiers(date,value));}},[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 maybeUpdateDate=React.useCallback(inputValue=>{const date=processDate(inputValue);if(date){updateDate(date,inputValue);}else {updateDateAsInvalid();}},[processDate,updateDate,updateDateAsInvalid]);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]);React.useEffect(()=>{setValue(propValue);},[propValue]);useOnMountEffect(()=>{if(!isValid()){updateDateAsInvalid();}});const handleBlur=e=>{if(!isValid()){setValue(propValue);}if(onBlur){onBlur(e);}};const handleChange=newValue=>{maybeUpdateDate(newValue);setValue(newValue);};return jsxs(View,{style:styles$1.container,onClick:e=>{if(!restProps.disabled&&onClick){onClick(e);}},children:[jsx(TextField,{ref:ref,...restProps,onBlur:handleBlur,onFocus:onFocus,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 styles$1=StyleSheet.create({container:{alignItems:"center",flexDirection:"row",justifyContent:"stretch"},icon:{pointerEvents:"none",position:"absolute",insetInlineEnd:sizing.size_080},textField:{width:"100%"}});
|
|
19
19
|
|
|
20
|
-
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){e.preventDefault();focusableElementsInsideRef.current[0]?.focus();}};const handleKeydownNextFocusableElement=e=>{if(e.key==="Tab"&&e.shiftKey){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",{ref:setComponentRootNode,children:children}),jsx("div",{tabIndex:0,"data-testid":"focus-sentinel-next",onFocus:handleFocusNextFocusableElement,style:{position:"fixed"}})]})}
|
|
20
|
+
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){e.preventDefault();focusableElementsInsideRef.current[0]?.focus();}};const handleKeydownNextFocusableElement=e=>{if(e.key==="Tab"&&e.shiftKey){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"}})]})}
|
|
21
21
|
|
|
22
22
|
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={pointerEvents:"none",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:"bottom-start",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)};
|
|
23
23
|
|
|
24
|
-
const customRootStyle={"--rdp-accent-color":semanticColor.core.border.instructive.default};const DatePicker=props=>{const{locale,
|
|
24
|
+
const customRootStyle={"--rdp-accent-color":semanticColor.core.border.instructive.default};const DatePicker=props=>{const{locale,updateDate,dateFormat,disabled,id,maxDate,minDate,inputAriaLabel="Choose or enter a date",placeholder,selectedDate,style,closeOnSelect=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 open=React.useCallback(()=>{if(!disabled){setShowOverlay(true);}},[disabled]);const close=React.useCallback(()=>setShowOverlay(false),[]);const computedLocale=locale??enUS;const dir=refWrapper.current?.closest("[dir]")?.getAttribute("dir")||"ltr";React.useEffect(()=>{setCurrentDate(selectedDate);},[selectedDate]);React.useEffect(()=>{const handleClick=e=>{const target=e.target;const thisElement=refWrapper.current;const dayPickerCalendar=datePickerRef.current;if(showOverlay&&closeOnSelect&&thisElement&&!thisElement.contains(target)&&dayPickerCalendar&&!dayPickerCalendar.contains(target)){setShowOverlay(false);}};document.addEventListener("mouseup",handleClick);return ()=>{document.removeEventListener("mouseup",handleClick);}},[showOverlay,closeOnSelect]);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 handleInputChange=(selectedDate,modifiers)=>{if(!selectedDate||modifiers.disabled){return}const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(selectedDate);setCurrentDate(wrappedDate);updateDate(wrappedDate);};const handleKeyDown=e=>{if(e.key==="Escape"){close();datePickerInputRef.current?.focus();}};const RootWithEsc=props=>{const{onKeyDown,rootRef:_,...rest}=props;return jsx("div",{...rest,tabIndex:-1,onKeyDown:e=>{onKeyDown?.(e);if(e.key==="Escape"){close();datePickerInputRef.current?.focus();}}})};const handleDayClick=(date,{disabled,selected})=>{if(disabled||!date){return}datePickerInputRef.current?.focus();const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(date);setCurrentDate(selected?undefined:wrappedDate);setShowOverlay(!closeOnSelect);updateDate(wrappedDate);};const renderInput=modifiers=>{const selectedDateAsValue=currentDate?TemporalLocaleUtils.formatDate(currentDate,dateFormat,enUSLocaleCode):"";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,modifiers:modifiers,testId:id&&`${id}-input`})};const maybeRenderFooter=()=>{if(!footer){return null}return jsx(View,{testId:"date-picker-footer",style:styles.footer,children:footer({close})})};const selectedDateValue=currentDate?TemporalLocaleUtils.temporalDateToJsDate(currentDate):undefined;const minDateToShow=minDate&&selectedDateValue?Temporal.PlainDate.compare(minDate,currentDate)<0?TemporalLocaleUtils.temporalDateToJsDate(minDate):selectedDateValue:minDate?TemporalLocaleUtils.temporalDateToJsDate(minDate):undefined;const modifiers={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}};return jsxs(View,{style:[styles.wrapper,style],ref:refWrapper,children:[renderInput(modifiers),showOverlay&&jsx(DatePickerOverlay,{referenceElement:datePickerInputRef.current,onClose:close,children:jsxs(View,{ref:datePickerRef,children:[jsx(DayPicker,{defaultMonth:selectedDateValue??undefined,startMonth:minDateToShow??undefined,endMonth:maxDate?TemporalLocaleUtils.temporalDateToJsDate(maxDate):undefined,modifiers:modifiers,onDayClick:handleDayClick,components:{Root:RootWithEsc},locale:computedLocale,dir:dir,styles:{root:{...customRootStyle},nav:{width:"auto"}}}),maybeRenderFooter()]})})]})};DatePicker.defaultProps={closeOnSelect:true};const styles=StyleSheet.create({wrapper:{width:225,height:40},footer:{margin:sizing.size_120,marginBlockStart:0}});
|
|
25
25
|
|
|
26
26
|
export { DatePicker, TemporalLocaleUtils };
|
package/dist/index.js
CHANGED
|
@@ -44,11 +44,11 @@ const enUSLocaleCode="en-US";function formatDate(date,format,locale=enUSLocaleCo
|
|
|
44
44
|
|
|
45
45
|
const DatePickerInput=React__namespace.forwardRef((props,ref)=>{const{value:propValue,onBlur,onClick,onFocus,onKeyDown,onChange,dateFormat,locale=enUSLocaleCode,modifiers,getModifiersForDay,parseDate,placeholder,testId,["aria-label"]:ariaLabel,...restProps}=props;const[value,setValue]=React__namespace.useState(propValue);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,value)=>{if(onChange){onChange(date,processModifiers(date,value));}},[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 maybeUpdateDate=React__namespace.useCallback(inputValue=>{const date=processDate(inputValue);if(date){updateDate(date,inputValue);}else {updateDateAsInvalid();}},[processDate,updateDate,updateDateAsInvalid]);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]);React__namespace.useEffect(()=>{setValue(propValue);},[propValue]);wonderBlocksCore.useOnMountEffect(()=>{if(!isValid()){updateDateAsInvalid();}});const handleBlur=e=>{if(!isValid()){setValue(propValue);}if(onBlur){onBlur(e);}};const handleChange=newValue=>{maybeUpdateDate(newValue);setValue(newValue);};return jsxRuntime.jsxs(wonderBlocksCore.View,{style:styles$1.container,onClick:e=>{if(!restProps.disabled&&onClick){onClick(e);}},children:[jsxRuntime.jsx(wonderBlocksForm.TextField,{ref:ref,...restProps,onBlur:handleBlur,onFocus:onFocus,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 styles$1=aphrodite.StyleSheet.create({container:{alignItems:"center",flexDirection:"row",justifyContent:"stretch"},icon:{pointerEvents:"none",position:"absolute",insetInlineEnd:wonderBlocksTokens.sizing.size_080},textField:{width:"100%"}});
|
|
46
46
|
|
|
47
|
-
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){e.preventDefault();focusableElementsInsideRef.current[0]?.focus();}};const handleKeydownNextFocusableElement=e=>{if(e.key==="Tab"&&e.shiftKey){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",{ref:setComponentRootNode,children:children}),jsxRuntime.jsx("div",{tabIndex:0,"data-testid":"focus-sentinel-next",onFocus:handleFocusNextFocusableElement,style:{position:"fixed"}})]})}
|
|
47
|
+
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){e.preventDefault();focusableElementsInsideRef.current[0]?.focus();}};const handleKeydownNextFocusableElement=e=>{if(e.key==="Tab"&&e.shiftKey){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"}})]})}
|
|
48
48
|
|
|
49
49
|
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={pointerEvents:"none",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:"bottom-start",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)};
|
|
50
50
|
|
|
51
|
-
const customRootStyle={"--rdp-accent-color":wonderBlocksTokens.semanticColor.core.border.instructive.default};const DatePicker=props=>{const{locale: locale$1,
|
|
51
|
+
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="Choose or enter a date",placeholder,selectedDate,style,closeOnSelect=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 open=React__namespace.useCallback(()=>{if(!disabled){setShowOverlay(true);}},[disabled]);const close=React__namespace.useCallback(()=>setShowOverlay(false),[]);const computedLocale=locale$1??locale.enUS;const dir=refWrapper.current?.closest("[dir]")?.getAttribute("dir")||"ltr";React__namespace.useEffect(()=>{setCurrentDate(selectedDate);},[selectedDate]);React__namespace.useEffect(()=>{const handleClick=e=>{const target=e.target;const thisElement=refWrapper.current;const dayPickerCalendar=datePickerRef.current;if(showOverlay&&closeOnSelect&&thisElement&&!thisElement.contains(target)&&dayPickerCalendar&&!dayPickerCalendar.contains(target)){setShowOverlay(false);}};document.addEventListener("mouseup",handleClick);return ()=>{document.removeEventListener("mouseup",handleClick);}},[showOverlay,closeOnSelect]);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 handleInputChange=(selectedDate,modifiers)=>{if(!selectedDate||modifiers.disabled){return}const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(selectedDate);setCurrentDate(wrappedDate);updateDate(wrappedDate);};const handleKeyDown=e=>{if(e.key==="Escape"){close();datePickerInputRef.current?.focus();}};const RootWithEsc=props=>{const{onKeyDown,rootRef:_,...rest}=props;return jsxRuntime.jsx("div",{...rest,tabIndex:-1,onKeyDown:e=>{onKeyDown?.(e);if(e.key==="Escape"){close();datePickerInputRef.current?.focus();}}})};const handleDayClick=(date,{disabled,selected})=>{if(disabled||!date){return}datePickerInputRef.current?.focus();const wrappedDate=TemporalLocaleUtils.jsDateToTemporalDate(date);setCurrentDate(selected?undefined:wrappedDate);setShowOverlay(!closeOnSelect);updateDate(wrappedDate);};const renderInput=modifiers=>{const selectedDateAsValue=currentDate?TemporalLocaleUtils.formatDate(currentDate,dateFormat,enUSLocaleCode):"";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,modifiers:modifiers,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 selectedDateValue=currentDate?TemporalLocaleUtils.temporalDateToJsDate(currentDate):undefined;const minDateToShow=minDate&&selectedDateValue?temporalPolyfill.Temporal.PlainDate.compare(minDate,currentDate)<0?TemporalLocaleUtils.temporalDateToJsDate(minDate):selectedDateValue:minDate?TemporalLocaleUtils.temporalDateToJsDate(minDate):undefined;const modifiers={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}};return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles.wrapper,style],ref:refWrapper,children:[renderInput(modifiers),showOverlay&&jsxRuntime.jsx(DatePickerOverlay,{referenceElement:datePickerInputRef.current,onClose:close,children:jsxRuntime.jsxs(wonderBlocksCore.View,{ref:datePickerRef,children:[jsxRuntime.jsx(reactDayPicker.DayPicker,{defaultMonth:selectedDateValue??undefined,startMonth:minDateToShow??undefined,endMonth:maxDate?TemporalLocaleUtils.temporalDateToJsDate(maxDate):undefined,modifiers:modifiers,onDayClick:handleDayClick,components:{Root:RootWithEsc},locale:computedLocale,dir:dir,styles:{root:{...customRootStyle},nav:{width:"auto"}}}),maybeRenderFooter()]})})]})};DatePicker.defaultProps={closeOnSelect:true};const styles=aphrodite.StyleSheet.create({wrapper:{width:225,height:40},footer:{margin:wonderBlocksTokens.sizing.size_120,marginBlockStart:0}});
|
|
52
52
|
|
|
53
53
|
exports.DatePicker = DatePicker;
|
|
54
54
|
exports.TemporalLocaleUtils = TemporalLocaleUtils;
|
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.0.0-PR2904-
|
|
6
|
+
"version": "0.0.0-PR2904-20251212210336",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@khanacademy/wonder-blocks-core": "0.0.0-PR2904-
|
|
31
|
-
"@khanacademy/wonder-blocks-form": "0.0.0-PR2904-
|
|
32
|
-
"@khanacademy/wonder-blocks-icon": "0.0.0-PR2904-
|
|
33
|
-
"@khanacademy/wonder-blocks-modal": "0.0.0-PR2904-
|
|
30
|
+
"@khanacademy/wonder-blocks-core": "0.0.0-PR2904-20251212210336",
|
|
31
|
+
"@khanacademy/wonder-blocks-form": "0.0.0-PR2904-20251212210336",
|
|
32
|
+
"@khanacademy/wonder-blocks-icon": "0.0.0-PR2904-20251212210336",
|
|
33
|
+
"@khanacademy/wonder-blocks-modal": "0.0.0-PR2904-20251212210336",
|
|
34
34
|
"@khanacademy/wonder-blocks-styles": "0.2.37",
|
|
35
35
|
"@khanacademy/wonder-blocks-tokens": "14.1.3"
|
|
36
36
|
},
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"temporal-polyfill": "^0.3.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@khanacademy/wb-dev-build-settings": "0.0.0-PR2904-
|
|
48
|
+
"@khanacademy/wb-dev-build-settings": "0.0.0-PR2904-20251212210336"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"test": "echo \"Error: no test specified\" && exit 1"
|