4
4
* See License-AGPL.txt in the project root for license information.
5
5
*/
6
6
7
- import { User } from "@gitpod/gitpod-protocol" ;
8
- import { useContext } from "react" ;
9
- import { Link } from "react-router-dom" ;
7
+ import { Team , User } from "@gitpod/gitpod-protocol" ;
8
+ import { useContext , useEffect , useState } from "react" ;
9
+ import { Link , useHistory } from "react-router-dom" ;
10
+ import { useLocation } from "react-router" ;
11
+ import { Location } from "history" ;
10
12
import gitpodIcon from '../icons/gitpod.svg' ;
11
- import { gitpodHostUrl } from "../service/service" ;
13
+ import CaretDown from "../icons/CaretDown.svg" ;
14
+ import { getGitpodService , gitpodHostUrl } from "../service/service" ;
12
15
import { UserContext } from "../user-context" ;
13
16
import ContextMenu from "./ContextMenu" ;
14
- import { useLocation } from "react-router" ;
17
+ import Separator from "./Separator" ;
18
+ import PillMenuItem from "./PillMenuItem" ;
19
+ import TabMenuItem from "./TabMenuItem" ;
20
+
15
21
interface Entry {
16
22
title : string ,
17
23
link : string ,
18
24
alternatives ?: string [ ]
19
25
}
20
26
21
- function MenuItem ( entry : Entry ) {
22
- const location = useLocation ( ) ;
23
- let classes = "flex block text-sm font-medium dark:text-gray-200 px-3 px-0 py-1.5 rounded-md transition ease-in-out" ;
27
+ function isSelected ( entry : Entry , location : Location < any > ) {
24
28
const all = [ entry . link , ...( entry . alternatives || [ ] ) ] ;
25
29
const path = location . pathname . toLowerCase ( ) ;
26
- if ( all . find ( n => n === path || n + '/' === path ) ) {
27
- classes += " bg-gray-200 dark:bg-gray-700" ;
28
- } else {
29
- classes += " text-gray-600 hover:bg-gray-100 dark:hover:bg-gray-800" ;
30
- }
31
- return < li key = { entry . title } >
32
- { entry . link . startsWith ( 'https://' )
33
- ? < a className = { classes } href = { entry . link } >
34
- < div > { entry . title } </ div >
35
- </ a >
36
- : < Link className = { classes } to = { entry . link } >
37
- < div > { entry . title } </ div >
38
- </ Link > }
39
- </ li > ;
30
+ return all . some ( n => n === path || n + '/' === path ) ;
40
31
}
41
32
42
- function Menu ( props : { left : Entry [ ] , right : Entry [ ] } ) {
33
+ export default function Menu ( props : { left : Entry [ ] , right : Entry [ ] , showTeams ?: boolean } ) {
43
34
const { user } = useContext ( UserContext ) ;
35
+ const history = useHistory ( ) ;
36
+ const location = useLocation ( ) ;
37
+ const [ teams , setTeams ] = useState < Team [ ] > ( [ ] ) ;
38
+ useEffect ( ( ) => {
39
+ getGitpodService ( ) . server . getTeams ( ) . then ( setTeams ) . catch ( error => {
40
+ console . error ( 'Could not fetch teams!' , error ) ;
41
+ } ) ;
42
+ } , [ ] ) ;
44
43
45
- return (
46
- < header className = "lg:px-28 px-10 flex flex-wrap items-center py-4" >
47
- < div className = "flex justify-between items-center pr-3" >
48
- < Link to = "/" >
49
- < img src = { gitpodIcon } className = "h-6" />
50
- </ Link >
51
- </ div >
52
- < div className = "flex flex-1 items-center w-auto w-full" id = "menu" >
53
- < nav className = "flex-1" >
54
- < ul className = "flex flex-1 items-center justify-between text-base text-gray-700 space-x-2" >
55
- { props . left . map ( MenuItem ) }
56
- < li className = "flex-1" > </ li >
57
- { props . right . map ( MenuItem ) }
58
- </ ul >
59
- </ nav >
60
- < div className = "ml-3 flex items-center justify-start mb-0 pointer-cursor m-l-auto rounded-full border-2 border-transparent hover:border-gray-200 dark:hover:border-gray-700 p-0.5 font-medium" >
61
- < ContextMenu menuEntries = { [
62
- {
63
- title : ( user && User . getPrimaryEmail ( user ) ) || '' ,
64
- customFontStyle : 'text-gray-400' ,
65
- separator : true
66
- } ,
67
- {
68
- title : 'Settings' ,
69
- link : '/settings' ,
70
- separator : true
71
- } ,
72
- {
73
- title : 'Logout' ,
74
- href : gitpodHostUrl . asApiLogout ( ) . toString ( )
75
- } ,
76
- ] } >
77
- < img className = "rounded-full w-6 h-6"
78
- src = { user ?. avatarUrl || '' } alt = { user ?. name || 'Anonymous' } />
79
- </ ContextMenu >
44
+ const userFullName = user ?. fullName || user ?. name || '...' ;
45
+
46
+ return < >
47
+ < header className = "lg:px-28 px-10 flex flex-col pt-4 space-y-4" >
48
+ < div className = "flex" >
49
+ < div className = "flex justify-between items-center pr-3" >
50
+ < Link to = "/" >
51
+ < img src = { gitpodIcon } className = "h-6" />
52
+ </ Link >
53
+ < div className = "ml-2 text-base" >
54
+ { ! ! props . showTeams
55
+ ? < ContextMenu classes = "w-64 left-0" menuEntries = { [
56
+ {
57
+ title : userFullName ,
58
+ customContent : < div className = "w-full text-gray-400 flex flex-col" >
59
+ < span className = "text-gray-800 text-base font-semibold" > { userFullName } </ span >
60
+ < span className = "" > Personal Account</ span >
61
+ </ div > ,
62
+ separator : true ,
63
+ onClick : ( ) => { } ,
64
+ } ,
65
+ ...( teams || [ ] ) . map ( t => ( {
66
+ title : t . name ,
67
+ customContent : < div className = "w-full text-gray-400 flex flex-col" >
68
+ < span className = "text-gray-800 text-base font-semibold" > { t . name } </ span >
69
+ < span className = "" > N members</ span >
70
+ </ div > ,
71
+ separator : true ,
72
+ onClick : ( ) => { } ,
73
+ } ) ) . sort ( ( a , b ) => a . title . toLowerCase ( ) > b . title . toLowerCase ( ) ? 1 : - 1 ) ,
74
+ {
75
+ title : 'Create a new team' ,
76
+ customContent : < div className = "w-full text-gray-400 flex items-center" >
77
+ < span className = "flex-1 font-semibold" > New Team</ span >
78
+ < svg xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 14 14" className = "w-3.5" > < path fill = "currentColor" fill-rule = "evenodd" d = "M7 0a1 1 0 011 1v5h5a1 1 0 110 2H8v5a1 1 0 11-2 0V8H1a1 1 0 010-2h5V1a1 1 0 011-1z" clip-rule = "evenodd" /> </ svg >
79
+ </ div > ,
80
+ onClick : ( ) => history . push ( "/new-team" ) ,
81
+ }
82
+ ] } >
83
+ < div className = "flex p-1.5 pl-3 rounded-lg hover:bg-gray-200" >
84
+ < span className = "text-base text-gray-600 font-semibold" > { userFullName } </ span >
85
+ < img className = "m-2 filter-grayscale" src = { CaretDown } />
86
+ </ div >
87
+ </ ContextMenu >
88
+ : < nav className = "flex-1" >
89
+ < ul className = "flex flex-1 items-center justify-between text-base text-gray-700 space-x-2" >
90
+ < li className = "flex-1" > </ li >
91
+ { props . left . map ( entry => < li key = { entry . title } >
92
+ < PillMenuItem name = { entry . title } selected = { isSelected ( entry , location ) } link = { entry . link } />
93
+ </ li > ) }
94
+ </ ul >
95
+ </ nav >
96
+ }
97
+ </ div >
98
+ </ div >
99
+ < div className = "flex flex-1 items-center w-auto" id = "menu" >
100
+ < nav className = "flex-1" >
101
+ < ul className = "flex flex-1 items-center justify-between text-base text-gray-700 space-x-2" >
102
+ < li className = "flex-1" > </ li >
103
+ { props . right . map ( entry => < li key = { entry . title } >
104
+ < PillMenuItem name = { entry . title } selected = { isSelected ( entry , location ) } link = { entry . link } />
105
+ </ li > ) }
106
+ </ ul >
107
+ </ nav >
108
+ < div className = "ml-3 flex items-center justify-start mb-0 pointer-cursor m-l-auto rounded-full border-2 border-transparent hover:border-gray-200 dark:hover:border-gray-700 p-0.5 font-medium" >
109
+ < ContextMenu menuEntries = { [
110
+ {
111
+ title : ( user && User . getPrimaryEmail ( user ) ) || '' ,
112
+ customFontStyle : 'text-gray-400' ,
113
+ separator : true
114
+ } ,
115
+ {
116
+ title : 'Settings' ,
117
+ link : '/settings' ,
118
+ separator : true
119
+ } ,
120
+ {
121
+ title : 'Logout' ,
122
+ href : gitpodHostUrl . asApiLogout ( ) . toString ( )
123
+ } ,
124
+ ] } >
125
+ < img className = "rounded-full w-6 h-6" src = { user ?. avatarUrl || '' } alt = { user ?. name || 'Anonymous' } />
126
+ </ ContextMenu >
127
+ </ div >
80
128
</ div >
81
129
</ div >
130
+ { ! ! props . showTeams && < div className = "flex" >
131
+ { props . left . map ( entry => < TabMenuItem name = { entry . title } selected = { isSelected ( entry , location ) } link = { entry . link } /> ) }
132
+ </ div > }
82
133
</ header >
83
- ) ;
84
- }
85
-
86
- export default Menu ;
134
+ { ! ! props . showTeams && < Separator /> }
135
+ </ > ;
136
+ }
0 commit comments