Skip to content

Commit

Permalink
fix: adds support for multiple refs for mouse outside
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Micko committed Feb 16, 2019
1 parent 76c787d commit ea46b25
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 17 deletions.
14 changes: 9 additions & 5 deletions packages/react-forms/src/InputDate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ type State = {

export default class InputDate extends React.Component<Props, State> {
component = React.createRef /*:: <HTMLElement> */();
s = React.createRef /*:: <HTMLElement> */();
refA = React.createRef /*:: <HTMLElement> */();
refB = React.createRef /*:: <HTMLElement> */();

state = {
current: undefined,
Expand Down Expand Up @@ -108,8 +109,11 @@ export default class InputDate extends React.Component<Props, State> {
const isDateValid = !isNaN(dateValue.getTime());

return (
<MouseOutside onClickOutside={this.handleClose}>
{mouseOutsideRef => (
<MouseOutside
onClickOutside={this.handleClose}
refs={[this.refA, this.refB]}
>
{() => (
<Manager>
<Reference>
{({ ref }) => (
Expand All @@ -130,7 +134,7 @@ export default class InputDate extends React.Component<Props, State> {
onClick={this.preventDefault}
min={min}
max={max}
ref={mergeRefs(ref, mouseOutsideRef)}
ref={mergeRefs(ref, this.refA)}
{...props}
/>
)}
Expand All @@ -145,7 +149,7 @@ export default class InputDate extends React.Component<Props, State> {
selected={isDateValid ? dateValue : undefined}
minDate={min ? new Date(min) : undefined}
maxDate={max ? new Date(max) : undefined}
ref={mergeRefs(ref, this.component)}
ref={mergeRefs(ref, this.component, this.refB)}
style={style}
/>
)}
Expand Down
34 changes: 24 additions & 10 deletions packages/react-mouse-outside/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,48 @@ type Props = {
onMoveOutside?: Event => void,
delay: number,
children: RenderProp<React.ElementRef<any>>,
refs: Array<React.ElementRef<any>>,
};

function isElement(target): boolean %checks {
// $FlowFixMe HTMLDocument isn't supported (https://github.com/facebook/flow/issues/2839)
return target instanceof HTMLElement || target instanceof HTMLDocument;
}

function filterInvalidRefs(refs): Array<React.ElementRef<any>> {
return refs.filter(ref => {
return ref.current && ref.current instanceof HTMLElement;
});
}
function isTargetOutside(ref, target) {
return ref.current.contains(target) === false;
}

/** @visibleName Usage example */
export default class MouseOutside extends React.Component<Props> {
static defaultProps = {
delay: 0,
refs: [],
};

container = React.createRef /*:: <HTMLElement> */();

isTargetOutside = (target: EventTarget) => {
return (
// $FlowFixMe HTMLDocument isn't supported (https://github.com/facebook/flow/issues/2839)
(target instanceof HTMLElement || target instanceof HTMLDocument) &&
(this.container.current instanceof HTMLElement &&
!this.container.current.contains(target) &&
document.contains(target))
);
areTargetsOutside = (target: EventTarget) => {
const refs = [this.container, ...this.props.refs];
if (isElement(target) && document.contains(target)) {
return filterInvalidRefs(refs).every(ref => isTargetOutside(ref, target));
}
return false;
};

handleClickOutside = (evt: Event) => {
if (this.props.onClickOutside && this.isTargetOutside(evt.target)) {
if (this.props.onClickOutside && this.areTargetsOutside(evt.target)) {
this.props.onClickOutside(evt);
}
};

handleMoveOutside = conditionalDebounce((evt: Event) => {
if (this.props.onMoveOutside && this.isTargetOutside(evt.target)) {
if (this.props.onMoveOutside && this.areTargetsOutside(evt.target)) {
this.props.onMoveOutside(evt);
}
}, this.props.delay);
Expand Down
6 changes: 4 additions & 2 deletions packages/react-mouse-outside/src/index.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
```jsx
initialState = { txt: 'click outside' };
const refA = React.createRef();

const callback = () =>
setState({ txt: 'clicked!' }, () =>
setTimeout(() => setState({ txt: 'click outside' }), 300)
);

<MouseOutside onClickOutside={callback}>
{getRef => <div ref={getRef}>{state.txt}</div>}
<MouseOutside onClickOutside={callback} refs={[refA]}>
{() => <div ref={refA}>{state.txt}</div>}
</MouseOutside>;
```

Expand Down

0 comments on commit ea46b25

Please sign in to comment.