-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
Menu.js
204 lines (169 loc) · 5.4 KB
/
Menu.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
import cx from 'clsx'
import _ from 'lodash'
import PropTypes from 'prop-types'
import * as React from 'react'
import {
childrenUtils,
customPropTypes,
createShorthandFactory,
getComponentType,
getUnhandledProps,
SUI,
useKeyOnly,
useKeyOrValueAndKey,
useValueAndKey,
useWidthProp,
useAutoControlledValue,
} from '../../lib'
import MenuHeader from './MenuHeader'
import MenuItem from './MenuItem'
import MenuMenu from './MenuMenu'
/**
* A menu displays grouped navigation actions.
* @see Dropdown
*/
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,
})
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 = getComponentType(props)
if (!childrenUtils.isNil(children)) {
return (
<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,
/** Index of the currently active item. */
activeIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/** A menu may be attached to other content segments. */
attached: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['top', 'bottom'])]),
/** A menu item or menu can have no borders. */
borderless: PropTypes.bool,
/** Primary content. */
children: PropTypes.node,
/** Additional classes. */
className: PropTypes.string,
/** Additional colors can be specified. */
color: PropTypes.oneOf(SUI.COLORS),
/** A menu can take up only the space necessary to fit its content. */
compact: PropTypes.bool,
/** Initial activeIndex value. */
defaultActiveIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
/** A menu can be fixed to a side of its context. */
fixed: PropTypes.oneOf(['left', 'right', 'bottom', 'top']),
/** A menu can be floated. */
floated: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['right'])]),
/** A vertical menu may take the size of its container. */
fluid: PropTypes.bool,
/** A menu may have just icons (bool) or labeled icons. */
icon: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['labeled'])]),
/** A menu may have its colors inverted to show greater contrast. */
inverted: PropTypes.bool,
/** Shorthand array of props for Menu. */
items: customPropTypes.collectionShorthand,
/**
* onClick handler for MenuItem. Mutually exclusive with children.
*
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All item props.
*/
onItemClick: customPropTypes.every([customPropTypes.disallow(['children']), PropTypes.func]),
/** A pagination menu is specially formatted to present links to pages of content. */
pagination: PropTypes.bool,
/** A menu can point to show its relationship to nearby content. */
pointing: PropTypes.bool,
/** A menu can adjust its appearance to de-emphasize its contents. */
secondary: PropTypes.bool,
/** A menu can vary in size. */
size: PropTypes.oneOf(_.without(SUI.SIZES, 'medium', 'big')),
/** A menu can stack at mobile resolutions. */
stackable: PropTypes.bool,
/** A menu can be formatted to show tabs of information. */
tabular: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['right'])]),
/** A menu can be formatted for text content. */
text: PropTypes.bool,
/** A vertical menu displays elements vertically. */
vertical: PropTypes.bool,
/** A menu can have its items divided evenly. */
widths: PropTypes.oneOf(SUI.WIDTHS),
}
Menu.Header = MenuHeader
Menu.Item = MenuItem
Menu.Menu = MenuMenu
Menu.create = createShorthandFactory(Menu, (items) => ({ items }))
export default Menu