@instructure/ui-menu 11.6.0 → 11.6.1-snapshot-129

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/CHANGELOG.md +41 -297
  2. package/es/Menu/{MenuItem → v1/MenuItem}/index.js +2 -2
  3. package/es/Menu/{MenuItemGroup → v1/MenuItemGroup}/index.js +1 -1
  4. package/es/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/index.js +1 -1
  5. package/es/Menu/{index.js → v1/index.js} +3 -3
  6. package/es/Menu/v2/MenuItem/index.js +262 -0
  7. package/es/Menu/v2/MenuItem/props.js +26 -0
  8. package/es/Menu/v2/MenuItem/styles.js +146 -0
  9. package/es/Menu/v2/MenuItemGroup/index.js +175 -0
  10. package/es/Menu/v2/MenuItemGroup/props.js +26 -0
  11. package/es/Menu/v2/MenuItemGroup/styles.js +60 -0
  12. package/es/Menu/v2/MenuItemSeparator/index.js +70 -0
  13. package/es/Menu/v2/MenuItemSeparator/props.js +29 -0
  14. package/es/Menu/v2/MenuItemSeparator/styles.js +46 -0
  15. package/es/Menu/v2/index.js +405 -0
  16. package/es/Menu/v2/props.js +26 -0
  17. package/es/Menu/v2/styles.js +61 -0
  18. package/es/{index.js → exports/a.js} +1 -1
  19. package/es/exports/b.js +24 -0
  20. package/es/{MenuContext.js → utils/v1/MenuContext.js} +2 -0
  21. package/lib/Menu/{MenuItem → v1/MenuItem}/index.js +4 -4
  22. package/lib/Menu/{MenuItemGroup → v1/MenuItemGroup}/index.js +1 -1
  23. package/lib/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/index.js +1 -1
  24. package/lib/Menu/v1/index.js +432 -0
  25. package/lib/Menu/v2/MenuItem/index.js +271 -0
  26. package/lib/Menu/v2/MenuItem/props.js +31 -0
  27. package/lib/Menu/v2/MenuItem/styles.js +152 -0
  28. package/lib/Menu/v2/MenuItemGroup/index.js +183 -0
  29. package/lib/Menu/v2/MenuItemGroup/props.js +31 -0
  30. package/lib/Menu/v2/MenuItemGroup/styles.js +66 -0
  31. package/lib/Menu/v2/MenuItemSeparator/index.js +75 -0
  32. package/lib/Menu/v2/MenuItemSeparator/props.js +34 -0
  33. package/lib/Menu/v2/MenuItemSeparator/styles.js +52 -0
  34. package/lib/Menu/{index.js → v2/index.js} +4 -5
  35. package/lib/Menu/v2/props.js +31 -0
  36. package/lib/Menu/v2/styles.js +66 -0
  37. package/lib/{index.js → exports/a.js} +5 -5
  38. package/lib/exports/b.js +30 -0
  39. package/lib/{MenuContext.js → utils/v1/MenuContext.js} +1 -0
  40. package/package.json +45 -23
  41. package/src/Menu/{MenuItem → v1/MenuItem}/index.tsx +2 -2
  42. package/src/Menu/{MenuItemGroup → v1/MenuItemGroup}/index.tsx +1 -1
  43. package/src/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/index.tsx +1 -1
  44. package/src/Menu/{index.tsx → v1/index.tsx} +3 -3
  45. package/src/Menu/{props.ts → v1/props.ts} +1 -1
  46. package/src/Menu/v2/MenuItem/index.tsx +318 -0
  47. package/src/Menu/v2/MenuItem/props.ts +132 -0
  48. package/src/Menu/v2/MenuItem/styles.ts +172 -0
  49. package/src/Menu/v2/MenuItemGroup/index.tsx +245 -0
  50. package/src/Menu/v2/MenuItemGroup/props.ts +105 -0
  51. package/src/Menu/v2/MenuItemGroup/styles.ts +66 -0
  52. package/src/Menu/v2/MenuItemSeparator/index.tsx +79 -0
  53. package/src/Menu/v2/MenuItemSeparator/props.ts +48 -0
  54. package/src/Menu/v2/MenuItemSeparator/styles.ts +52 -0
  55. package/src/Menu/v2/README.md +104 -0
  56. package/src/Menu/v2/index.tsx +514 -0
  57. package/src/Menu/v2/props.ts +213 -0
  58. package/src/Menu/v2/styles.ts +68 -0
  59. package/src/{index.ts → exports/a.ts} +5 -5
  60. package/src/exports/b.ts +29 -0
  61. package/src/{MenuContext.ts → utils/v1/MenuContext.ts} +3 -3
  62. package/tsconfig.build.tsbuildinfo +1 -1
  63. package/types/Menu/{MenuItem → v1/MenuItem}/index.d.ts +3 -3
  64. package/types/Menu/v1/MenuItem/index.d.ts.map +1 -0
  65. package/types/Menu/v1/MenuItem/props.d.ts.map +1 -0
  66. package/types/Menu/v1/MenuItem/styles.d.ts.map +1 -0
  67. package/types/Menu/v1/MenuItem/theme.d.ts.map +1 -0
  68. package/types/Menu/v1/MenuItemGroup/index.d.ts.map +1 -0
  69. package/types/Menu/v1/MenuItemGroup/props.d.ts.map +1 -0
  70. package/types/Menu/v1/MenuItemGroup/styles.d.ts.map +1 -0
  71. package/types/Menu/v1/MenuItemGroup/theme.d.ts.map +1 -0
  72. package/types/Menu/v1/MenuItemSeparator/index.d.ts.map +1 -0
  73. package/types/Menu/v1/MenuItemSeparator/props.d.ts.map +1 -0
  74. package/types/Menu/v1/MenuItemSeparator/styles.d.ts.map +1 -0
  75. package/types/Menu/v1/MenuItemSeparator/theme.d.ts.map +1 -0
  76. package/types/Menu/{index.d.ts → v1/index.d.ts} +3 -3
  77. package/types/Menu/v1/index.d.ts.map +1 -0
  78. package/types/Menu/{props.d.ts → v1/props.d.ts} +1 -1
  79. package/types/Menu/v1/props.d.ts.map +1 -0
  80. package/types/Menu/v1/styles.d.ts.map +1 -0
  81. package/types/Menu/v1/theme.d.ts.map +1 -0
  82. package/types/Menu/v2/MenuItem/index.d.ts +66 -0
  83. package/types/Menu/v2/MenuItem/index.d.ts.map +1 -0
  84. package/types/Menu/v2/MenuItem/props.d.ts +73 -0
  85. package/types/Menu/v2/MenuItem/props.d.ts.map +1 -0
  86. package/types/Menu/v2/MenuItem/styles.d.ts +15 -0
  87. package/types/Menu/v2/MenuItem/styles.d.ts.map +1 -0
  88. package/types/Menu/v2/MenuItemGroup/index.d.ts +52 -0
  89. package/types/Menu/v2/MenuItemGroup/index.d.ts.map +1 -0
  90. package/types/Menu/v2/MenuItemGroup/props.d.ts +51 -0
  91. package/types/Menu/v2/MenuItemGroup/props.d.ts.map +1 -0
  92. package/types/Menu/v2/MenuItemGroup/styles.d.ts +15 -0
  93. package/types/Menu/v2/MenuItemGroup/styles.d.ts.map +1 -0
  94. package/types/Menu/v2/MenuItemSeparator/index.d.ts +21 -0
  95. package/types/Menu/v2/MenuItemSeparator/index.d.ts.map +1 -0
  96. package/types/Menu/v2/MenuItemSeparator/props.d.ts +11 -0
  97. package/types/Menu/v2/MenuItemSeparator/props.d.ts.map +1 -0
  98. package/types/Menu/v2/MenuItemSeparator/styles.d.ts +15 -0
  99. package/types/Menu/v2/MenuItemSeparator/styles.d.ts.map +1 -0
  100. package/types/Menu/v2/index.d.ts +118 -0
  101. package/types/Menu/v2/index.d.ts.map +1 -0
  102. package/types/Menu/v2/props.d.ts +138 -0
  103. package/types/Menu/v2/props.d.ts.map +1 -0
  104. package/types/Menu/v2/styles.d.ts +15 -0
  105. package/types/Menu/v2/styles.d.ts.map +1 -0
  106. package/types/exports/a.d.ts +6 -0
  107. package/types/exports/a.d.ts.map +1 -0
  108. package/types/exports/b.d.ts +6 -0
  109. package/types/exports/b.d.ts.map +1 -0
  110. package/types/utils/v1/MenuContext.d.ts +13 -0
  111. package/types/utils/v1/MenuContext.d.ts.map +1 -0
  112. package/types/Menu/MenuItem/index.d.ts.map +0 -1
  113. package/types/Menu/MenuItem/props.d.ts.map +0 -1
  114. package/types/Menu/MenuItem/styles.d.ts.map +0 -1
  115. package/types/Menu/MenuItem/theme.d.ts.map +0 -1
  116. package/types/Menu/MenuItemGroup/index.d.ts.map +0 -1
  117. package/types/Menu/MenuItemGroup/props.d.ts.map +0 -1
  118. package/types/Menu/MenuItemGroup/styles.d.ts.map +0 -1
  119. package/types/Menu/MenuItemGroup/theme.d.ts.map +0 -1
  120. package/types/Menu/MenuItemSeparator/index.d.ts.map +0 -1
  121. package/types/Menu/MenuItemSeparator/props.d.ts.map +0 -1
  122. package/types/Menu/MenuItemSeparator/styles.d.ts.map +0 -1
  123. package/types/Menu/MenuItemSeparator/theme.d.ts.map +0 -1
  124. package/types/Menu/index.d.ts.map +0 -1
  125. package/types/Menu/props.d.ts.map +0 -1
  126. package/types/Menu/styles.d.ts.map +0 -1
  127. package/types/Menu/theme.d.ts.map +0 -1
  128. package/types/MenuContext.d.ts +0 -14
  129. package/types/MenuContext.d.ts.map +0 -1
  130. package/types/index.d.ts +0 -6
  131. package/types/index.d.ts.map +0 -1
  132. /package/es/Menu/{MenuItem → v1/MenuItem}/props.js +0 -0
  133. /package/es/Menu/{MenuItem → v1/MenuItem}/styles.js +0 -0
  134. /package/es/Menu/{MenuItem → v1/MenuItem}/theme.js +0 -0
  135. /package/es/Menu/{MenuItemGroup → v1/MenuItemGroup}/props.js +0 -0
  136. /package/es/Menu/{MenuItemGroup → v1/MenuItemGroup}/styles.js +0 -0
  137. /package/es/Menu/{MenuItemGroup → v1/MenuItemGroup}/theme.js +0 -0
  138. /package/es/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/props.js +0 -0
  139. /package/es/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/styles.js +0 -0
  140. /package/es/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/theme.js +0 -0
  141. /package/es/Menu/{props.js → v1/props.js} +0 -0
  142. /package/es/Menu/{styles.js → v1/styles.js} +0 -0
  143. /package/es/Menu/{theme.js → v1/theme.js} +0 -0
  144. /package/lib/Menu/{MenuItem → v1/MenuItem}/props.js +0 -0
  145. /package/lib/Menu/{MenuItem → v1/MenuItem}/styles.js +0 -0
  146. /package/lib/Menu/{MenuItem → v1/MenuItem}/theme.js +0 -0
  147. /package/lib/Menu/{MenuItemGroup → v1/MenuItemGroup}/props.js +0 -0
  148. /package/lib/Menu/{MenuItemGroup → v1/MenuItemGroup}/styles.js +0 -0
  149. /package/lib/Menu/{MenuItemGroup → v1/MenuItemGroup}/theme.js +0 -0
  150. /package/lib/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/props.js +0 -0
  151. /package/lib/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/styles.js +0 -0
  152. /package/lib/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/theme.js +0 -0
  153. /package/lib/Menu/{props.js → v1/props.js} +0 -0
  154. /package/lib/Menu/{styles.js → v1/styles.js} +0 -0
  155. /package/lib/Menu/{theme.js → v1/theme.js} +0 -0
  156. /package/src/Menu/{MenuItem → v1/MenuItem}/props.ts +0 -0
  157. /package/src/Menu/{MenuItem → v1/MenuItem}/styles.ts +0 -0
  158. /package/src/Menu/{MenuItem → v1/MenuItem}/theme.ts +0 -0
  159. /package/src/Menu/{MenuItemGroup → v1/MenuItemGroup}/props.ts +0 -0
  160. /package/src/Menu/{MenuItemGroup → v1/MenuItemGroup}/styles.ts +0 -0
  161. /package/src/Menu/{MenuItemGroup → v1/MenuItemGroup}/theme.ts +0 -0
  162. /package/src/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/props.ts +0 -0
  163. /package/src/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/styles.ts +0 -0
  164. /package/src/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/theme.ts +0 -0
  165. /package/src/Menu/{README.md → v1/README.md} +0 -0
  166. /package/src/Menu/{styles.ts → v1/styles.ts} +0 -0
  167. /package/src/Menu/{theme.ts → v1/theme.ts} +0 -0
  168. /package/types/Menu/{MenuItem → v1/MenuItem}/props.d.ts +0 -0
  169. /package/types/Menu/{MenuItem → v1/MenuItem}/styles.d.ts +0 -0
  170. /package/types/Menu/{MenuItem → v1/MenuItem}/theme.d.ts +0 -0
  171. /package/types/Menu/{MenuItemGroup → v1/MenuItemGroup}/index.d.ts +0 -0
  172. /package/types/Menu/{MenuItemGroup → v1/MenuItemGroup}/props.d.ts +0 -0
  173. /package/types/Menu/{MenuItemGroup → v1/MenuItemGroup}/styles.d.ts +0 -0
  174. /package/types/Menu/{MenuItemGroup → v1/MenuItemGroup}/theme.d.ts +0 -0
  175. /package/types/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/index.d.ts +0 -0
  176. /package/types/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/props.d.ts +0 -0
  177. /package/types/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/styles.d.ts +0 -0
  178. /package/types/Menu/{MenuItemSeparator → v1/MenuItemSeparator}/theme.d.ts +0 -0
  179. /package/types/Menu/{styles.d.ts → v1/styles.d.ts} +0 -0
  180. /package/types/Menu/{theme.d.ts → v1/theme.d.ts} +0 -0
@@ -0,0 +1,318 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import { Component } from 'react'
26
+ import keycode from 'keycode'
27
+
28
+ import { CheckInstUIIcon, ChevronRightInstUIIcon } from '@instructure/ui-icons'
29
+ import {
30
+ omitProps,
31
+ getElementType,
32
+ withDeterministicId,
33
+ callRenderProp
34
+ } from '@instructure/ui-react-utils'
35
+ import { createChainedFunction } from '@instructure/ui-utils'
36
+ import { isActiveElement, findDOMNode } from '@instructure/ui-dom-utils'
37
+ import { withStyle } from '@instructure/emotion'
38
+
39
+ import { MenuContext } from '../../../utils/v1/MenuContext'
40
+
41
+ import generateStyle from './styles'
42
+
43
+ import { allowedProps } from './props'
44
+ import type { MenuItemProps, MenuItemState } from './props'
45
+
46
+ /**
47
+ ---
48
+ parent: Menu
49
+ id: Menu.Item
50
+ ---
51
+ **/
52
+ @withDeterministicId()
53
+ @withStyle(generateStyle)
54
+ class MenuItem extends Component<MenuItemProps, MenuItemState> {
55
+ static readonly componentId = 'Menu.Item'
56
+
57
+ static allowedProps = allowedProps
58
+ static defaultProps = {
59
+ type: 'button',
60
+ disabled: false
61
+ } as const
62
+
63
+ declare context: React.ContextType<typeof MenuContext>
64
+ static contextType = MenuContext
65
+
66
+ constructor(props: MenuItemProps) {
67
+ super(props)
68
+
69
+ const state: MenuItemState = {
70
+ isHovered: false,
71
+ isFocused: false
72
+ }
73
+
74
+ if (typeof props.selected === 'undefined') {
75
+ state.selected = !!props.defaultSelected
76
+ }
77
+
78
+ this.state = state
79
+ this.labelId = props.deterministicId!('MenuItem__label')
80
+ }
81
+
82
+ get _node() {
83
+ console.warn(
84
+ '_node property is deprecated and will be removed in v9, please use ref instead'
85
+ )
86
+
87
+ return this.ref
88
+ }
89
+
90
+ labelId: string
91
+ ref: Element | null = null
92
+
93
+ handleRef = (el: Element | null) => {
94
+ this.ref = el
95
+ }
96
+
97
+ componentDidMount() {
98
+ this.props.makeStyles?.()
99
+ const context = this.context
100
+
101
+ if (context && context.registerMenuItem) {
102
+ context.registerMenuItem(this)
103
+ }
104
+ }
105
+
106
+ componentDidUpdate() {
107
+ this.props.makeStyles?.()
108
+ }
109
+
110
+ componentWillUnmount() {
111
+ const context = this.context
112
+
113
+ if (context && context.removeMenuItem) {
114
+ context.removeMenuItem(this)
115
+ }
116
+ }
117
+
118
+ handleClick = (e: React.MouseEvent) => {
119
+ const { onSelect, onClick, disabled, value } = this.props
120
+ const selected = !this.selected
121
+
122
+ if (disabled) {
123
+ e.preventDefault()
124
+ return
125
+ }
126
+
127
+ if (typeof this.props.selected === 'undefined') {
128
+ this.setState({ selected })
129
+ }
130
+
131
+ if (typeof onSelect === 'function') {
132
+ e.persist()
133
+ onSelect(e, value, selected, this)
134
+ }
135
+
136
+ if (typeof onClick === 'function') {
137
+ onClick(e)
138
+ }
139
+ }
140
+
141
+ handleKeyDown = (e: React.KeyboardEvent) => {
142
+ const spaceKey = e.keyCode === keycode.codes.space
143
+ const enterKey = e.keyCode === keycode.codes.enter
144
+
145
+ if (spaceKey || enterKey) {
146
+ e.preventDefault()
147
+ e.stopPropagation()
148
+
149
+ if (enterKey) {
150
+ // handle space key on keyUp for FF
151
+ const refNode = findDOMNode(this.ref) as HTMLElement
152
+ refNode.click()
153
+ }
154
+ }
155
+ }
156
+
157
+ handleKeyUp = (e: React.KeyboardEvent) => {
158
+ const spaceKey = e.keyCode === keycode.codes.space
159
+ const enterKey = e.keyCode === keycode.codes.enter
160
+
161
+ if (spaceKey || enterKey) {
162
+ e.preventDefault()
163
+ e.stopPropagation()
164
+
165
+ if (spaceKey) {
166
+ const refNode = findDOMNode(this.ref) as HTMLElement
167
+ refNode.click()
168
+ }
169
+ }
170
+ }
171
+
172
+ handleMouseOver = (event: React.MouseEvent) => {
173
+ if (!this.focused) {
174
+ this.focus()
175
+ }
176
+
177
+ if (typeof this.props.onMouseOver === 'function') {
178
+ this.props.onMouseOver(event, this)
179
+ }
180
+ }
181
+
182
+ handleMouseEnter = () => {
183
+ this.setState({ isHovered: true })
184
+ }
185
+
186
+ handleMouseLeave = () => {
187
+ this.setState({ isHovered: false })
188
+ }
189
+
190
+ handleFocusEvent = () => {
191
+ this.setState({ isFocused: true })
192
+ }
193
+
194
+ handleBlurEvent = () => {
195
+ this.setState({ isFocused: false })
196
+ }
197
+
198
+ getIconColor = () => {
199
+ const { type } = this.props
200
+
201
+ if (type === 'flyout') {
202
+ return 'baseColor'
203
+ }
204
+ return this.selected ? 'inverseColor' : 'baseColor'
205
+ }
206
+
207
+ get elementType() {
208
+ return getElementType(MenuItem, this.props)
209
+ }
210
+
211
+ get role() {
212
+ switch (this.props.type) {
213
+ case 'checkbox':
214
+ return 'menuitemcheckbox'
215
+ case 'radio':
216
+ return 'menuitemradio'
217
+ default:
218
+ return 'menuitem'
219
+ }
220
+ }
221
+
222
+ get selected() {
223
+ return typeof this.props.selected === 'undefined'
224
+ ? this.state.selected
225
+ : this.props.selected
226
+ }
227
+
228
+ get focused() {
229
+ return isActiveElement(this.ref)
230
+ }
231
+
232
+ focus() {
233
+ const refNode = findDOMNode(this.ref) as HTMLElement
234
+ refNode.focus()
235
+ }
236
+
237
+ renderContent() {
238
+ const { children, type, renderLabelInfo } = this.props
239
+
240
+ return (
241
+ <span>
242
+ <span css={this.props.styles?.label} id={this.labelId}>
243
+ {children}
244
+ </span>
245
+ {(type === 'checkbox' || type === 'radio') && (
246
+ <span css={this.props.styles?.icon}>
247
+ {this.selected && (
248
+ <CheckInstUIIcon size="md" color={this.getIconColor()} />
249
+ )}
250
+ </span>
251
+ )}
252
+ {type === 'flyout' && (
253
+ <span css={this.props.styles?.icon}>
254
+ <ChevronRightInstUIIcon size="md" color={this.getIconColor()} />
255
+ </span>
256
+ )}
257
+ {renderLabelInfo && (
258
+ <span css={this.props.styles?.labelInfo}>
259
+ {callRenderProp(renderLabelInfo)}
260
+ </span>
261
+ )}
262
+ </span>
263
+ )
264
+ }
265
+
266
+ render() {
267
+ const {
268
+ disabled,
269
+ controls,
270
+ onKeyDown,
271
+ onKeyUp,
272
+ onFocus,
273
+ onBlur,
274
+ type,
275
+ href,
276
+ target
277
+ } = this.props
278
+
279
+ const props = omitProps(this.props, MenuItem.allowedProps)
280
+ const ElementType = this.elementType
281
+
282
+ return (
283
+ <ElementType
284
+ tabIndex={-1} // note: tabIndex can be overridden by Menu or MenuItemGroup components
285
+ {...props}
286
+ href={href}
287
+ target={target}
288
+ role={this.role}
289
+ aria-labelledby={this.labelId}
290
+ aria-disabled={disabled ? 'true' : undefined}
291
+ aria-controls={controls}
292
+ aria-checked={
293
+ type === 'checkbox' || type === 'radio'
294
+ ? this.selected
295
+ ? 'true'
296
+ : 'false'
297
+ : undefined
298
+ }
299
+ onClick={this.handleClick}
300
+ onKeyUp={createChainedFunction(onKeyUp, this.handleKeyUp)}
301
+ onKeyDown={createChainedFunction(onKeyDown, this.handleKeyDown)}
302
+ ref={this.handleRef}
303
+ css={this.props.styles?.menuItem}
304
+ onMouseOver={this.handleMouseOver}
305
+ onMouseEnter={this.handleMouseEnter}
306
+ onMouseLeave={this.handleMouseLeave}
307
+ onFocus={createChainedFunction(onFocus, this.handleFocusEvent)}
308
+ onBlur={createChainedFunction(onBlur, this.handleBlurEvent)}
309
+ data-cid="MenuItem"
310
+ >
311
+ {this.renderContent()}
312
+ </ElementType>
313
+ )
314
+ }
315
+ }
316
+
317
+ export default MenuItem
318
+ export { MenuItem }
@@ -0,0 +1,132 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import React from 'react'
26
+ import MenuItem from '../MenuItem'
27
+
28
+ import type {
29
+ AsElementType,
30
+ MenuItemTheme,
31
+ OtherHTMLAttributes
32
+ } from '@instructure/shared-types'
33
+ import type { WithStyleProps, ComponentStyle } from '@instructure/emotion'
34
+ import type { WithDeterministicIdProps } from '@instructure/ui-react-utils'
35
+
36
+ type OnMenuItemSelect = (
37
+ e: React.MouseEvent,
38
+ value: MenuItemProps['value'],
39
+ selected: MenuItemProps['selected'],
40
+ args: MenuItem
41
+ ) => void
42
+
43
+ type MenuItemOwnProps = {
44
+ /**
45
+ * the menu item label
46
+ */
47
+ children: React.ReactNode
48
+ /**
49
+ * whether to set the menu item state to selected or not on initial render
50
+ */
51
+ defaultSelected?: boolean
52
+ /**
53
+ * whether the menu item is selected or not (must be accompanied by an `onSelect` prop)
54
+ */
55
+ selected?: boolean // TODO: controllable(PropTypes.bool, 'onSelect', 'defaultSelected')
56
+ /**
57
+ * when used with the `selected` prop, the component will not control its own state
58
+ */
59
+ onSelect?: OnMenuItemSelect
60
+ onClick?: (e: React.MouseEvent) => void
61
+ onKeyDown?: (e: React.KeyboardEvent) => void
62
+ onKeyUp?: (e: React.KeyboardEvent) => void
63
+ onMouseOver?: (e: React.MouseEvent, args: MenuItem) => void
64
+ /**
65
+ * the id of the element that the menu item will act upon
66
+ */
67
+ controls?: string
68
+ disabled?: boolean
69
+ /**
70
+ * the element type to render as (will default to `<a>` if href is provided)
71
+ */
72
+ as?: AsElementType
73
+ /**
74
+ * How this component should be rendered. If it's `checkbox` or `radio` it will
75
+ * display a checkmark based on its own 'selected' state, if it's `flyout` it will
76
+ * render an arrow after the label.
77
+ */
78
+ type?: 'button' | 'checkbox' | 'radio' | 'flyout'
79
+ /**
80
+ * Arbitrary value that you can store in this component. Is sent out by the
81
+ * `onSelect` event
82
+ */
83
+ value?: string | number
84
+ /**
85
+ * Value of the `href` prop that will be put on the underlying DOM element.
86
+ */
87
+ href?: string
88
+ /**
89
+ * Where to display the linked URL, as the name for a browsing context (a tab, window, or <iframe>).
90
+ */
91
+ target?: string
92
+ /**
93
+ * Content to render in the label's info region
94
+ */
95
+ renderLabelInfo?: React.ReactNode | (() => React.ReactNode)
96
+ }
97
+
98
+ type PropKeys = keyof MenuItemOwnProps
99
+
100
+ type AllowedPropKeys = Readonly<Array<PropKeys>>
101
+
102
+ type MenuItemProps = MenuItemOwnProps &
103
+ WithStyleProps<MenuItemTheme, MenuItemStyle> &
104
+ OtherHTMLAttributes<MenuItemOwnProps> &
105
+ WithDeterministicIdProps
106
+
107
+ type MenuItemStyle = ComponentStyle<'menuItem' | 'icon' | 'labelInfo' | 'label'>
108
+ const allowedProps: AllowedPropKeys = [
109
+ 'children',
110
+ 'defaultSelected',
111
+ 'selected',
112
+ 'onSelect',
113
+ 'onClick',
114
+ 'onKeyDown',
115
+ 'onKeyUp',
116
+ 'onMouseOver',
117
+ 'controls',
118
+ 'disabled',
119
+ 'as',
120
+ 'type',
121
+ 'value',
122
+ 'href',
123
+ 'target',
124
+ 'renderLabelInfo'
125
+ ]
126
+ type MenuItemState = {
127
+ selected?: boolean
128
+ isHovered: boolean
129
+ isFocused: boolean
130
+ }
131
+ export type { MenuItemProps, MenuItemStyle, MenuItemState, OnMenuItemSelect }
132
+ export { allowedProps }
@@ -0,0 +1,172 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import type { NewComponentTypes } from '@instructure/ui-themes'
26
+ import type { MenuItemProps, MenuItemStyle } from './props'
27
+
28
+ /**
29
+ * ---
30
+ * private: true
31
+ * ---
32
+ * Generates the style object from the theme and provided additional information
33
+ * @param {Object} componentTheme The theme variable object.
34
+ * @param {Object} props the props of the component, the style is applied to
35
+ * @param {Object} state the state of the component, the style is applied to
36
+ * @return {Object} The final style object, which will be used in the component
37
+ */
38
+ const generateStyle = (
39
+ componentTheme: NewComponentTypes['MenuItem'],
40
+ props: MenuItemProps
41
+ ): MenuItemStyle => {
42
+ const { type, disabled, selected } = props
43
+
44
+ const isRadioOrCheckbox = type === 'checkbox' || type === 'radio'
45
+
46
+ const flyoutIconStyles =
47
+ type === 'flyout'
48
+ ? {
49
+ insetInlineStart: 'auto',
50
+ insetInlineEnd: componentTheme.iconPadding
51
+ }
52
+ : {}
53
+
54
+ const roleStyles = isRadioOrCheckbox
55
+ ? {
56
+ paddingInlineEnd: componentTheme.labelPadding
57
+ }
58
+ : {}
59
+
60
+ const roleIconStyles = isRadioOrCheckbox
61
+ ? {
62
+ insetInlineStart: 'auto',
63
+ insetInlineEnd: componentTheme.iconPadding
64
+ }
65
+ : {}
66
+
67
+ const disabledStyles = disabled
68
+ ? {
69
+ cursor: 'not-allowed',
70
+ pointerEvents: 'none',
71
+ opacity: 0.5
72
+ }
73
+ : {}
74
+
75
+ const selectedStyles = selected
76
+ ? {
77
+ background: componentTheme.activeBackground,
78
+ '[class*="menuItem__label"]': {
79
+ color: componentTheme.activeLabelColor
80
+ }
81
+ }
82
+ : {}
83
+
84
+ const selectedHighlightedStyles = selected
85
+ ? {
86
+ background: componentTheme.selectedHighlightedBackground
87
+ }
88
+ : {
89
+ background: componentTheme.highlightedBackground,
90
+ '[class*="menuItem__label"]': {
91
+ color: componentTheme.highlightedLabelColor
92
+ },
93
+ '[class*="menuItem__labelInfo"]': {
94
+ color: componentTheme.highlightedLabelInfoColor
95
+ }
96
+ }
97
+
98
+ const linkStyles = { textDecoration: 'none' }
99
+
100
+ return {
101
+ menuItem: {
102
+ label: 'menuItem',
103
+ position: 'relative',
104
+ border: 'none',
105
+ outline: 'none',
106
+ padding: `${componentTheme.paddingVertical} ${componentTheme.paddingHorizontal}`,
107
+ margin: '0',
108
+ width: '100%',
109
+ borderRadius: 'initial',
110
+ boxSizing: 'border-box',
111
+ textAlign: 'start',
112
+ cursor: 'pointer',
113
+ userSelect: 'none',
114
+ fontFamily: componentTheme.fontFamily,
115
+ fontWeight: componentTheme.fontWeight,
116
+ lineHeight: componentTheme.lineHeight,
117
+ fontSize: componentTheme.fontSize,
118
+ background: componentTheme.background,
119
+ // Changing the following to display: flex; causes a VO
120
+ // bug where items with role menuitem, menuitemcheckbox
121
+ // and menuitemradio are selected twice with control+
122
+ // option+space. So we set the display to block.
123
+ display: 'block',
124
+ textDecoration: 'none',
125
+ ...roleStyles,
126
+ '&:focus, &:active, &:hover': {
127
+ ...selectedHighlightedStyles
128
+ },
129
+ //removes extra ff button spacing
130
+ '&::-moz-focus-inner': {
131
+ padding: '0',
132
+ margin: '0',
133
+ border: '0'
134
+ },
135
+ ...disabledStyles,
136
+ ...selectedStyles,
137
+
138
+ // NOTE: needs separate groups for `:is()` and `:-webkit-any()` because of css selector group validation (see https://www.w3.org/TR/selectors-3/#grouping)
139
+ '&:is(a)': {
140
+ '&, &:link, &:visited, &:active, &:hover, &:focus': linkStyles
141
+ },
142
+ '&:-webkit-any(a)': {
143
+ '&, &:link, &:visited, &:active, &:hover, &:focus': linkStyles
144
+ }
145
+ },
146
+ icon: {
147
+ label: 'menuItem__icon',
148
+ position: 'absolute',
149
+ display: 'flex',
150
+ alignItems: 'center',
151
+ top: '0',
152
+ width: '1em',
153
+ height: '100%',
154
+ ...roleIconStyles,
155
+ ...flyoutIconStyles
156
+ },
157
+ labelInfo: {
158
+ label: 'menuItem__labelInfo',
159
+ height: '100%',
160
+ float: 'right',
161
+ clear: 'right',
162
+ paddingRight: '1.75rem',
163
+ color: componentTheme.labelInfoColor
164
+ },
165
+ label: {
166
+ label: 'menuItem__label',
167
+ color: componentTheme.labelColor
168
+ }
169
+ }
170
+ }
171
+
172
+ export default generateStyle