@khanacademy/wonder-blocks-clickable 8.0.5 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @khanacademy/wonder-blocks-clickable
2
2
 
3
+ ## 8.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6e257eb: Add `onBlur` event handler to Clickable API
8
+
3
9
  ## 8.0.5
4
10
 
5
11
  ### Patch Changes
@@ -1,7 +1,45 @@
1
1
  import * as React from "react";
2
2
  import { NavigateFunction } from "react-router-dom-v5-compat";
3
3
  export type ClickableRole = "button" | "checkbox" | "link" | "listbox" | "menu" | "menuitem" | "menuitemcheckbox" | "option" | "radio" | "switch" | "tab";
4
- type CommonProps = Readonly<{
4
+ export interface ExposedEventHandlers {
5
+ /**
6
+ * A function to be executed `onclick`.
7
+ */
8
+ onClick?: (e: React.SyntheticEvent) => unknown;
9
+ /**
10
+ * Respond to raw "onfocus" event.
11
+ */
12
+ onFocus?: (e: React.FocusEvent) => unknown;
13
+ /**
14
+ * Respont to raw "onblur" event.
15
+ */
16
+ onBlur?: (e: React.FocusEvent) => unknown;
17
+ /**
18
+ * Respond to raw "keydown" event.
19
+ */
20
+ onKeyDown?: (e: React.KeyboardEvent) => unknown;
21
+ /**
22
+ * Respond to raw "keyup" event.
23
+ */
24
+ onKeyUp?: (e: React.KeyboardEvent) => unknown;
25
+ /**
26
+ * Respond to a raw "mousedown" event.
27
+ */
28
+ onMouseDown?: (e: React.MouseEvent) => unknown;
29
+ /**
30
+ * Respond to a raw "mouseup" event.
31
+ */
32
+ onMouseUp?: (e: React.MouseEvent) => unknown;
33
+ /**
34
+ * Respond to raw "mouseenter" event.
35
+ */
36
+ onMouseEnter?: (e: React.MouseEvent) => unknown;
37
+ /**
38
+ * Respond to raw "mouseleave" event.
39
+ */
40
+ onMouseLeave?: (e: React.MouseEvent) => unknown;
41
+ }
42
+ interface CommonProps extends ExposedEventHandlers {
5
43
  /**
6
44
  * A function that returns the a React `Element`.
7
45
  *
@@ -44,10 +82,6 @@ type CommonProps = Readonly<{
44
82
  * element non-focusable via keyboard navigation.
45
83
  */
46
84
  tabIndex?: number;
47
- /**
48
- * A function to be executed `onclick`.
49
- */
50
- onClick?: (e: React.SyntheticEvent) => unknown;
51
85
  /**
52
86
  * Run async code in the background while client-side navigating. If the
53
87
  * browser does a full page load navigation, the callback promise must be
@@ -67,34 +101,6 @@ type CommonProps = Readonly<{
67
101
  * enter and space keys.
68
102
  */
69
103
  role?: ClickableRole;
70
- /**
71
- * Respond to raw "onfocus" event.
72
- */
73
- onFocus?: (e: React.FocusEvent) => unknown;
74
- /**
75
- * Respond to raw "keydown" event.
76
- */
77
- onKeyDown?: (e: React.KeyboardEvent) => unknown;
78
- /**
79
- * Respond to raw "keyup" event.
80
- */
81
- onKeyUp?: (e: React.KeyboardEvent) => unknown;
82
- /**
83
- * Respond to a raw "mousedown" event.
84
- */
85
- onMouseDown?: (e: React.MouseEvent) => unknown;
86
- /**
87
- * Respond to a raw "mouseup" event.
88
- */
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;
98
104
  /**
99
105
  * An optional prop that enables a
100
106
  * [https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API](View
@@ -104,7 +110,7 @@ type CommonProps = Readonly<{
104
110
  * @see https://reactrouter.com/6.30.0/components/link#viewtransition
105
111
  */
106
112
  viewTransition?: boolean;
107
- }>;
113
+ }
108
114
  type Props = (CommonProps & Readonly<{
109
115
  /**
110
116
  * A target destination window for a link to open in. Should only be used
@@ -160,22 +166,13 @@ export type ClickableState = Readonly<{
160
166
  type DefaultProps = Readonly<{
161
167
  disabled: Props["disabled"];
162
168
  }>;
163
- export type ChildrenProps = Readonly<{
164
- onClick: (e: React.SyntheticEvent) => unknown;
165
- onMouseEnter: (e: React.MouseEvent) => unknown;
166
- onMouseLeave: (e: React.MouseEvent) => unknown;
167
- onMouseDown: (e: React.MouseEvent) => unknown;
168
- onMouseUp: (e: React.MouseEvent) => unknown;
169
+ export interface ChildrenProps extends Required<ExposedEventHandlers> {
169
170
  onTouchStart: () => unknown;
170
171
  onTouchEnd: () => unknown;
171
172
  onTouchCancel: () => unknown;
172
- onKeyDown: (e: React.KeyboardEvent) => unknown;
173
- onKeyUp: (e: React.KeyboardEvent) => unknown;
174
- onFocus: (e: React.FocusEvent) => unknown;
175
- onBlur: (e: React.FocusEvent) => unknown;
176
173
  tabIndex?: number;
177
174
  rel?: string;
178
- }>;
175
+ }
179
176
  /**
180
177
  * Add hover, focus, and active status updates to a clickable component.
181
178
  *
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import type { AriaProps, StyleType } from "@khanacademy/wonder-blocks-core";
3
- import type { ClickableRole, ClickableState } from "./clickable-behavior";
3
+ import type { ClickableRole, ClickableState, ExposedEventHandlers } from "./clickable-behavior";
4
4
  type CommonProps =
5
5
  /**
6
6
  * aria-label should be used when `spinner={true}` to let people using screen
@@ -14,14 +14,6 @@ Partial<Omit<AriaProps, "aria-disabled">> & {
14
14
  * three boolean properties: hovered, focused, and pressed.
15
15
  */
16
16
  children: (clickableState: ClickableState) => React.ReactNode;
17
- /**
18
- * An onClick function which Clickable can execute when clicked
19
- */
20
- onClick?: (e: React.SyntheticEvent) => unknown;
21
- /**
22
- * An onFocus function which Clickable can execute when focused
23
- */
24
- onFocus?: (e: React.FocusEvent) => unknown;
25
17
  /**
26
18
  * Optional href which Clickable should direct to, uses client-side routing
27
19
  * by default if react-router is present
@@ -62,30 +54,6 @@ Partial<Omit<AriaProps, "aria-disabled">> & {
62
54
  * Test ID used for e2e testing.
63
55
  */
64
56
  testId?: string;
65
- /**
66
- * Respond to raw "keydown" event.
67
- */
68
- onKeyDown?: (e: React.KeyboardEvent) => unknown;
69
- /**
70
- * Respond to raw "keyup" event.
71
- */
72
- onKeyUp?: (e: React.KeyboardEvent) => unknown;
73
- /**
74
- * Respond to raw "mousedown" event.
75
- */
76
- onMouseDown?: (e: React.MouseEvent) => unknown;
77
- /**
78
- * Respond to raw "mouseup" event.
79
- */
80
- onMouseUp?: (e: React.MouseEvent) => unknown;
81
- /**
82
- * Respond to raw "mouseenter" event.
83
- */
84
- onMouseEnter?: (e: React.MouseEvent) => unknown;
85
- /**
86
- * Respond to raw "mouseleave" event.
87
- */
88
- onMouseLeave?: (e: React.MouseEvent) => unknown;
89
57
  /**
90
58
  * Don't show the default focus ring. This should be used when implementing
91
59
  * a custom focus ring within your own component that uses Clickable.
@@ -117,7 +85,7 @@ Partial<Omit<AriaProps, "aria-disabled">> & {
117
85
  * navigation is guaranteed to succeed.
118
86
  */
119
87
  safeWithNav?: () => Promise<unknown>;
120
- };
88
+ } & ExposedEventHandlers;
121
89
  type Props = (CommonProps & {
122
90
  href: string;
123
91
  /**
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.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};
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(_=>{}).finally(()=>{this.navigateOrReset(shouldNavigate);})}}runCallbackAndMaybeNavigate(e){const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined,href,type}=this.props;let shouldNavigate=true;let canSubmit=true;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=>{this.props.onMouseEnter?.(e);if(!this.waitingForClick){this.setState({hovered:true});}},this.handleMouseLeave=e=>{this.props.onMouseLeave?.(e);if(!this.waitingForClick){this.setState({hovered:false,pressed:false,focused:false});}},this.handleMouseDown=e=>{this.props.onMouseDown?.(e);this.setState({pressed:true});},this.handleMouseUp=e=>{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;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;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},()=>{onFocus?.(e);});},this.handleBlur=e=>{const{onBlur}=this.props;this.setState({focused:false,pressed:false},()=>{onBlur?.(e);});};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,onFocus,onKeyDown,onKeyUp,onMouseDown,onMouseUp,onMouseEnter,onMouseLeave,skipClientNav,beforeNav=undefined,safeWithNav=undefined,style,target=undefined,testId,hideDefaultFocusRing,disabled,tabIndex,...restProps}=props;const ClickableBehavior=getClickableBehavior(href,skipClientNav,inRouterContext);const getStyle=state=>[styles.reset,styles.link,!hideDefaultFocusRing&&state.focused&&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={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}`}},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,onBlur,onKeyDown,onKeyUp,onMouseDown,onMouseUp,onMouseEnter,onMouseLeave,skipClientNav,beforeNav=undefined,safeWithNav=undefined,style,target=undefined,testId,hideDefaultFocusRing,disabled,tabIndex,...restProps}=props;const ClickableBehavior=getClickableBehavior(href,skipClientNav,inRouterContext);const getStyle=state=>[styles.reset,styles.link,!hideDefaultFocusRing&&state.focused&&styles.focused,disabled&&styles.disabled,style];const sharedProps={href,onClick,safeWithNav,onFocus,onBlur,onKeyDown,onKeyUp,onMouseDown,onMouseUp,onMouseEnter,onMouseLeave,disabled,tabIndex};if(beforeNav){return jsx(ClickableBehavior,{...sharedProps,beforeNav:beforeNav,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}else {return jsx(ClickableBehavior,{...sharedProps,target:target,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}});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}`}},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.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};
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(_=>{}).finally(()=>{this.navigateOrReset(shouldNavigate);})}}runCallbackAndMaybeNavigate(e){const{onClick=undefined,beforeNav=undefined,safeWithNav=undefined,href,type}=this.props;let shouldNavigate=true;let canSubmit=true;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=>{this.props.onMouseEnter?.(e);if(!this.waitingForClick){this.setState({hovered:true});}},this.handleMouseLeave=e=>{this.props.onMouseLeave?.(e);if(!this.waitingForClick){this.setState({hovered:false,pressed:false,focused:false});}},this.handleMouseDown=e=>{this.props.onMouseDown?.(e);this.setState({pressed:true});},this.handleMouseUp=e=>{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;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;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},()=>{onFocus?.(e);});},this.handleBlur=e=>{const{onBlur}=this.props;this.setState({focused:false,pressed:false},()=>{onBlur?.(e);});};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,onFocus,onKeyDown,onKeyUp,onMouseDown,onMouseUp,onMouseEnter,onMouseLeave,skipClientNav,beforeNav=undefined,safeWithNav=undefined,style,target=undefined,testId,hideDefaultFocusRing,disabled,tabIndex,...restProps}=props;const ClickableBehavior=getClickableBehavior(href,skipClientNav,inRouterContext);const getStyle=state=>[styles.reset,styles.link,!hideDefaultFocusRing&&state.focused&&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={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}`}},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,onBlur,onKeyDown,onKeyUp,onMouseDown,onMouseUp,onMouseEnter,onMouseLeave,skipClientNav,beforeNav=undefined,safeWithNav=undefined,style,target=undefined,testId,hideDefaultFocusRing,disabled,tabIndex,...restProps}=props;const ClickableBehavior=getClickableBehavior(href,skipClientNav,inRouterContext);const getStyle=state=>[styles.reset,styles.link,!hideDefaultFocusRing&&state.focused&&styles.focused,disabled&&styles.disabled,style];const sharedProps={href,onClick,safeWithNav,onFocus,onBlur,onKeyDown,onKeyUp,onMouseDown,onMouseUp,onMouseEnter,onMouseLeave,disabled,tabIndex};if(beforeNav){return jsxRuntime.jsx(ClickableBehavior,{...sharedProps,beforeNav:beforeNav,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}else {return jsxRuntime.jsx(ClickableBehavior,{...sharedProps,target:target,children:(state,childrenProps)=>getCorrectTag(state,inRouterContext,{...restProps,"data-testid":testId,style:getStyle(state),...childrenProps})})}});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}`}},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
@@ -3,7 +3,7 @@
3
3
  "description": "Clickable component for Wonder-Blocks.",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "8.0.5",
6
+ "version": "8.1.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },