Skip to content

Commit 74387fa

Browse files
disclosure widget implementation continue
1 parent 81dcd14 commit 74387fa

File tree

2 files changed

+93
-83
lines changed

2 files changed

+93
-83
lines changed

src/UnderlineNav2/UnderlineNav.tsx

Lines changed: 86 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useRef, forwardRef, useCallback, useState, MutableRefObject, RefObject} from 'react'
1+
import React, {useRef, forwardRef, useCallback, useState, MutableRefObject, RefObject, useEffect} from 'react'
22
import Box from '../Box'
33
import sx, {merge, BetterSystemStyleObject, SxProp} from '../sx'
44
import {UnderlineNavContext} from './UnderlineNavContext'
@@ -12,6 +12,11 @@ import styled from 'styled-components'
1212
import {LoadingCounter} from './LoadingCounter'
1313
import {Button} from '../Button'
1414
import Link from '../Link'
15+
import {useFocusZone} from '../hooks/useFocusZone'
16+
import {FocusKeys, getAnchoredPosition} from '@primer/behaviors'
17+
import {ChevronDownIcon, TriangleDownIcon} from '@primer/octicons-react'
18+
import {useOnEscapePress} from '../hooks/useOnEscapePress'
19+
import {useOnOutsideClick} from '../hooks/useOnOutsideClick'
1520

1621
export type UnderlineNavProps = {
1722
'aria-label'?: React.AriaAttributes['aria-label']
@@ -35,10 +40,6 @@ const NavigationList = styled.ul`
3540
${sx};
3641
`
3742

38-
const DisclosureBox = styled.ul`
39-
${sx};
40-
`
41-
4243
const MoreMenuListItem = styled.li`
4344
display: flex;
4445
`
@@ -135,8 +136,8 @@ export const UnderlineNav = forwardRef(
135136
const navRef = (forwardedRef ?? backupRef) as MutableRefObject<HTMLElement>
136137
const listRef = useRef<HTMLUListElement>(null)
137138
const moreMenuRef = useRef<HTMLLIElement>(null)
138-
const containerRef = useRef<HTMLUListElement>(null)
139139
const moreMenuBtnRef = useRef<HTMLButtonElement>(null)
140+
const containerRef = React.useRef<HTMLUListElement>(null)
140141

141142
const {theme} = useTheme()
142143

@@ -247,17 +248,37 @@ export const UnderlineNav = forwardRef(
247248
// eslint-disable-next-line no-console
248249
console.warn('Use the `aria-label` prop to provide an accessible label for assistive technology')
249250
}
250-
251251
const [isWidgetOpen, setIsWidgetOpen] = useState(false)
252252

253-
const onAnchorKeyDown = useCallback(
254-
(event: React.KeyboardEvent<HTMLButtonElement>) => {
255-
if (!event.defaultPrevented) {
256-
if (!isWidgetOpen && ['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(event.key)) {
257-
setIsWidgetOpen(true)
258-
event.preventDefault()
259-
}
253+
const closeOverlay = React.useCallback(() => {
254+
setIsWidgetOpen(false)
255+
}, [setIsWidgetOpen])
256+
257+
const toggleOverlay = React.useCallback(() => {
258+
setIsWidgetOpen(!isWidgetOpen)
259+
}, [setIsWidgetOpen, isWidgetOpen])
260+
261+
useOnOutsideClick({onClickOutside: closeOverlay, containerRef, ignoreClickRefs: [moreMenuBtnRef]})
262+
263+
useFocusZone({
264+
containerRef: backupRef,
265+
bindKeys: FocusKeys.ArrowVertical | FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd | FocusKeys.Tab
266+
})
267+
268+
useFocusZone({
269+
containerRef,
270+
bindKeys: FocusKeys.ArrowVertical | FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd,
271+
focusInStrategy: 'first'
272+
})
273+
274+
useOnEscapePress(
275+
(event: KeyboardEvent) => {
276+
if (isWidgetOpen) {
277+
event.preventDefault()
278+
closeOverlay()
260279
}
280+
// onClose('escape')
281+
// event.preventDefault()
261282
},
262283
[isWidgetOpen]
263284
)
@@ -266,21 +287,11 @@ export const UnderlineNav = forwardRef(
266287
if (event.defaultPrevented || event.button !== 0) {
267288
return
268289
}
269-
setIsWidgetOpen(!isWidgetOpen)
290+
toggleOverlay()
270291
},
271-
[isWidgetOpen]
292+
[toggleOverlay]
272293
)
273294

274-
const handleMenuItemKeyDown = useCallback((event: React.KeyboardEvent<HTMLAnchorElement>) => {
275-
if (!event.defaultPrevented) {
276-
if (event.key === 'ArrowDown') {
277-
// manage focus on children
278-
279-
event.preventDefault()
280-
}
281-
}
282-
}, [])
283-
284295
return (
285296
<UnderlineNavContext.Provider
286297
value={{
@@ -315,64 +326,60 @@ export const UnderlineNav = forwardRef(
315326
sx={moreBtnStyles}
316327
aria-controls="disclosure-widget"
317328
aria-expanded={isWidgetOpen}
318-
onKeyDown={onAnchorKeyDown}
319329
onClick={onAnchorClick}
330+
trailingIcon={TriangleDownIcon}
320331
>
321332
More
322333
</Button>
323334

324-
{isWidgetOpen && (
325-
<DisclosureBox
326-
ref={containerRef}
327-
// onKeyDown={keyPressHandler}
328-
id="disclosure-widget"
329-
sx={{
330-
position: 'absolute',
331-
top: '100%',
332-
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24)',
333-
borderRadius: '12px',
334-
right: '100px',
335-
backgroundColor: `${theme?.colors.canvas.overlay}`,
336-
listStyle: 'none',
337-
padding: '8px',
338-
minWidth: '192px',
339-
maxWidth: '640px'
340-
}}
341-
>
342-
{actions.map((action, index) => {
343-
const {children: actionElementChildren, ...actionElementProps} = action.props
344-
return (
345-
<Box key={index} as="li">
346-
<a
347-
href="#"
348-
// sx={menuItemStyles}
349-
// as={asNavItem}
350-
onClick={(event: React.MouseEvent<HTMLAnchorElement>) => {
351-
swapMenuItemWithListItem(action, index, event, updateListAndMenu)
352-
setSelectEvent(event)
353-
setIsWidgetOpen(false)
354-
moreMenuBtnRef.current?.focus()
355-
}}
356-
onKeyDown={handleMenuItemKeyDown}
357-
>
358-
<Box
359-
as="span"
360-
sx={{display: 'flex', alignItems: 'center', justifyContent: 'space-between'}}
361-
>
362-
{actionElementChildren}
363-
364-
{loadingCounters ? (
365-
<LoadingCounter />
366-
) : (
367-
<CounterLabel>{actionElementProps.counter}</CounterLabel>
368-
)}
369-
</Box>
370-
</a>
371-
</Box>
372-
)
373-
})}
374-
</DisclosureBox>
375-
)}
335+
<Box
336+
as="ul"
337+
ref={containerRef}
338+
id="disclosure-widget"
339+
sx={{
340+
position: 'absolute',
341+
top: '90%',
342+
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24)',
343+
borderRadius: '12px',
344+
right: '0',
345+
backgroundColor: `${theme?.colors.canvas.overlay}`,
346+
listStyle: 'none',
347+
padding: '8px',
348+
minWidth: '192px',
349+
maxWidth: '640px',
350+
display: isWidgetOpen ? 'block' : 'none'
351+
}}
352+
// onEscape={() => setIsWidgetOpen(false)}
353+
>
354+
{actions.map((action, index) => {
355+
const {children: actionElementChildren, ...actionElementProps} = action.props
356+
return (
357+
<Box key={index} as="li">
358+
<a
359+
href="#hello"
360+
// sx={menuItemStyles}
361+
// as={asNavItem}
362+
onClick={(event: React.MouseEvent<HTMLAnchorElement>) => {
363+
swapMenuItemWithListItem(action, index, event, updateListAndMenu)
364+
setSelectEvent(event)
365+
closeOverlay()
366+
moreMenuBtnRef.current?.focus()
367+
}}
368+
>
369+
<Box as="span" sx={{display: 'flex', alignItems: 'center', justifyContent: 'space-between'}}>
370+
{actionElementChildren}
371+
372+
{loadingCounters ? (
373+
<LoadingCounter />
374+
) : (
375+
<CounterLabel>{actionElementProps.counter}</CounterLabel>
376+
)}
377+
</Box>
378+
</a>
379+
</Box>
380+
)
381+
})}
382+
</Box>
376383
</MoreMenuListItem>
377384
)}
378385
</NavigationList>

src/UnderlineNav2/styles.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ export const getNavStyles = (theme?: Theme, props?: Partial<Pick<UnderlineNavPro
4040
borderBottom: '1px solid',
4141
borderBottomColor: `${theme?.colors.border.muted}`,
4242
align: 'row',
43-
alignItems: 'center',
44-
position: 'relative'
43+
alignItems: 'center'
4544
})
4645

4746
export const ulStyles = {
@@ -52,7 +51,8 @@ export const ulStyles = {
5251
margin: 0,
5352
marginBottom: '-1px',
5453
alignItems: 'center',
55-
gap: `${GAP}px`
54+
gap: `${GAP}px`,
55+
position: 'relative'
5656
}
5757

5858
export const getDividerStyle = (theme?: Theme) => ({
@@ -71,7 +71,10 @@ export const moreBtnStyles = {
7171
fontWeight: 'normal',
7272
boxShadow: 'none',
7373
paddingY: 1,
74-
paddingX: 2
74+
paddingX: 2,
75+
'& > span[data-component="trailingIcon"]': {
76+
marginLeft: 0
77+
}
7578
}
7679

7780
export const getLinkStyles = (

0 commit comments

Comments
 (0)