@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
|
@@ -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=()
|
|
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,
|
|
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=()
|
|
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,
|
|
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.
|
|
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": "
|
|
16
|
+
"@khanacademy/wonder-blocks-tokens": "13.0.0"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"aphrodite": "^1.2.5",
|