@instructure/ui-navigation 8.27.1-snapshot-7 → 8.27.1-snapshot-13
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.
- package/CHANGELOG.md +1 -1
- package/es/AppNav/index.js +17 -118
- package/es/AppNav/styles.js +4 -23
- package/lib/AppNav/index.js +19 -122
- package/lib/AppNav/styles.js +4 -23
- package/package.json +24 -23
- package/src/AppNav/index.tsx +39 -132
- package/src/AppNav/props.ts +4 -8
- package/src/AppNav/styles.ts +4 -23
- package/tsconfig.build.json +1 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/AppNav/index.d.ts +3 -13
- package/types/AppNav/index.d.ts.map +1 -1
- package/types/AppNav/props.d.ts +4 -1
- package/types/AppNav/props.d.ts.map +1 -1
- package/types/AppNav/styles.d.ts.map +1 -1
package/src/AppNav/index.tsx
CHANGED
|
@@ -23,15 +23,11 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
/** @jsx jsx */
|
|
26
|
-
import {
|
|
26
|
+
import React, { Component } from 'react'
|
|
27
27
|
|
|
28
28
|
import { withStyle, jsx } from '@instructure/emotion'
|
|
29
29
|
|
|
30
|
-
import { getBoundingClientRect } from '@instructure/ui-dom-utils'
|
|
31
30
|
import { callRenderProp, omitProps } from '@instructure/ui-react-utils'
|
|
32
|
-
import { px } from '@instructure/ui-utils'
|
|
33
|
-
import { debounce } from '@instructure/debounce'
|
|
34
|
-
import type { Debounced } from '@instructure/debounce'
|
|
35
31
|
import { testable } from '@instructure/ui-testable'
|
|
36
32
|
|
|
37
33
|
import { View } from '@instructure/ui-view'
|
|
@@ -44,6 +40,8 @@ import type { AppNavProps } from './props'
|
|
|
44
40
|
import { allowedProps, propTypes } from './props'
|
|
45
41
|
import { AppNavItemProps } from './Item/props'
|
|
46
42
|
|
|
43
|
+
import { TruncateList } from '@instructure/ui-truncate-list'
|
|
44
|
+
|
|
47
45
|
/**
|
|
48
46
|
---
|
|
49
47
|
category: components
|
|
@@ -73,88 +71,15 @@ class AppNav extends Component<AppNavProps> {
|
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
ref: Element | null = null
|
|
76
|
-
_list: HTMLUListElement | null = null
|
|
77
|
-
_debounced?: Debounced
|
|
78
|
-
_resizeListener?: ResizeObserver
|
|
79
74
|
|
|
80
75
|
componentDidMount() {
|
|
81
76
|
this.props.makeStyles?.()
|
|
82
|
-
const { width: origWidth } = getBoundingClientRect(this._list)
|
|
83
|
-
|
|
84
|
-
this._debounced = debounce(this.handleResize, this.props.debounce, {
|
|
85
|
-
leading: true,
|
|
86
|
-
trailing: true
|
|
87
|
-
})
|
|
88
|
-
this._resizeListener = new ResizeObserver((entries) => {
|
|
89
|
-
for (const entry of entries) {
|
|
90
|
-
const { width } = entry.contentRect
|
|
91
|
-
|
|
92
|
-
if (origWidth !== width) {
|
|
93
|
-
this._debounced!()
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
this._resizeListener.observe(this._list!)
|
|
99
|
-
|
|
100
|
-
this.handleResize()
|
|
101
77
|
}
|
|
102
78
|
|
|
103
79
|
componentDidUpdate() {
|
|
104
80
|
this.props.makeStyles?.()
|
|
105
81
|
}
|
|
106
82
|
|
|
107
|
-
componentWillUnmount() {
|
|
108
|
-
if (this._resizeListener) {
|
|
109
|
-
this._resizeListener.disconnect()
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (this._debounced) {
|
|
113
|
-
this._debounced.cancel()
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
measureItems = () => {
|
|
118
|
-
const menuTriggerWidth = px(this.props.styles?.menuTriggerWidth as number)
|
|
119
|
-
let visibleItemsCount = 0
|
|
120
|
-
|
|
121
|
-
if (this._list) {
|
|
122
|
-
const { width: navWidth } = getBoundingClientRect(this._list)
|
|
123
|
-
|
|
124
|
-
const itemWidths = Array.from(this._list.getElementsByTagName('li')).map(
|
|
125
|
-
(item) => {
|
|
126
|
-
const { width } = getBoundingClientRect(item)
|
|
127
|
-
return width
|
|
128
|
-
}
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
let currentWidth = 0
|
|
132
|
-
|
|
133
|
-
for (let i = 0; i < itemWidths.length; i++) {
|
|
134
|
-
currentWidth += itemWidths[i]
|
|
135
|
-
|
|
136
|
-
if (currentWidth <= navWidth - menuTriggerWidth) {
|
|
137
|
-
visibleItemsCount++
|
|
138
|
-
} else {
|
|
139
|
-
break
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return { visibleItemsCount }
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
handleResize = () => {
|
|
148
|
-
this.setState({ isMeasuring: true }, () => {
|
|
149
|
-
const { visibleItemsCount } = this.measureItems()
|
|
150
|
-
|
|
151
|
-
if (typeof this.props.onUpdate === 'function') {
|
|
152
|
-
this.props.onUpdate({ visibleItemsCount })
|
|
153
|
-
}
|
|
154
|
-
this.setState({ isMeasuring: false })
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
|
|
158
83
|
handleRef = (el: Element | null) => {
|
|
159
84
|
const { elementRef } = this.props
|
|
160
85
|
|
|
@@ -165,22 +90,8 @@ class AppNav extends Component<AppNavProps> {
|
|
|
165
90
|
}
|
|
166
91
|
}
|
|
167
92
|
|
|
168
|
-
renderListItem(item: React.ReactNode, isMenuTrigger: boolean, key?: number) {
|
|
169
|
-
return (
|
|
170
|
-
<li
|
|
171
|
-
key={key}
|
|
172
|
-
css={
|
|
173
|
-
isMenuTrigger
|
|
174
|
-
? this.props.styles?.listItemWithMenuTrigger
|
|
175
|
-
: this.props.styles?.listItem
|
|
176
|
-
}
|
|
177
|
-
>
|
|
178
|
-
{item}
|
|
179
|
-
</li>
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
93
|
renderMenu(items: React.ComponentElement<AppNavItemProps, Item>[]) {
|
|
183
|
-
|
|
94
|
+
return (
|
|
184
95
|
<Menu
|
|
185
96
|
trigger={
|
|
186
97
|
<AppNav.Item
|
|
@@ -188,29 +99,29 @@ class AppNav extends Component<AppNavProps> {
|
|
|
188
99
|
/>
|
|
189
100
|
}
|
|
190
101
|
>
|
|
191
|
-
{items.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
102
|
+
{(items as React.ComponentElement<AppNavItemProps, Item>[]).map(
|
|
103
|
+
(item, index) => {
|
|
104
|
+
return (
|
|
105
|
+
<Menu.Item
|
|
106
|
+
href={item.props.href ? item.props.href : undefined}
|
|
107
|
+
onClick={
|
|
108
|
+
item.props.onClick && !item.props.href
|
|
109
|
+
? item.props.onClick
|
|
110
|
+
: undefined
|
|
111
|
+
}
|
|
112
|
+
key={index}
|
|
113
|
+
>
|
|
114
|
+
{callRenderProp(item.props.renderLabel)}
|
|
115
|
+
</Menu.Item>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
)}
|
|
206
119
|
</Menu>
|
|
207
120
|
)
|
|
208
|
-
|
|
209
|
-
return this.renderListItem(menu, true)
|
|
210
121
|
}
|
|
211
122
|
|
|
212
123
|
render() {
|
|
213
|
-
const {
|
|
124
|
+
const { visibleItemsCount, screenReaderLabel, margin, debounce, styles } =
|
|
214
125
|
this.props
|
|
215
126
|
|
|
216
127
|
const passthroughProps = View.omitViewProps(
|
|
@@ -218,15 +129,6 @@ class AppNav extends Component<AppNavProps> {
|
|
|
218
129
|
AppNav
|
|
219
130
|
)
|
|
220
131
|
|
|
221
|
-
const { isMeasuring } = this.state
|
|
222
|
-
const childrenArray = Children.toArray(children) as React.ComponentElement<
|
|
223
|
-
AppNavItemProps,
|
|
224
|
-
Item
|
|
225
|
-
>[]
|
|
226
|
-
const visibleChildren = isMeasuring
|
|
227
|
-
? childrenArray
|
|
228
|
-
: childrenArray.splice(0, visibleItemsCount)
|
|
229
|
-
const hiddenChildren = isMeasuring ? [] : childrenArray
|
|
230
132
|
const renderBeforeItems = callRenderProp(this.props.renderBeforeItems)
|
|
231
133
|
const renderAfterItems = callRenderProp(this.props.renderAfterItems)
|
|
232
134
|
const hasRenderedContent = renderBeforeItems || renderAfterItems
|
|
@@ -235,25 +137,30 @@ class AppNav extends Component<AppNavProps> {
|
|
|
235
137
|
<View
|
|
236
138
|
{...passthroughProps}
|
|
237
139
|
as="nav"
|
|
238
|
-
css={[
|
|
239
|
-
this.props.styles?.appNav,
|
|
240
|
-
hasRenderedContent ? this.props.styles?.alignCenter : ''
|
|
241
|
-
]}
|
|
140
|
+
css={[styles?.appNav, hasRenderedContent ? styles?.alignCenter : '']}
|
|
242
141
|
margin={margin}
|
|
243
142
|
display={hasRenderedContent ? 'flex' : 'block'}
|
|
244
143
|
elementRef={this.handleRef}
|
|
245
144
|
>
|
|
246
145
|
{renderBeforeItems && <span>{renderBeforeItems}</span>}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
146
|
+
|
|
147
|
+
<TruncateList
|
|
148
|
+
visibleItemsCount={visibleItemsCount}
|
|
149
|
+
debounce={debounce}
|
|
150
|
+
onUpdate={this.props.onUpdate}
|
|
151
|
+
renderHiddenItemMenu={(hiddenChildren) =>
|
|
152
|
+
this.renderMenu(
|
|
153
|
+
hiddenChildren as React.ComponentElement<AppNavItemProps, Item>[]
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
itemSpacing={styles?.horizontalMargin}
|
|
157
|
+
fixMenuTriggerWidth={styles?.menuTriggerWidth}
|
|
158
|
+
css={styles?.list}
|
|
250
159
|
aria-label={callRenderProp(screenReaderLabel)}
|
|
251
160
|
>
|
|
252
|
-
{
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
{hiddenChildren.length > 0 && this.renderMenu(hiddenChildren)}
|
|
256
|
-
</ul>
|
|
161
|
+
{this.props.children}
|
|
162
|
+
</TruncateList>
|
|
163
|
+
|
|
257
164
|
{renderAfterItems && <span>{renderAfterItems}</span>}
|
|
258
165
|
</View>
|
|
259
166
|
)
|
package/src/AppNav/props.ts
CHANGED
|
@@ -99,14 +99,10 @@ type AppNavProps = AppNavOwnProps &
|
|
|
99
99
|
WithStyleProps<AppNavTheme, AppNavStyle> &
|
|
100
100
|
OtherHTMLAttributes<AppNavOwnProps>
|
|
101
101
|
|
|
102
|
-
type AppNavStyle = ComponentStyle<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
| 'listItem'
|
|
107
|
-
| 'list'
|
|
108
|
-
| 'menuTriggerWidth'
|
|
109
|
-
>
|
|
102
|
+
type AppNavStyle = ComponentStyle<'appNav' | 'alignCenter' | 'list'> & {
|
|
103
|
+
horizontalMargin: string
|
|
104
|
+
menuTriggerWidth: string
|
|
105
|
+
}
|
|
110
106
|
|
|
111
107
|
const propTypes: PropValidators<PropKeys> = {
|
|
112
108
|
screenReaderLabel: PropTypes.string.isRequired,
|
package/src/AppNav/styles.ts
CHANGED
|
@@ -45,36 +45,17 @@ const generateStyle = (componentTheme: AppNavTheme): AppNavStyle => {
|
|
|
45
45
|
alignCenter: {
|
|
46
46
|
alignItems: 'center'
|
|
47
47
|
},
|
|
48
|
-
listItemWithMenuTrigger: {
|
|
49
|
-
label: 'appNav__listItemWithMenuTrigger',
|
|
50
|
-
minWidth: '0.0625rem',
|
|
51
|
-
marginInlineStart: componentTheme.horizontalMargin,
|
|
52
|
-
marginInlineEnd: '0',
|
|
53
|
-
flexShrink: 0,
|
|
54
|
-
flexBasis: componentTheme.menuTriggerWidth
|
|
55
|
-
},
|
|
56
|
-
listItem: {
|
|
57
|
-
label: 'appNav__listItem',
|
|
58
|
-
minWidth: '0.0625rem',
|
|
59
|
-
marginInlineStart: componentTheme.horizontalMargin,
|
|
60
|
-
marginInlineEnd: '0',
|
|
61
|
-
flexShrink: 0
|
|
62
|
-
},
|
|
63
48
|
list: {
|
|
64
49
|
label: 'appNav__list',
|
|
65
|
-
boxSizing: 'border-box',
|
|
66
|
-
listStyleType: 'none',
|
|
67
50
|
height: componentTheme.height,
|
|
68
|
-
margin: '0',
|
|
69
|
-
padding: '0',
|
|
70
|
-
display: 'flex',
|
|
71
|
-
alignItems: 'center',
|
|
72
51
|
flexGrow: 1,
|
|
73
52
|
flexShrink: 1,
|
|
74
53
|
flexBasis: '0',
|
|
75
|
-
minWidth: '0.0625rem'
|
|
54
|
+
minWidth: '0.0625rem',
|
|
55
|
+
paddingInlineStart: componentTheme.horizontalMargin
|
|
76
56
|
},
|
|
77
|
-
|
|
57
|
+
horizontalMargin: componentTheme.horizontalMargin.toString(),
|
|
58
|
+
menuTriggerWidth: componentTheme.menuTriggerWidth.toString()
|
|
78
59
|
}
|
|
79
60
|
}
|
|
80
61
|
|
package/tsconfig.build.json
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
{ "path": "../ui-react-utils/tsconfig.build.json" },
|
|
28
28
|
{ "path": "../ui-testable/tsconfig.build.json" },
|
|
29
29
|
{ "path": "../ui-tooltip/tsconfig.build.json" },
|
|
30
|
+
{ "path": "../ui-truncate-list/tsconfig.build.json" },
|
|
30
31
|
{ "path": "../ui-utils/tsconfig.build.json" },
|
|
31
32
|
{ "path": "../ui-view/tsconfig.build.json" }
|
|
32
33
|
]
|