1 import React from 'react'
2 import {ActionList, ActionMenu, Box} from '@primer/react'
3 import * as getNav from '../util/get-nav'
4 import usePage from '../hooks/use-page'
5 import {LinkNoUnderline} from './link'
6 import useLocationChange from '../hooks/use-location-change'
7
8 const VariantItem = ({title, shortName, url, active}) => (
9 <ActionList.Item as={LinkNoUnderline} to={url} state={{scrollUpdate: false}} id={shortName} active={active}>
10 {title}
11 </ActionList.Item>
12 )
13
14 const useVariantFocus = () => {
15 const locationChange = useLocationChange()
16 const anchorRef = React.useRef(null)
17
18 React.useEffect(() => {
19 if (locationChange.change && getNav.didVariantChange(locationChange.previous, locationChange.current)) {
20 anchorRef.current.focus()
21 }
22 }, [locationChange])
23
24 return anchorRef
25 }
26
27 const VariantMenu = ({title, latest, current, prerelease, legacy}) => {
28 const [open, setOpen] = React.useState(false)
29 const anchorRef = useVariantFocus()
30 const labelId = 'label-versions-list-item'
31
32 return (
33 <>
34 <Box as="p" sx={{m: 0}} id={labelId}>
35 Select CLI Version:
36 </Box>
37 <ActionMenu anchorRef={anchorRef} open={open} onOpenChange={setOpen}>
38 <ActionMenu.Button aria-describedby={labelId} sx={{width: ['100%', null, 'auto']}}>
39 {title}
40 </ActionMenu.Button>
41 <ActionMenu.Overlay width="auto" onEscape={() => setOpen(false)}>
42 <ActionList aria-labelledby={labelId}>
43 <ActionList.Group>
44 <ActionList.GroupHeading>Current</ActionList.GroupHeading>
45 <VariantItem {...latest} />
46 {current && <VariantItem {...current} />}
47 {prerelease && <VariantItem {...prerelease} />}
48 </ActionList.Group>
49 <ActionList.Group>
50 <ActionList.GroupHeading>Legacy</ActionList.GroupHeading>
51 {legacy.map(item => (
52 <VariantItem key={item.title} {...item} />
53 ))}
54 </ActionList.Group>
55 </ActionList>
56 </ActionMenu.Overlay>
57 </ActionMenu>
58 </>
59 )
60 }
61
62 const useVariants = () => {
63 const {pathname: path} = usePage().location
64
65 return React.useMemo(() => {
66 const variantPages = getNav.getVariantsForPath(path)
67
68 if (!variantPages.length) {
69 return null
70 }
71
72 const result = {latest: null, current: null, prerelease: null, legacy: []}
73
74 for (const {variant, page} of variantPages) {
75 const item = {...variant, url: page.url, active: page.url === path}
76 let typeDesc = ''
77 switch (variant.type) {
78 case 'latest':
79 result.latest = item
80 typeDesc = ' (Latest)'
81 break
82 case 'current':
83 result.current = item
84 typeDesc = ' (Current)'
85 break
86 case 'prerelease':
87 result.prerelease = item
88 typeDesc = ' (Prerelease)'
89 break
90 default:
91 result.legacy.push(item)
92 typeDesc = ' (Legacy)'
93 }
94 if (item.active) {
95 result.title = `${item.title}${typeDesc}`
96 }
97 }
98
99 result.legacy.sort((a, b) => parseInt(b.shortName.slice(1)) - parseInt(a.shortName.slice(1)))
100
101 return result
102 }, [path])
103 }
104
105 const VariantSelect = () => {
106 const variants = useVariants()
107 return variants ? (
108 <Box sx={{mt: 2, mb: 3}}>
109 <VariantMenu {...variants} />
110 </Box>
111 ) : null
112 }
113
114 export default VariantSelect