Migrating to menuWrapper to functional component
This commit is contained in:
parent
e441c4ee91
commit
009639b1ea
1 changed files with 52 additions and 68 deletions
|
@ -1,75 +1,49 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import React, {useRef, useState, useEffect} from 'react'
|
||||
|
||||
import './menuWrapper.scss'
|
||||
|
||||
type Props = {
|
||||
children?: React.ReactNode;
|
||||
onToggle?: (open: boolean) => void;
|
||||
isDisabled?: boolean;
|
||||
stopPropagationOnToggle?: boolean;
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
type State = {
|
||||
open: boolean;
|
||||
}
|
||||
const MenuWrapper = React.memo((props: Props) => {
|
||||
const node = useRef<HTMLDivElement>(null)
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
export default class MenuWrapper extends React.PureComponent<Props, State> {
|
||||
private node: React.RefObject<HTMLDivElement>
|
||||
|
||||
public constructor(props: Props) {
|
||||
super(props)
|
||||
if (!Array.isArray(props.children) || props.children.length !== 2) {
|
||||
throw new Error('MenuWrapper needs exactly 2 children')
|
||||
}
|
||||
this.state = {
|
||||
open: false,
|
||||
}
|
||||
this.node = React.createRef()
|
||||
if (!Array.isArray(props.children) || props.children.length !== 2) {
|
||||
throw new Error('MenuWrapper needs exactly 2 children')
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
document.addEventListener('menuItemClicked', this.close, true)
|
||||
document.addEventListener('click', this.closeOnBlur, true)
|
||||
document.addEventListener('keyup', this.keyboardClose, true)
|
||||
const close = (): void => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
document.removeEventListener('menuItemClicked', this.close, true)
|
||||
document.removeEventListener('click', this.closeOnBlur, true)
|
||||
document.removeEventListener('keyup', this.keyboardClose, true)
|
||||
}
|
||||
|
||||
private keyboardClose = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
this.close()
|
||||
}
|
||||
|
||||
if (e.key === 'Tab') {
|
||||
this.closeOnBlur(e)
|
||||
}
|
||||
}
|
||||
|
||||
private closeOnBlur = (e: Event) => {
|
||||
if (this.node && this.node.current && e.target && this.node.current.contains(e.target as Node)) {
|
||||
const closeOnBlur = (e: Event) => {
|
||||
if (e.target && node.current?.contains(e.target as Node)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.close()
|
||||
close()
|
||||
}
|
||||
|
||||
public close = (): void => {
|
||||
if (this.state.open) {
|
||||
this.setState({open: false})
|
||||
const keyboardClose = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
close()
|
||||
}
|
||||
|
||||
if (e.key === 'Tab') {
|
||||
closeOnBlur(e)
|
||||
}
|
||||
}
|
||||
|
||||
private toggle = (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
|
||||
if (this.props.disabled) {
|
||||
const toggle = (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
|
||||
if (props.disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -79,33 +53,43 @@ export default class MenuWrapper extends React.PureComponent<Props, State> {
|
|||
* We need to refactor this so that the modal is explicitly closed on toggle, but for now I am aiming to preserve the existing logic
|
||||
* so as to not break other things
|
||||
**/
|
||||
if (this.props.stopPropagationOnToggle) {
|
||||
if (props.stopPropagationOnToggle) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
const newState = !this.state.open
|
||||
this.setState({open: newState})
|
||||
setOpen(!open)
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const {children} = this.props
|
||||
let className = 'MenuWrapper'
|
||||
if (this.props.disabled) {
|
||||
className += ' disabled'
|
||||
}
|
||||
if (this.props.className) {
|
||||
className += ' ' + this.props.className
|
||||
useEffect(() => {
|
||||
document.addEventListener('menuItemClicked', close, true)
|
||||
document.addEventListener('click', closeOnBlur, true)
|
||||
document.addEventListener('keyup', keyboardClose, true)
|
||||
return () => {
|
||||
document.removeEventListener('menuItemClicked', close, true)
|
||||
document.removeEventListener('click', closeOnBlur, true)
|
||||
document.removeEventListener('keyup', keyboardClose, true)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onClick={this.toggle}
|
||||
ref={this.node}
|
||||
>
|
||||
{children ? Object.values(children)[0] : null}
|
||||
{children && !this.props.disabled && this.state.open ? Object.values(children)[1] : null}
|
||||
</div>
|
||||
)
|
||||
const {children} = props
|
||||
let className = 'MenuWrapper'
|
||||
if (props.disabled) {
|
||||
className += ' disabled'
|
||||
}
|
||||
}
|
||||
if (props.className) {
|
||||
className += ' ' + props.className
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onClick={toggle}
|
||||
ref={node}
|
||||
>
|
||||
{children ? Object.values(children)[0] : null}
|
||||
{children && !props.disabled && open ? Object.values(children)[1] : null}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default MenuWrapper
|
||||
|
|
Loading…
Reference in a new issue