From 2c2213893e3b8689a81c4ab18649bd1c0fb533f9 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 16 Jul 2018 18:02:50 -0300 Subject: [PATCH] Add withMouseHover HOC --- src/core/client/ui/hocs/index.ts | 1 + src/core/client/ui/hocs/withKeyboardFocus.tsx | 11 ++-- src/core/client/ui/hocs/withMouseHover.tsx | 65 +++++++++++++++++++ src/core/client/ui/hocs/withStyles.ts | 5 +- 4 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 src/core/client/ui/hocs/withMouseHover.tsx diff --git a/src/core/client/ui/hocs/index.ts b/src/core/client/ui/hocs/index.ts index 1c49cdc24..1dd4dbe65 100644 --- a/src/core/client/ui/hocs/index.ts +++ b/src/core/client/ui/hocs/index.ts @@ -1,2 +1,3 @@ export { default as withStyles } from "./withStyles"; export { default as withKeyboardFocus } from "./withKeyboardFocus"; +export { default as withMouseHover } from "./withMouseHover"; diff --git a/src/core/client/ui/hocs/withKeyboardFocus.tsx b/src/core/client/ui/hocs/withKeyboardFocus.tsx index eaae1529d..00ddc65a6 100644 --- a/src/core/client/ui/hocs/withKeyboardFocus.tsx +++ b/src/core/client/ui/hocs/withKeyboardFocus.tsx @@ -3,10 +3,10 @@ import { FocusEvent, MouseEvent } from "react"; import { hoistStatics } from "recompose"; interface InjectedProps { - onFocus?: React.EventHandler>; - onBlur?: React.EventHandler>; - onMouseDown?: React.EventHandler>; - keyboardFocus?: boolean; + onFocus: React.EventHandler>; + onBlur: React.EventHandler>; + onMouseDown: React.EventHandler>; + keyboardFocus: boolean; } /** @@ -61,4 +61,5 @@ export default hoistStatics( return WithKeyboardFocus as React.ComponentType; } -); + // TODO: workaround, add bug link. +) as (WrappedComponent: T) => T; diff --git a/src/core/client/ui/hocs/withMouseHover.tsx b/src/core/client/ui/hocs/withMouseHover.tsx new file mode 100644 index 000000000..8f3b0129a --- /dev/null +++ b/src/core/client/ui/hocs/withMouseHover.tsx @@ -0,0 +1,65 @@ +import * as React from "react"; +import { MouseEvent, TouchEvent } from "react"; +import { hoistStatics } from "recompose"; + +interface InjectedProps { + onMouseOver: React.EventHandler>; + onMouseOut: React.EventHandler>; + onTouchEnd: React.EventHandler>; + mouseHover: boolean; +} + +/** + * withMouseHover provides a property `MouseHover: boolean` + * to indicate a focus on the element, that wasn't triggered by mouse + * or touch. + */ +export default hoistStatics( + (WrappedComponent: React.ComponentType) => { + class WithMouseHover extends React.Component { + public state = { + mouseHover: false, + lastTouchEndTime: 0, + }; + + private handleTouchEnd: React.EventHandler> = event => { + if (this.props.onTouchEnd) { + this.props.onTouchEnd(event); + } + this.setState({ lastTouchEndTime: new Date().getTime() }); + }; + + private handleMouseOver: React.EventHandler> = event => { + if (this.props.onMouseOver) { + this.props.onMouseOver(event); + } + const now = new Date().getTime(); + if (now - this.state.lastTouchEndTime > 750) { + this.setState({ mouseHover: true }); + } + }; + + private handleMouseOut: React.EventHandler> = event => { + if (this.props.onMouseOut) { + this.props.onMouseOut(event); + } + this.setState({ mouseHover: false }); + }; + + public render() { + return ( + + ); + } + } + + return WithMouseHover as React.ComponentType; + } + // TODO: workaround, add bug link. +) as (WrappedComponent: T) => T; diff --git a/src/core/client/ui/hocs/withStyles.ts b/src/core/client/ui/hocs/withStyles.ts index 0ae53e860..d8ae4313b 100644 --- a/src/core/client/ui/hocs/withStyles.ts +++ b/src/core/client/ui/hocs/withStyles.ts @@ -13,17 +13,18 @@ function withStyles( ): DefaultingInferableComponentEnhancer<{ classes?: Partial }> { const classes = { ...(styles as any) }; return withPropsOnChange(["classes"], props => { + const resolvedClasses = { ...classes }; if (props.classes) { Object.keys(props.classes).forEach(k => { if (classes[k]) { - classes[k] += ` ${props.classes[k]}`; + resolvedClasses[k] += ` ${props.classes[k]}`; } else if (process.env.NODE_ENV !== "production") { // tslint:disable:next-line: no-console console.warn("Extending non existant className", k); } }); } - return { classes }; + return { classes: resolvedClasses }; }); }