Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 83 additions & 80 deletions src/collections/Menu/Menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'
import React from 'react'

import {
ModernAutoControlledComponent as Component,
childrenUtils,
customPropTypes,
createShorthandFactory,
Expand All @@ -15,6 +14,7 @@ import {
useKeyOrValueAndKey,
useValueAndKey,
useWidthProp,
useAutoControlledValue,
} from '../../lib'
import MenuHeader from './MenuHeader'
import MenuItem from './MenuItem'
Expand All @@ -24,90 +24,95 @@ import MenuMenu from './MenuMenu'
* A menu displays grouped navigation actions.
* @see Dropdown
*/
class Menu extends Component {
handleItemOverrides = (predefinedProps) => ({
onClick: (e, itemProps) => {
const { index } = itemProps

this.setState({ activeIndex: index })

_.invoke(predefinedProps, 'onClick', e, itemProps)
_.invoke(this.props, 'onItemClick', e, itemProps)
},
const Menu = React.forwardRef(function (props, ref) {
const {
attached,
borderless,
children,
className,
color,
compact,
fixed,
floated,
fluid,
icon,
inverted,
items,
pagination,
pointing,
secondary,
size,
stackable,
tabular,
text,
vertical,
widths,
} = props
const [activeIndex, setActiveIndex] = useAutoControlledValue({
state: props.activeIndex,
defaultState: props.defaultActiveIndex,
initialState: -1,
})

renderItems() {
const { items } = this.props
const { activeIndex } = this.state

return _.map(items, (item, index) =>
MenuItem.create(item, {
defaultProps: {
active: parseInt(activeIndex, 10) === index,
index,
},
overrideProps: this.handleItemOverrides,
}),
)
}

render() {
const {
attached,
borderless,
children,
className,
color,
compact,
fixed,
floated,
fluid,
icon,
inverted,
pagination,
pointing,
secondary,
size,
stackable,
tabular,
text,
vertical,
widths,
} = this.props
const classes = cx(
'ui',
color,
size,
useKeyOnly(borderless, 'borderless'),
useKeyOnly(compact, 'compact'),
useKeyOnly(fluid, 'fluid'),
useKeyOnly(inverted, 'inverted'),
useKeyOnly(pagination, 'pagination'),
useKeyOnly(pointing, 'pointing'),
useKeyOnly(secondary, 'secondary'),
useKeyOnly(stackable, 'stackable'),
useKeyOnly(text, 'text'),
useKeyOnly(vertical, 'vertical'),
useKeyOrValueAndKey(attached, 'attached'),
useKeyOrValueAndKey(floated, 'floated'),
useKeyOrValueAndKey(icon, 'icon'),
useKeyOrValueAndKey(tabular, 'tabular'),
useValueAndKey(fixed, 'fixed'),
useWidthProp(widths, 'item'),
className,
'menu',
)
const rest = getUnhandledProps(Menu, this.props)
const ElementType = getElementType(Menu, this.props)

const classes = cx(
'ui',
color,
size,
useKeyOnly(borderless, 'borderless'),
useKeyOnly(compact, 'compact'),
useKeyOnly(fluid, 'fluid'),
useKeyOnly(inverted, 'inverted'),
useKeyOnly(pagination, 'pagination'),
useKeyOnly(pointing, 'pointing'),
useKeyOnly(secondary, 'secondary'),
useKeyOnly(stackable, 'stackable'),
useKeyOnly(text, 'text'),
useKeyOnly(vertical, 'vertical'),
useKeyOrValueAndKey(attached, 'attached'),
useKeyOrValueAndKey(floated, 'floated'),
useKeyOrValueAndKey(icon, 'icon'),
useKeyOrValueAndKey(tabular, 'tabular'),
useValueAndKey(fixed, 'fixed'),
useWidthProp(widths, 'item'),
className,
'menu',
)
const rest = getUnhandledProps(Menu, props)
const ElementType = getElementType(Menu, props)

if (!childrenUtils.isNil(children)) {
return (
<ElementType {...rest} className={classes}>
{childrenUtils.isNil(children) ? this.renderItems() : children}
<ElementType {...rest} className={classes} ref={ref}>
{children}
</ElementType>
)
}
}

return (
<ElementType {...rest} className={classes} ref={ref}>
{_.map(items, (item, index) =>
MenuItem.create(item, {
defaultProps: {
active: parseInt(activeIndex, 10) === index,
index,
},
overrideProps: (predefinedProps) => ({
onClick: (e, itemProps) => {
const itemIndex = itemProps.index

setActiveIndex(itemIndex)

_.invoke(predefinedProps, 'onClick', e, itemProps)
_.invoke(props, 'onItemClick', e, itemProps)
},
}),
}),
)}
</ElementType>
)
})

Menu.displayName = 'Menu'
Menu.propTypes = {
/** An element type to render as (string or function). */
as: PropTypes.elementType,
Expand Down Expand Up @@ -190,8 +195,6 @@ Menu.propTypes = {
widths: PropTypes.oneOf(SUI.WIDTHS),
}

Menu.autoControlledProps = ['activeIndex']

Menu.Header = MenuHeader
Menu.Item = MenuItem
Menu.Menu = MenuMenu
Expand Down
7 changes: 4 additions & 3 deletions src/collections/Menu/MenuHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ import { childrenUtils, customPropTypes, getElementType, getUnhandledProps } fro
/**
* A menu item may include a header or may itself be a header.
*/
function MenuHeader(props) {
const MenuHeader = React.forwardRef(function (props, ref) {
const { children, className, content } = props
const classes = cx('header', className)
const rest = getUnhandledProps(MenuHeader, props)
const ElementType = getElementType(MenuHeader, props)

return (
<ElementType {...rest} className={classes}>
<ElementType {...rest} className={classes} ref={ref}>
{childrenUtils.isNil(children) ? content : children}
</ElementType>
)
}
})

MenuHeader.displayName = 'MenuHeader'
MenuHeader.propTypes = {
/** An element type to render as (string or function). */
as: PropTypes.elementType,
Expand Down
106 changes: 54 additions & 52 deletions src/collections/Menu/MenuItem.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import cx from 'clsx'
import _ from 'lodash'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import React from 'react'

import {
childrenUtils,
Expand All @@ -12,70 +12,70 @@ import {
SUI,
useKeyOnly,
useKeyOrValueAndKey,
useEventCallback,
} from '../../lib'
import Icon from '../../elements/Icon'

/**
* A menu can contain an item.
*/
export default class MenuItem extends Component {
handleClick = (e) => {
const { disabled } = this.props

if (!disabled) _.invoke(this.props, 'onClick', e, this.props)
}

render() {
const {
active,
children,
className,
color,
content,
disabled,
fitted,
header,
icon,
link,
name,
onClick,
position,
} = this.props

const classes = cx(
color,
position,
useKeyOnly(active, 'active'),
useKeyOnly(disabled, 'disabled'),
useKeyOnly(icon === true || (icon && !(name || content)), 'icon'),
useKeyOnly(header, 'header'),
useKeyOnly(link, 'link'),
useKeyOrValueAndKey(fitted, 'fitted'),
'item',
className,
)
const ElementType = getElementType(MenuItem, this.props, () => {
if (onClick) return 'a'
})
const rest = getUnhandledProps(MenuItem, this.props)

if (!childrenUtils.isNil(children)) {
return (
<ElementType {...rest} className={classes} onClick={this.handleClick}>
{children}
</ElementType>
)
const MenuItem = React.forwardRef(function (props, ref) {
const {
active,
children,
className,
color,
content,
disabled,
fitted,
header,
icon,
link,
name,
onClick,
position,
} = props

const classes = cx(
color,
position,
useKeyOnly(active, 'active'),
useKeyOnly(disabled, 'disabled'),
useKeyOnly(icon === true || (icon && !(name || content)), 'icon'),
useKeyOnly(header, 'header'),
useKeyOnly(link, 'link'),
useKeyOrValueAndKey(fitted, 'fitted'),
'item',
className,
)
const ElementType = getElementType(MenuItem, props, () => {
if (onClick) return 'a'
})
const rest = getUnhandledProps(MenuItem, props)

const handleClick = useEventCallback((e) => {
if (!disabled) {
_.invoke(props, 'onClick', e, props)
}
})

if (!childrenUtils.isNil(children)) {
return (
<ElementType {...rest} className={classes} onClick={this.handleClick}>
{Icon.create(icon, { autoGenerateKey: false })}
{childrenUtils.isNil(content) ? _.startCase(name) : content}
<ElementType {...rest} className={classes} onClick={handleClick} ref={ref}>
{children}
</ElementType>
)
}
}

return (
<ElementType {...rest} className={classes} onClick={handleClick} ref={ref}>
{Icon.create(icon, { autoGenerateKey: false })}
{childrenUtils.isNil(content) ? _.startCase(name) : content}
</ElementType>
)
})

MenuItem.displayName = 'MenuItem'
MenuItem.propTypes = {
/** An element type to render as (string or function). */
as: PropTypes.elementType,
Expand Down Expand Up @@ -130,3 +130,5 @@ MenuItem.propTypes = {
}

MenuItem.create = createShorthandFactory(MenuItem, (val) => ({ content: val, name: val }))

export default MenuItem
7 changes: 4 additions & 3 deletions src/collections/Menu/MenuMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ import { childrenUtils, customPropTypes, getElementType, getUnhandledProps } fro
/**
* A menu can contain a sub menu.
*/
function MenuMenu(props) {
const MenuMenu = React.forwardRef(function (props, ref) {
const { children, className, content, position } = props

const classes = cx(position, 'menu', className)
const rest = getUnhandledProps(MenuMenu, props)
const ElementType = getElementType(MenuMenu, props)

return (
<ElementType {...rest} className={classes}>
<ElementType {...rest} className={classes} ref={ref}>
{childrenUtils.isNil(children) ? content : children}
</ElementType>
)
}
})

MenuMenu.displayName = 'MenuMenu'
MenuMenu.propTypes = {
/** An element type to render as (string or function). */
as: PropTypes.elementType,
Expand Down
Loading