@khanacademy/wonder-blocks-clickable 7.1.24 → 7.1.25

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,13 @@
1
1
  # @khanacademy/wonder-blocks-clickable
2
2
 
3
+ ## 7.1.25
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [ede6085]
8
+ - Updated dependencies [a70f274]
9
+ - @khanacademy/wonder-blocks-tokens@13.0.0
10
+
3
11
  ## 7.1.24
4
12
 
5
13
  ### Patch Changes
@@ -87,6 +87,14 @@ type CommonProps = Readonly<{
87
87
  * Respond to a raw "mouseup" event.
88
88
  */
89
89
  onMouseUp?: (e: React.MouseEvent) => unknown;
90
+ /**
91
+ * Respond to raw "mouseenter" event.
92
+ */
93
+ onMouseEnter?: (e: React.MouseEvent) => unknown;
94
+ /**
95
+ * Respond to raw "mouseleave" event.
96
+ */
97
+ onMouseLeave?: (e: React.MouseEvent) => unknown;
90
98
  /**
91
99
  * An optional prop that enables a
92
100
  * [https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API](View
@@ -155,7 +163,7 @@ type DefaultProps = Readonly<{
155
163
  export type ChildrenProps = Readonly<{
156
164
  onClick: (e: React.SyntheticEvent) => unknown;
157
165
  onMouseEnter: (e: React.MouseEvent) => unknown;
158
- onMouseLeave: () => unknown;
166
+ onMouseLeave: (e: React.MouseEvent) => unknown;
159
167
  onMouseDown: (e: React.MouseEvent) => unknown;
160
168
  onMouseUp: (e: React.MouseEvent) => unknown;
161
169
  onTouchStart: () => unknown;
@@ -260,7 +268,7 @@ export default class ClickableBehavior extends React.Component<Props, ClickableS
260
268
  runCallbackAndMaybeNavigate(e: React.SyntheticEvent): Promise<undefined> | null | undefined;
261
269
  handleClick: (e: React.SyntheticEvent) => void;
262
270
  handleMouseEnter: (e: React.MouseEvent) => void;
263
- handleMouseLeave: () => void;
271
+ handleMouseLeave: (e: React.MouseEvent) => void;
264
272
  handleMouseDown: (e: React.MouseEvent) => void;
265
273
  handleMouseUp: (e: React.MouseEvent) => void;
266
274
  handleTouchStart: () => void;
@@ -84,6 +84,14 @@ Partial<Omit<AriaProps, "aria-disabled">> & {
84
84
  * Respond to raw "mouseup" event.
85
85
  */
86
86
  onMouseUp?: (e: React.MouseEvent) => unknown;
87
+ /**
88
+ * Respond to raw "mouseenter" event.
89
+ */
90
+ onMouseEnter?: (e: React.MouseEvent) => unknown;
91
+ /**
92
+ * Respond to raw "mouseleave" event.
93
+ */
94
+ onMouseLeave?: (e: React.MouseEvent) => unknown;
87
95
  /**
88
96
  * Don't show the default focus ring. This should be used when implementing
89
97
  * a custom focus ring within your own component that uses Clickable.
package/dist/es/index.js CHANGED
@@ -5,12 +5,12 @@ import { useNavigate, Link, useInRouterContext } from 'react-router-dom-v5-compa
5
5
  import { keys, addStyle } from '@khanacademy/wonder-blocks-core';
6
6
  import { border, semanticColor } from '@khanacademy/wonder-blocks-tokens';
7
7
 
8
- const getAppropriateTriggersForRole=role=>{switch(role){case"link":return {triggerOnEnter:true,triggerOnSpace:false};case"checkbox":case"radio":case"listbox":return {triggerOnEnter:false,triggerOnSpace:true};case"button":case"menuitem":case"menu":case"option":default:return {triggerOnEnter:true,triggerOnSpace:true}}};const disabledHandlers={onClick:()=>void 0,onMouseEnter:()=>void 0,onMouseLeave:()=>void 0,onMouseDown:()=>void 0,onMouseUp:()=>void 0,onTouchStart:()=>void 0,onTouchEnd:()=>void 0,onTouchCancel:()=>void 0,onKeyDown:()=>void 0,onKeyUp:()=>void 0};const startState={hovered:false,focused:false,pressed:false,waiting:false};class ClickableBehavior extends React.Component{static getDerivedStateFromProps(props,state){if(props.disabled){return {...startState,focused:state.focused}}else {return null}}navigateOrReset(shouldNavigate){if(shouldNavigate){const{navigate,href,skipClientNav,target=undefined}=this.props;if(href){if(target==="_blank"){window.open(href,"_blank");this.setState({waiting:false});}else if(navigate&&!skipClientNav){navigate(href,{viewTransition:this.props.viewTransition});this.setState({waiting:false});}else {window.location.assign(href);}}}else {this.setState({waiting:false});}}handleSafeWithNav(safeWithNav,shouldNavigate){const{skipClientNav,navigate}=this.props;if(navigate&&!skipClientNav||this.props.target==="_blank"){safeWithNav();this.navigateOrReset(shouldNavigate);return Promise.resolve()}else {if(!this.state.waiting){this.setState({waiting:true});}return safeWithNav().then(()=>{if(!this.state.waiting){this.setState({waiting:true});}return}).catch(error=>{}).finally(()=>{this.navigateOrReset(shouldNavigate);})}}runCallbackAndMaybeNavigate(e){const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined,href,type}=this.props;let shouldNavigate=true;let canSubmit=true;if(onClick){onClick(e);}if(e.defaultPrevented){shouldNavigate=false;canSubmit=false;}e.preventDefault();if(!href&&type==="submit"&&canSubmit){let target=e.currentTarget;while(target){if(target instanceof window.HTMLFormElement){const event=new window.Event("submit",{bubbles:true,cancelable:true});target.dispatchEvent(event);break}target=target.parentElement;}}if(beforeNav){this.setState({waiting:true});beforeNav().then(()=>{if(safeWithNav){return this.handleSafeWithNav(safeWithNav,shouldNavigate)}else {return this.navigateOrReset(shouldNavigate)}}).catch(()=>{});}else if(safeWithNav){return this.handleSafeWithNav(safeWithNav,shouldNavigate)}else {this.navigateOrReset(shouldNavigate);}}render(){const rel=this.props.rel||(this.props.target==="_blank"?"noopener noreferrer":undefined);const childrenProps=this.props.disabled?{...disabledHandlers,onFocus:this.handleFocus,onBlur:this.handleBlur,tabIndex:this.props.tabIndex,rel}:{onClick:this.handleClick,onMouseEnter:this.handleMouseEnter,onMouseLeave:this.handleMouseLeave,onMouseDown:this.handleMouseDown,onMouseUp:this.handleMouseUp,onTouchStart:this.handleTouchStart,onTouchEnd:this.handleTouchEnd,onTouchCancel:this.handleTouchCancel,onKeyDown:this.handleKeyDown,onKeyUp:this.handleKeyUp,onFocus:this.handleFocus,onBlur:this.handleBlur,tabIndex:this.props.tabIndex,rel};const{children}=this.props;return children&&children(this.state,childrenProps)}constructor(props){super(props),this.handleClick=e=>{const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined}=this.props;if(this.enterClick){return}if(onClick||beforeNav||safeWithNav){this.waitingForClick=false;}this.runCallbackAndMaybeNavigate(e);},this.handleMouseEnter=e=>{if(!this.waitingForClick){this.setState({hovered:true});}},this.handleMouseLeave=()=>{if(!this.waitingForClick){this.setState({hovered:false,pressed:false,focused:false});}},this.handleMouseDown=e=>{if(this.props.onMouseDown){this.props.onMouseDown(e);}this.setState({pressed:true});},this.handleMouseUp=e=>{if(this.props.onMouseUp){this.props.onMouseUp(e);}this.setState({pressed:false,focused:false});},this.handleTouchStart=()=>{this.setState({pressed:true});},this.handleTouchEnd=()=>{this.setState({pressed:false});this.waitingForClick=true;},this.handleTouchCancel=()=>{this.setState({pressed:false});this.waitingForClick=true;},this.handleKeyDown=e=>{const{onKeyDown,role}=this.props;if(onKeyDown){onKeyDown(e);}const keyName=e.key;const{triggerOnEnter,triggerOnSpace}=getAppropriateTriggersForRole(role);if(triggerOnEnter&&keyName===keys.enter||triggerOnSpace&&keyName===keys.space){e.preventDefault();this.setState({pressed:true});}else if(!triggerOnEnter&&keyName===keys.enter){this.enterClick=true;}},this.handleKeyUp=e=>{const{onKeyUp,role}=this.props;if(onKeyUp){onKeyUp(e);}const keyName=e.key;const{triggerOnEnter,triggerOnSpace}=getAppropriateTriggersForRole(role);if(triggerOnEnter&&keyName===keys.enter||triggerOnSpace&&keyName===keys.space){this.setState({pressed:false,focused:true});this.runCallbackAndMaybeNavigate(e);}else if(!triggerOnEnter&&keyName===keys.enter){this.enterClick=false;}},this.handleFocus=e=>{const{onFocus}=this.props;this.setState({focused:true},()=>{if(onFocus){onFocus(e);}});},this.handleBlur=e=>{this.setState({focused:false,pressed:false});};this.state=startState;this.waitingForClick=false;this.enterClick=false;}}ClickableBehavior.defaultProps={disabled:false};
8
+ const getAppropriateTriggersForRole=role=>{switch(role){case"link":return {triggerOnEnter:true,triggerOnSpace:false};case"checkbox":case"radio":case"listbox":return {triggerOnEnter:false,triggerOnSpace:true};case"button":case"menuitem":case"menu":case"option":default:return {triggerOnEnter:true,triggerOnSpace:true}}};const disabledHandlers={onClick:()=>void 0,onMouseEnter:()=>void 0,onMouseLeave:()=>void 0,onMouseDown:()=>void 0,onMouseUp:()=>void 0,onTouchStart:()=>void 0,onTouchEnd:()=>void 0,onTouchCancel:()=>void 0,onKeyDown:()=>void 0,onKeyUp:()=>void 0};const startState={hovered:false,focused:false,pressed:false,waiting:false};class ClickableBehavior extends React.Component{static getDerivedStateFromProps(props,state){if(props.disabled){return {...startState,focused:state.focused}}else {return null}}navigateOrReset(shouldNavigate){if(shouldNavigate){const{navigate,href,skipClientNav,target=undefined}=this.props;if(href){if(target==="_blank"){window.open(href,"_blank");this.setState({waiting:false});}else if(navigate&&!skipClientNav){navigate(href,{viewTransition:this.props.viewTransition});this.setState({waiting:false});}else {window.location.assign(href);}}}else {this.setState({waiting:false});}}handleSafeWithNav(safeWithNav,shouldNavigate){const{skipClientNav,navigate}=this.props;if(navigate&&!skipClientNav||this.props.target==="_blank"){safeWithNav();this.navigateOrReset(shouldNavigate);return Promise.resolve()}else {if(!this.state.waiting){this.setState({waiting:true});}return safeWithNav().then(()=>{if(!this.state.waiting){this.setState({waiting:true});}return}).catch(error=>{}).finally(()=>{this.navigateOrReset(shouldNavigate);})}}runCallbackAndMaybeNavigate(e){const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined,href,type}=this.props;let shouldNavigate=true;let canSubmit=true;if(onClick){onClick(e);}if(e.defaultPrevented){shouldNavigate=false;canSubmit=false;}e.preventDefault();if(!href&&type==="submit"&&canSubmit){let target=e.currentTarget;while(target){if(target instanceof window.HTMLFormElement){const event=new window.Event("submit",{bubbles:true,cancelable:true});target.dispatchEvent(event);break}target=target.parentElement;}}if(beforeNav){this.setState({waiting:true});beforeNav().then(()=>{if(safeWithNav){return this.handleSafeWithNav(safeWithNav,shouldNavigate)}else {return this.navigateOrReset(shouldNavigate)}}).catch(()=>{});}else if(safeWithNav){return this.handleSafeWithNav(safeWithNav,shouldNavigate)}else {this.navigateOrReset(shouldNavigate);}}render(){const rel=this.props.rel||(this.props.target==="_blank"?"noopener noreferrer":undefined);const childrenProps=this.props.disabled?{...disabledHandlers,onFocus:this.handleFocus,onBlur:this.handleBlur,tabIndex:this.props.tabIndex,rel}:{onClick:this.handleClick,onMouseEnter:this.handleMouseEnter,onMouseLeave:this.handleMouseLeave,onMouseDown:this.handleMouseDown,onMouseUp:this.handleMouseUp,onTouchStart:this.handleTouchStart,onTouchEnd:this.handleTouchEnd,onTouchCancel:this.handleTouchCancel,onKeyDown:this.handleKeyDown,onKeyUp:this.handleKeyUp,onFocus:this.handleFocus,onBlur:this.handleBlur,tabIndex:this.props.tabIndex,rel};const{children}=this.props;return children&&children(this.state,childrenProps)}constructor(props){super(props),this.handleClick=e=>{const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined}=this.props;if(this.enterClick){return}if(onClick||beforeNav||safeWithNav){this.waitingForClick=false;}this.runCallbackAndMaybeNavigate(e);},this.handleMouseEnter=e=>{if(this.props.onMouseEnter){this.props.onMouseEnter(e);}if(!this.waitingForClick){this.setState({hovered:true});}},this.handleMouseLeave=e=>{if(this.props.onMouseLeave){this.props.onMouseLeave(e);}if(!this.waitingForClick){this.setState({hovered:false,pressed:false,focused:false});}},this.handleMouseDown=e=>{if(this.props.onMouseDown){this.props.onMouseDown(e);}this.setState({pressed:true});},this.handleMouseUp=e=>{if(this.props.onMouseUp){this.props.onMouseUp(e);}this.setState({pressed:false,focused:false});},this.handleTouchStart=()=>{this.setState({pressed:true});},this.handleTouchEnd=()=>{this.setState({pressed:false});this.waitingForClick=true;},this.handleTouchCancel=()=>{this.setState({pressed:false});this.waitingForClick=true;},this.handleKeyDown=e=>{const{onKeyDown,role}=this.props;if(onKeyDown){onKeyDown(e);}const keyName=e.key;const{triggerOnEnter,triggerOnSpace}=getAppropriateTriggersForRole(role);if(triggerOnEnter&&keyName===keys.enter||triggerOnSpace&&keyName===keys.space){e.preventDefault();this.setState({pressed:true});}else if(!triggerOnEnter&&keyName===keys.enter){this.enterClick=true;}},this.handleKeyUp=e=>{const{onKeyUp,role}=this.props;if(onKeyUp){onKeyUp(e);}const keyName=e.key;const{triggerOnEnter,triggerOnSpace}=getAppropriateTriggersForRole(role);if(triggerOnEnter&&keyName===keys.enter||triggerOnSpace&&keyName===keys.space){this.setState({pressed:false,focused:true});this.runCallbackAndMaybeNavigate(e);}else if(!triggerOnEnter&&keyName===keys.enter){this.enterClick=false;}},this.handleFocus=e=>{const{onFocus}=this.props;this.setState({focused:true},()=>{if(onFocus){onFocus(e);}});},this.handleBlur=e=>{this.setState({focused:false,pressed:false});};this.state=startState;this.waitingForClick=false;this.enterClick=false;}}ClickableBehavior.defaultProps={disabled:false};
9
9
 
10
10
  const isClientSideUrl=href=>{if(typeof href!=="string"){return false}return !/^(https?:)?\/\//i.test(href)&&!/^([^#]*#[\w-]*|[\w\-.]+:)/.test(href)};
11
11
 
12
12
  function withRouter(Component){function WithRouterWrapper(props){const navigate=useNavigate();return jsx(Component,{...props,navigate:navigate})}WithRouterWrapper.displayName="withRouter(ClickableBehavior)";return WithRouterWrapper}const ClickableBehaviorWithRouter=withRouter(ClickableBehavior);function getClickableBehavior(href,skipClientNav,inRouterContext){if(inRouterContext&&skipClientNav!==true&&href&&isClientSideUrl(href)){return ClickableBehaviorWithRouter}return ClickableBehavior}
13
13
 
14
- const StyledA=addStyle("a");const StyledButton=addStyle("button");const StyledLink=addStyle(Link);const Clickable=React.forwardRef(function Clickable(props,ref){const getCorrectTag=(clickableState,inRouterContext,commonProps)=>{const activeHref=props.href&&!props.disabled;const useClient=inRouterContext&&!props.skipClientNav&&isClientSideUrl(props.href||"");if(activeHref&&useClient&&props.href){return jsx(StyledLink,{...commonProps,to:props.href,role:props.role,target:props.target||undefined,"aria-disabled":props.disabled?"true":"false",ref:ref,children:props.children(clickableState)})}else if(activeHref&&!useClient){return jsx(StyledA,{...commonProps,href:props.href,role:props.role,target:props.target||undefined,"aria-disabled":props.disabled?"true":"false",ref:ref,children:props.children(clickableState)})}else {return jsx(StyledButton,{...commonProps,type:"button","aria-disabled":props.disabled,ref:ref,children:props.children(clickableState)})}};const inRouterContext=useInRouterContext();const{href,onClick,skipClientNav,beforeNav=undefined,safeWithNav=undefined,style,target=undefined,testId,onFocus,onKeyDown,onKeyUp,onMouseDown,onMouseUp,hideDefaultFocusRing,light,disabled,tabIndex,...restProps}=props;const ClickableBehavior=getClickableBehavior(href,skipClientNav,inRouterContext);const getStyle=state=>[styles.reset,styles.link,!hideDefaultFocusRing&&state.focused&&(light?styles.focusedLight:styles.focused),disabled&&styles.disabled,style];if(beforeNav){return jsx(ClickableBehavior,{href:href,onClick:onClick,beforeNav:beforeNav,safeWithNav:safeWithNav,onFocus:onFocus,onKeyDown:onKeyDown,onKeyUp:onKeyUp,onMouseDown:onMouseDown,onMouseUp:onMouseUp,disabled:disabled,tabIndex:tabIndex,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}else {return jsx(ClickableBehavior,{href:href,onClick:onClick,safeWithNav:safeWithNav,onFocus:onFocus,onKeyDown:onKeyDown,onKeyUp:onKeyUp,onMouseDown:onMouseDown,onMouseUp:onMouseUp,target:target,disabled:disabled,tabIndex:tabIndex,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}});Clickable.defaultProps={light:false,disabled:false};const styles=StyleSheet.create({reset:{border:"none",margin:0,padding:0,width:"auto",overflow:"visible",background:"transparent",textDecoration:"none",color:"inherit",font:"inherit",boxSizing:"border-box",touchAction:"manipulation",userSelect:"none",outline:"none",lineHeight:"normal",WebkitFontSmoothing:"inherit",MozOsxFontSmoothing:"inherit"},link:{cursor:"pointer"},focused:{":focus":{outline:`solid ${border.width.medium} ${semanticColor.focus.outer}`}},focusedLight:{outline:`solid ${border.width.medium} ${semanticColor.core.border.knockout.default}`},disabled:{color:semanticColor.action.secondary.disabled.foreground,cursor:"not-allowed",":focus":{outline:"none"},":focus-visible":{outline:`solid ${border.width.medium} ${semanticColor.focus.outer}`}}});
14
+ const StyledA=addStyle("a");const StyledButton=addStyle("button");const StyledLink=addStyle(Link);const Clickable=React.forwardRef(function Clickable(props,ref){const getCorrectTag=(clickableState,inRouterContext,commonProps)=>{const activeHref=props.href&&!props.disabled;const useClient=inRouterContext&&!props.skipClientNav&&isClientSideUrl(props.href||"");if(activeHref&&useClient&&props.href){return jsx(StyledLink,{...commonProps,to:props.href,role:props.role,target:props.target||undefined,"aria-disabled":props.disabled?"true":"false",ref:ref,children:props.children(clickableState)})}else if(activeHref&&!useClient){return jsx(StyledA,{...commonProps,href:props.href,role:props.role,target:props.target||undefined,"aria-disabled":props.disabled?"true":"false",ref:ref,children:props.children(clickableState)})}else {return jsx(StyledButton,{...commonProps,type:"button","aria-disabled":props.disabled,ref:ref,children:props.children(clickableState)})}};const inRouterContext=useInRouterContext();const{href,onClick,onFocus,onKeyDown,onKeyUp,onMouseDown,onMouseUp,onMouseEnter,onMouseLeave,skipClientNav,beforeNav=undefined,safeWithNav=undefined,style,target=undefined,testId,hideDefaultFocusRing,light,disabled,tabIndex,...restProps}=props;const ClickableBehavior=getClickableBehavior(href,skipClientNav,inRouterContext);const getStyle=state=>[styles.reset,styles.link,!hideDefaultFocusRing&&state.focused&&(light?styles.focusedLight:styles.focused),disabled&&styles.disabled,style];if(beforeNav){return jsx(ClickableBehavior,{href:href,onClick:onClick,beforeNav:beforeNav,safeWithNav:safeWithNav,onFocus:onFocus,onKeyDown:onKeyDown,onKeyUp:onKeyUp,onMouseDown:onMouseDown,onMouseUp:onMouseUp,onMouseEnter:onMouseEnter,onMouseLeave:onMouseLeave,disabled:disabled,tabIndex:tabIndex,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}else {return jsx(ClickableBehavior,{href:href,onClick:onClick,safeWithNav:safeWithNav,onFocus:onFocus,onKeyDown:onKeyDown,onKeyUp:onKeyUp,onMouseDown:onMouseDown,onMouseUp:onMouseUp,onMouseEnter:onMouseEnter,onMouseLeave:onMouseLeave,target:target,disabled:disabled,tabIndex:tabIndex,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}});Clickable.defaultProps={light:false,disabled:false};const styles=StyleSheet.create({reset:{border:"none",margin:0,padding:0,width:"auto",overflow:"visible",background:"transparent",textDecoration:"none",color:"inherit",font:"inherit",boxSizing:"border-box",touchAction:"manipulation",userSelect:"none",outline:"none",lineHeight:"normal",WebkitFontSmoothing:"inherit",MozOsxFontSmoothing:"inherit"},link:{cursor:"pointer"},focused:{":focus":{outline:`solid ${border.width.medium} ${semanticColor.focus.outer}`}},focusedLight:{outline:`solid ${border.width.medium} ${semanticColor.core.border.knockout.default}`},disabled:{color:semanticColor.action.secondary.disabled.foreground,cursor:"not-allowed",":focus":{outline:"none"},":focus-visible":{outline:`solid ${border.width.medium} ${semanticColor.focus.outer}`}}});
15
15
 
16
16
  export { ClickableBehavior, Clickable as default, getClickableBehavior, isClientSideUrl };
package/dist/index.js CHANGED
@@ -29,13 +29,13 @@ function _interopNamespace(e) {
29
29
 
30
30
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
31
31
 
32
- const getAppropriateTriggersForRole=role=>{switch(role){case"link":return {triggerOnEnter:true,triggerOnSpace:false};case"checkbox":case"radio":case"listbox":return {triggerOnEnter:false,triggerOnSpace:true};case"button":case"menuitem":case"menu":case"option":default:return {triggerOnEnter:true,triggerOnSpace:true}}};const disabledHandlers={onClick:()=>void 0,onMouseEnter:()=>void 0,onMouseLeave:()=>void 0,onMouseDown:()=>void 0,onMouseUp:()=>void 0,onTouchStart:()=>void 0,onTouchEnd:()=>void 0,onTouchCancel:()=>void 0,onKeyDown:()=>void 0,onKeyUp:()=>void 0};const startState={hovered:false,focused:false,pressed:false,waiting:false};class ClickableBehavior extends React__namespace.Component{static getDerivedStateFromProps(props,state){if(props.disabled){return {...startState,focused:state.focused}}else {return null}}navigateOrReset(shouldNavigate){if(shouldNavigate){const{navigate,href,skipClientNav,target=undefined}=this.props;if(href){if(target==="_blank"){window.open(href,"_blank");this.setState({waiting:false});}else if(navigate&&!skipClientNav){navigate(href,{viewTransition:this.props.viewTransition});this.setState({waiting:false});}else {window.location.assign(href);}}}else {this.setState({waiting:false});}}handleSafeWithNav(safeWithNav,shouldNavigate){const{skipClientNav,navigate}=this.props;if(navigate&&!skipClientNav||this.props.target==="_blank"){safeWithNav();this.navigateOrReset(shouldNavigate);return Promise.resolve()}else {if(!this.state.waiting){this.setState({waiting:true});}return safeWithNav().then(()=>{if(!this.state.waiting){this.setState({waiting:true});}return}).catch(error=>{}).finally(()=>{this.navigateOrReset(shouldNavigate);})}}runCallbackAndMaybeNavigate(e){const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined,href,type}=this.props;let shouldNavigate=true;let canSubmit=true;if(onClick){onClick(e);}if(e.defaultPrevented){shouldNavigate=false;canSubmit=false;}e.preventDefault();if(!href&&type==="submit"&&canSubmit){let target=e.currentTarget;while(target){if(target instanceof window.HTMLFormElement){const event=new window.Event("submit",{bubbles:true,cancelable:true});target.dispatchEvent(event);break}target=target.parentElement;}}if(beforeNav){this.setState({waiting:true});beforeNav().then(()=>{if(safeWithNav){return this.handleSafeWithNav(safeWithNav,shouldNavigate)}else {return this.navigateOrReset(shouldNavigate)}}).catch(()=>{});}else if(safeWithNav){return this.handleSafeWithNav(safeWithNav,shouldNavigate)}else {this.navigateOrReset(shouldNavigate);}}render(){const rel=this.props.rel||(this.props.target==="_blank"?"noopener noreferrer":undefined);const childrenProps=this.props.disabled?{...disabledHandlers,onFocus:this.handleFocus,onBlur:this.handleBlur,tabIndex:this.props.tabIndex,rel}:{onClick:this.handleClick,onMouseEnter:this.handleMouseEnter,onMouseLeave:this.handleMouseLeave,onMouseDown:this.handleMouseDown,onMouseUp:this.handleMouseUp,onTouchStart:this.handleTouchStart,onTouchEnd:this.handleTouchEnd,onTouchCancel:this.handleTouchCancel,onKeyDown:this.handleKeyDown,onKeyUp:this.handleKeyUp,onFocus:this.handleFocus,onBlur:this.handleBlur,tabIndex:this.props.tabIndex,rel};const{children}=this.props;return children&&children(this.state,childrenProps)}constructor(props){super(props),this.handleClick=e=>{const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined}=this.props;if(this.enterClick){return}if(onClick||beforeNav||safeWithNav){this.waitingForClick=false;}this.runCallbackAndMaybeNavigate(e);},this.handleMouseEnter=e=>{if(!this.waitingForClick){this.setState({hovered:true});}},this.handleMouseLeave=()=>{if(!this.waitingForClick){this.setState({hovered:false,pressed:false,focused:false});}},this.handleMouseDown=e=>{if(this.props.onMouseDown){this.props.onMouseDown(e);}this.setState({pressed:true});},this.handleMouseUp=e=>{if(this.props.onMouseUp){this.props.onMouseUp(e);}this.setState({pressed:false,focused:false});},this.handleTouchStart=()=>{this.setState({pressed:true});},this.handleTouchEnd=()=>{this.setState({pressed:false});this.waitingForClick=true;},this.handleTouchCancel=()=>{this.setState({pressed:false});this.waitingForClick=true;},this.handleKeyDown=e=>{const{onKeyDown,role}=this.props;if(onKeyDown){onKeyDown(e);}const keyName=e.key;const{triggerOnEnter,triggerOnSpace}=getAppropriateTriggersForRole(role);if(triggerOnEnter&&keyName===wonderBlocksCore.keys.enter||triggerOnSpace&&keyName===wonderBlocksCore.keys.space){e.preventDefault();this.setState({pressed:true});}else if(!triggerOnEnter&&keyName===wonderBlocksCore.keys.enter){this.enterClick=true;}},this.handleKeyUp=e=>{const{onKeyUp,role}=this.props;if(onKeyUp){onKeyUp(e);}const keyName=e.key;const{triggerOnEnter,triggerOnSpace}=getAppropriateTriggersForRole(role);if(triggerOnEnter&&keyName===wonderBlocksCore.keys.enter||triggerOnSpace&&keyName===wonderBlocksCore.keys.space){this.setState({pressed:false,focused:true});this.runCallbackAndMaybeNavigate(e);}else if(!triggerOnEnter&&keyName===wonderBlocksCore.keys.enter){this.enterClick=false;}},this.handleFocus=e=>{const{onFocus}=this.props;this.setState({focused:true},()=>{if(onFocus){onFocus(e);}});},this.handleBlur=e=>{this.setState({focused:false,pressed:false});};this.state=startState;this.waitingForClick=false;this.enterClick=false;}}ClickableBehavior.defaultProps={disabled:false};
32
+ const getAppropriateTriggersForRole=role=>{switch(role){case"link":return {triggerOnEnter:true,triggerOnSpace:false};case"checkbox":case"radio":case"listbox":return {triggerOnEnter:false,triggerOnSpace:true};case"button":case"menuitem":case"menu":case"option":default:return {triggerOnEnter:true,triggerOnSpace:true}}};const disabledHandlers={onClick:()=>void 0,onMouseEnter:()=>void 0,onMouseLeave:()=>void 0,onMouseDown:()=>void 0,onMouseUp:()=>void 0,onTouchStart:()=>void 0,onTouchEnd:()=>void 0,onTouchCancel:()=>void 0,onKeyDown:()=>void 0,onKeyUp:()=>void 0};const startState={hovered:false,focused:false,pressed:false,waiting:false};class ClickableBehavior extends React__namespace.Component{static getDerivedStateFromProps(props,state){if(props.disabled){return {...startState,focused:state.focused}}else {return null}}navigateOrReset(shouldNavigate){if(shouldNavigate){const{navigate,href,skipClientNav,target=undefined}=this.props;if(href){if(target==="_blank"){window.open(href,"_blank");this.setState({waiting:false});}else if(navigate&&!skipClientNav){navigate(href,{viewTransition:this.props.viewTransition});this.setState({waiting:false});}else {window.location.assign(href);}}}else {this.setState({waiting:false});}}handleSafeWithNav(safeWithNav,shouldNavigate){const{skipClientNav,navigate}=this.props;if(navigate&&!skipClientNav||this.props.target==="_blank"){safeWithNav();this.navigateOrReset(shouldNavigate);return Promise.resolve()}else {if(!this.state.waiting){this.setState({waiting:true});}return safeWithNav().then(()=>{if(!this.state.waiting){this.setState({waiting:true});}return}).catch(error=>{}).finally(()=>{this.navigateOrReset(shouldNavigate);})}}runCallbackAndMaybeNavigate(e){const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined,href,type}=this.props;let shouldNavigate=true;let canSubmit=true;if(onClick){onClick(e);}if(e.defaultPrevented){shouldNavigate=false;canSubmit=false;}e.preventDefault();if(!href&&type==="submit"&&canSubmit){let target=e.currentTarget;while(target){if(target instanceof window.HTMLFormElement){const event=new window.Event("submit",{bubbles:true,cancelable:true});target.dispatchEvent(event);break}target=target.parentElement;}}if(beforeNav){this.setState({waiting:true});beforeNav().then(()=>{if(safeWithNav){return this.handleSafeWithNav(safeWithNav,shouldNavigate)}else {return this.navigateOrReset(shouldNavigate)}}).catch(()=>{});}else if(safeWithNav){return this.handleSafeWithNav(safeWithNav,shouldNavigate)}else {this.navigateOrReset(shouldNavigate);}}render(){const rel=this.props.rel||(this.props.target==="_blank"?"noopener noreferrer":undefined);const childrenProps=this.props.disabled?{...disabledHandlers,onFocus:this.handleFocus,onBlur:this.handleBlur,tabIndex:this.props.tabIndex,rel}:{onClick:this.handleClick,onMouseEnter:this.handleMouseEnter,onMouseLeave:this.handleMouseLeave,onMouseDown:this.handleMouseDown,onMouseUp:this.handleMouseUp,onTouchStart:this.handleTouchStart,onTouchEnd:this.handleTouchEnd,onTouchCancel:this.handleTouchCancel,onKeyDown:this.handleKeyDown,onKeyUp:this.handleKeyUp,onFocus:this.handleFocus,onBlur:this.handleBlur,tabIndex:this.props.tabIndex,rel};const{children}=this.props;return children&&children(this.state,childrenProps)}constructor(props){super(props),this.handleClick=e=>{const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined}=this.props;if(this.enterClick){return}if(onClick||beforeNav||safeWithNav){this.waitingForClick=false;}this.runCallbackAndMaybeNavigate(e);},this.handleMouseEnter=e=>{if(this.props.onMouseEnter){this.props.onMouseEnter(e);}if(!this.waitingForClick){this.setState({hovered:true});}},this.handleMouseLeave=e=>{if(this.props.onMouseLeave){this.props.onMouseLeave(e);}if(!this.waitingForClick){this.setState({hovered:false,pressed:false,focused:false});}},this.handleMouseDown=e=>{if(this.props.onMouseDown){this.props.onMouseDown(e);}this.setState({pressed:true});},this.handleMouseUp=e=>{if(this.props.onMouseUp){this.props.onMouseUp(e);}this.setState({pressed:false,focused:false});},this.handleTouchStart=()=>{this.setState({pressed:true});},this.handleTouchEnd=()=>{this.setState({pressed:false});this.waitingForClick=true;},this.handleTouchCancel=()=>{this.setState({pressed:false});this.waitingForClick=true;},this.handleKeyDown=e=>{const{onKeyDown,role}=this.props;if(onKeyDown){onKeyDown(e);}const keyName=e.key;const{triggerOnEnter,triggerOnSpace}=getAppropriateTriggersForRole(role);if(triggerOnEnter&&keyName===wonderBlocksCore.keys.enter||triggerOnSpace&&keyName===wonderBlocksCore.keys.space){e.preventDefault();this.setState({pressed:true});}else if(!triggerOnEnter&&keyName===wonderBlocksCore.keys.enter){this.enterClick=true;}},this.handleKeyUp=e=>{const{onKeyUp,role}=this.props;if(onKeyUp){onKeyUp(e);}const keyName=e.key;const{triggerOnEnter,triggerOnSpace}=getAppropriateTriggersForRole(role);if(triggerOnEnter&&keyName===wonderBlocksCore.keys.enter||triggerOnSpace&&keyName===wonderBlocksCore.keys.space){this.setState({pressed:false,focused:true});this.runCallbackAndMaybeNavigate(e);}else if(!triggerOnEnter&&keyName===wonderBlocksCore.keys.enter){this.enterClick=false;}},this.handleFocus=e=>{const{onFocus}=this.props;this.setState({focused:true},()=>{if(onFocus){onFocus(e);}});},this.handleBlur=e=>{this.setState({focused:false,pressed:false});};this.state=startState;this.waitingForClick=false;this.enterClick=false;}}ClickableBehavior.defaultProps={disabled:false};
33
33
 
34
34
  const isClientSideUrl=href=>{if(typeof href!=="string"){return false}return !/^(https?:)?\/\//i.test(href)&&!/^([^#]*#[\w-]*|[\w\-.]+:)/.test(href)};
35
35
 
36
36
  function withRouter(Component){function WithRouterWrapper(props){const navigate=reactRouterDomV5Compat.useNavigate();return jsxRuntime.jsx(Component,{...props,navigate:navigate})}WithRouterWrapper.displayName="withRouter(ClickableBehavior)";return WithRouterWrapper}const ClickableBehaviorWithRouter=withRouter(ClickableBehavior);function getClickableBehavior(href,skipClientNav,inRouterContext){if(inRouterContext&&skipClientNav!==true&&href&&isClientSideUrl(href)){return ClickableBehaviorWithRouter}return ClickableBehavior}
37
37
 
38
- const StyledA=wonderBlocksCore.addStyle("a");const StyledButton=wonderBlocksCore.addStyle("button");const StyledLink=wonderBlocksCore.addStyle(reactRouterDomV5Compat.Link);const Clickable=React__namespace.forwardRef(function Clickable(props,ref){const getCorrectTag=(clickableState,inRouterContext,commonProps)=>{const activeHref=props.href&&!props.disabled;const useClient=inRouterContext&&!props.skipClientNav&&isClientSideUrl(props.href||"");if(activeHref&&useClient&&props.href){return jsxRuntime.jsx(StyledLink,{...commonProps,to:props.href,role:props.role,target:props.target||undefined,"aria-disabled":props.disabled?"true":"false",ref:ref,children:props.children(clickableState)})}else if(activeHref&&!useClient){return jsxRuntime.jsx(StyledA,{...commonProps,href:props.href,role:props.role,target:props.target||undefined,"aria-disabled":props.disabled?"true":"false",ref:ref,children:props.children(clickableState)})}else {return jsxRuntime.jsx(StyledButton,{...commonProps,type:"button","aria-disabled":props.disabled,ref:ref,children:props.children(clickableState)})}};const inRouterContext=reactRouterDomV5Compat.useInRouterContext();const{href,onClick,skipClientNav,beforeNav=undefined,safeWithNav=undefined,style,target=undefined,testId,onFocus,onKeyDown,onKeyUp,onMouseDown,onMouseUp,hideDefaultFocusRing,light,disabled,tabIndex,...restProps}=props;const ClickableBehavior=getClickableBehavior(href,skipClientNav,inRouterContext);const getStyle=state=>[styles.reset,styles.link,!hideDefaultFocusRing&&state.focused&&(light?styles.focusedLight:styles.focused),disabled&&styles.disabled,style];if(beforeNav){return jsxRuntime.jsx(ClickableBehavior,{href:href,onClick:onClick,beforeNav:beforeNav,safeWithNav:safeWithNav,onFocus:onFocus,onKeyDown:onKeyDown,onKeyUp:onKeyUp,onMouseDown:onMouseDown,onMouseUp:onMouseUp,disabled:disabled,tabIndex:tabIndex,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}else {return jsxRuntime.jsx(ClickableBehavior,{href:href,onClick:onClick,safeWithNav:safeWithNav,onFocus:onFocus,onKeyDown:onKeyDown,onKeyUp:onKeyUp,onMouseDown:onMouseDown,onMouseUp:onMouseUp,target:target,disabled:disabled,tabIndex:tabIndex,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}});Clickable.defaultProps={light:false,disabled:false};const styles=aphrodite.StyleSheet.create({reset:{border:"none",margin:0,padding:0,width:"auto",overflow:"visible",background:"transparent",textDecoration:"none",color:"inherit",font:"inherit",boxSizing:"border-box",touchAction:"manipulation",userSelect:"none",outline:"none",lineHeight:"normal",WebkitFontSmoothing:"inherit",MozOsxFontSmoothing:"inherit"},link:{cursor:"pointer"},focused:{":focus":{outline:`solid ${wonderBlocksTokens.border.width.medium} ${wonderBlocksTokens.semanticColor.focus.outer}`}},focusedLight:{outline:`solid ${wonderBlocksTokens.border.width.medium} ${wonderBlocksTokens.semanticColor.core.border.knockout.default}`},disabled:{color:wonderBlocksTokens.semanticColor.action.secondary.disabled.foreground,cursor:"not-allowed",":focus":{outline:"none"},":focus-visible":{outline:`solid ${wonderBlocksTokens.border.width.medium} ${wonderBlocksTokens.semanticColor.focus.outer}`}}});
38
+ const StyledA=wonderBlocksCore.addStyle("a");const StyledButton=wonderBlocksCore.addStyle("button");const StyledLink=wonderBlocksCore.addStyle(reactRouterDomV5Compat.Link);const Clickable=React__namespace.forwardRef(function Clickable(props,ref){const getCorrectTag=(clickableState,inRouterContext,commonProps)=>{const activeHref=props.href&&!props.disabled;const useClient=inRouterContext&&!props.skipClientNav&&isClientSideUrl(props.href||"");if(activeHref&&useClient&&props.href){return jsxRuntime.jsx(StyledLink,{...commonProps,to:props.href,role:props.role,target:props.target||undefined,"aria-disabled":props.disabled?"true":"false",ref:ref,children:props.children(clickableState)})}else if(activeHref&&!useClient){return jsxRuntime.jsx(StyledA,{...commonProps,href:props.href,role:props.role,target:props.target||undefined,"aria-disabled":props.disabled?"true":"false",ref:ref,children:props.children(clickableState)})}else {return jsxRuntime.jsx(StyledButton,{...commonProps,type:"button","aria-disabled":props.disabled,ref:ref,children:props.children(clickableState)})}};const inRouterContext=reactRouterDomV5Compat.useInRouterContext();const{href,onClick,onFocus,onKeyDown,onKeyUp,onMouseDown,onMouseUp,onMouseEnter,onMouseLeave,skipClientNav,beforeNav=undefined,safeWithNav=undefined,style,target=undefined,testId,hideDefaultFocusRing,light,disabled,tabIndex,...restProps}=props;const ClickableBehavior=getClickableBehavior(href,skipClientNav,inRouterContext);const getStyle=state=>[styles.reset,styles.link,!hideDefaultFocusRing&&state.focused&&(light?styles.focusedLight:styles.focused),disabled&&styles.disabled,style];if(beforeNav){return jsxRuntime.jsx(ClickableBehavior,{href:href,onClick:onClick,beforeNav:beforeNav,safeWithNav:safeWithNav,onFocus:onFocus,onKeyDown:onKeyDown,onKeyUp:onKeyUp,onMouseDown:onMouseDown,onMouseUp:onMouseUp,onMouseEnter:onMouseEnter,onMouseLeave:onMouseLeave,disabled:disabled,tabIndex:tabIndex,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}else {return jsxRuntime.jsx(ClickableBehavior,{href:href,onClick:onClick,safeWithNav:safeWithNav,onFocus:onFocus,onKeyDown:onKeyDown,onKeyUp:onKeyUp,onMouseDown:onMouseDown,onMouseUp:onMouseUp,onMouseEnter:onMouseEnter,onMouseLeave:onMouseLeave,target:target,disabled:disabled,tabIndex:tabIndex,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}});Clickable.defaultProps={light:false,disabled:false};const styles=aphrodite.StyleSheet.create({reset:{border:"none",margin:0,padding:0,width:"auto",overflow:"visible",background:"transparent",textDecoration:"none",color:"inherit",font:"inherit",boxSizing:"border-box",touchAction:"manipulation",userSelect:"none",outline:"none",lineHeight:"normal",WebkitFontSmoothing:"inherit",MozOsxFontSmoothing:"inherit"},link:{cursor:"pointer"},focused:{":focus":{outline:`solid ${wonderBlocksTokens.border.width.medium} ${wonderBlocksTokens.semanticColor.focus.outer}`}},focusedLight:{outline:`solid ${wonderBlocksTokens.border.width.medium} ${wonderBlocksTokens.semanticColor.core.border.knockout.default}`},disabled:{color:wonderBlocksTokens.semanticColor.action.secondary.disabled.foreground,cursor:"not-allowed",":focus":{outline:"none"},":focus-visible":{outline:`solid ${wonderBlocksTokens.border.width.medium} ${wonderBlocksTokens.semanticColor.focus.outer}`}}});
39
39
 
40
40
  exports.ClickableBehavior = ClickableBehavior;
41
41
  exports["default"] = Clickable;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-clickable",
3
- "version": "7.1.24",
3
+ "version": "7.1.25",
4
4
  "design": "v1",
5
5
  "description": "Clickable component for Wonder-Blocks.",
6
6
  "main": "dist/index.js",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@khanacademy/wonder-blocks-core": "12.4.0",
16
- "@khanacademy/wonder-blocks-tokens": "12.2.1"
16
+ "@khanacademy/wonder-blocks-tokens": "13.0.0"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "aphrodite": "^1.2.5",