You've already forked focalboard
mirror of
https://github.com/mattermost/focalboard.git
synced 2025-07-12 23:50:27 +02:00
Fix hover menu behavior on ViewHeader component (#2662)
This commit is contained in:
@ -253,23 +253,29 @@ const ViewMenu = (props: Props) => {
|
|||||||
/>))}
|
/>))}
|
||||||
<BoardPermissionGate permissions={[Permission.ManageBoardProperties]}>
|
<BoardPermissionGate permissions={[Permission.ManageBoardProperties]}>
|
||||||
<Menu.Separator/>
|
<Menu.Separator/>
|
||||||
|
</BoardPermissionGate>
|
||||||
{!props.readonly &&
|
{!props.readonly &&
|
||||||
|
<BoardPermissionGate permissions={[Permission.ManageBoardProperties]}>
|
||||||
<Menu.Text
|
<Menu.Text
|
||||||
id='__duplicateView'
|
id='__duplicateView'
|
||||||
name={duplicateViewText}
|
name={duplicateViewText}
|
||||||
icon={<DuplicateIcon/>}
|
icon={<DuplicateIcon/>}
|
||||||
onClick={handleDuplicateView}
|
onClick={handleDuplicateView}
|
||||||
/>
|
/>
|
||||||
|
</BoardPermissionGate>
|
||||||
}
|
}
|
||||||
{!props.readonly && views.length > 1 &&
|
{!props.readonly && views.length > 1 &&
|
||||||
|
<BoardPermissionGate permissions={[Permission.ManageBoardProperties]}>
|
||||||
<Menu.Text
|
<Menu.Text
|
||||||
id='__deleteView'
|
id='__deleteView'
|
||||||
name={deleteViewText}
|
name={deleteViewText}
|
||||||
icon={<DeleteIcon/>}
|
icon={<DeleteIcon/>}
|
||||||
onClick={handleDeleteView}
|
onClick={handleDeleteView}
|
||||||
/>
|
/>
|
||||||
|
</BoardPermissionGate>
|
||||||
}
|
}
|
||||||
{!props.readonly &&
|
{!props.readonly &&
|
||||||
|
<BoardPermissionGate permissions={[Permission.ManageBoardProperties]}>
|
||||||
<Menu.SubMenu
|
<Menu.SubMenu
|
||||||
id='__addView'
|
id='__addView'
|
||||||
name={addViewText}
|
name={addViewText}
|
||||||
@ -300,8 +306,8 @@ const ViewMenu = (props: Props) => {
|
|||||||
onClick={handleAddViewCalendar}
|
onClick={handleAddViewCalendar}
|
||||||
/>
|
/>
|
||||||
</Menu.SubMenu>
|
</Menu.SubMenu>
|
||||||
}
|
|
||||||
</BoardPermissionGate>
|
</BoardPermissionGate>
|
||||||
|
}
|
||||||
</Menu>
|
</Menu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import SeparatorOption from './separatorOption'
|
|||||||
import SwitchOption from './switchOption'
|
import SwitchOption from './switchOption'
|
||||||
import TextOption from './textOption'
|
import TextOption from './textOption'
|
||||||
import ColorOption from './colorOption'
|
import ColorOption from './colorOption'
|
||||||
import SubMenuOption from './subMenuOption'
|
import SubMenuOption, {HoveringContext} from './subMenuOption'
|
||||||
import LabelOption from './labelOption'
|
import LabelOption from './labelOption'
|
||||||
|
|
||||||
import './menu.scss'
|
import './menu.scss'
|
||||||
@ -27,7 +27,7 @@ export default class Menu extends React.PureComponent<Props> {
|
|||||||
static Label = LabelOption
|
static Label = LabelOption
|
||||||
|
|
||||||
public state = {
|
public state = {
|
||||||
hoveringIdx: -1,
|
hovering: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
@ -36,16 +36,14 @@ export default class Menu extends React.PureComponent<Props> {
|
|||||||
<div className={'Menu noselect ' + (position || 'bottom')}>
|
<div className={'Menu noselect ' + (position || 'bottom')}>
|
||||||
<div className='menu-contents'>
|
<div className='menu-contents'>
|
||||||
<div className='menu-options'>
|
<div className='menu-options'>
|
||||||
{React.Children.map(children, (child, i) => {
|
{React.Children.map(children, (child) => (
|
||||||
return addChildMenuItem({
|
<div
|
||||||
child,
|
onMouseEnter={() => this.setState({hovering: child})}
|
||||||
onMouseEnter: () =>
|
>
|
||||||
this.setState({
|
<HoveringContext.Provider value={child == this.state.hovering}>
|
||||||
hoveringIdx: i,
|
{child}
|
||||||
}),
|
</HoveringContext.Provider>
|
||||||
isHovering: () => i === this.state.hoveringIdx,
|
</div>))}
|
||||||
})
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='menu-spacer hideOnWidescreen'/>
|
<div className='menu-spacer hideOnWidescreen'/>
|
||||||
@ -67,28 +65,3 @@ export default class Menu extends React.PureComponent<Props> {
|
|||||||
// No need to do anything, as click bubbled up to MenuWrapper, which closes
|
// No need to do anything, as click bubbled up to MenuWrapper, which closes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addChildMenuItem(props: {child: React.ReactNode, onMouseEnter: () => void, isHovering: () => boolean}): JSX.Element | null {
|
|
||||||
const {child, onMouseEnter, isHovering} = props
|
|
||||||
if (child !== null) {
|
|
||||||
if (React.isValidElement(child)) {
|
|
||||||
const castedChild = child as React.ReactElement
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
onMouseEnter={onMouseEnter}
|
|
||||||
>
|
|
||||||
{castedChild.type === SubMenuOption ? (
|
|
||||||
<castedChild.type
|
|
||||||
{...castedChild.props}
|
|
||||||
isHovering={isHovering}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<castedChild.type {...castedChild.props}/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (null)
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useEffect, useState} from 'react'
|
import React, {useEffect, useState, useContext} from 'react'
|
||||||
|
|
||||||
import SubmenuTriangleIcon from '../icons/submenuTriangle'
|
import SubmenuTriangleIcon from '../icons/submenuTriangle'
|
||||||
|
|
||||||
@ -8,25 +8,27 @@ import Menu from '.'
|
|||||||
|
|
||||||
import './subMenuOption.scss'
|
import './subMenuOption.scss'
|
||||||
|
|
||||||
|
export const HoveringContext = React.createContext(false)
|
||||||
|
|
||||||
type SubMenuOptionProps = {
|
type SubMenuOptionProps = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
position?: 'bottom' | 'top' | 'left' | 'left-bottom'
|
position?: 'bottom' | 'top' | 'left' | 'left-bottom'
|
||||||
icon?: React.ReactNode
|
icon?: React.ReactNode
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
isHovering?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubMenuOption(props: SubMenuOptionProps): JSX.Element {
|
function SubMenuOption(props: SubMenuOptionProps): JSX.Element {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
const isHovering = useContext(HoveringContext)
|
||||||
|
|
||||||
const openLeftClass = props.position === 'left' || props.position === 'left-bottom' ? ' open-left' : ''
|
const openLeftClass = props.position === 'left' || props.position === 'left-bottom' ? ' open-left' : ''
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.isHovering !== undefined) {
|
if (isHovering !== undefined) {
|
||||||
setIsOpen(props.isHovering)
|
setIsOpen(isHovering)
|
||||||
}
|
}
|
||||||
}, [props.isHovering])
|
}, [isHovering])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
Reference in New Issue
Block a user