Skip to content

Commit efea34e

Browse files
committed
Merge pull request #3457 from gitpod-io/minor-updates
[dashboard] improved workspaces
1 parent d397765 commit efea34e

File tree

13 files changed

+317
-110
lines changed

13 files changed

+317
-110
lines changed

components/dashboard/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"private": true,
66
"dependencies": {
77
"@gitpod/gitpod-protocol": "0.1.5",
8+
"moment": "^2.29.1",
89
"react": "^17.0.1",
910
"react-dom": "^17.0.1",
1011
"react-router-dom": "^5.2.0"
@@ -20,8 +21,8 @@
2021
"@types/node": "^12.0.0",
2122
"@types/react": "^17.0.0",
2223
"@types/react-dom": "^17.0.0",
23-
"@types/react-router-dom": "^5.1.7",
2424
"@types/react-router": "^5.1.12",
25+
"@types/react-router-dom": "^5.1.7",
2526
"@typescript-eslint/eslint-plugin": "^4.15.2",
2627
"@typescript-eslint/parser": "^4.15.2",
2728
"autoprefixer": "^9.8.6",
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useState } from 'react';
2+
3+
export interface ContextMenuProps {
4+
children: React.ReactChild[] | React.ReactChild;
5+
menuEntries: ContextMenuEntry[];
6+
}
7+
8+
export interface ContextMenuEntry {
9+
title: string;
10+
active?: boolean;
11+
/**
12+
* whether a separator line should be rendered below this item
13+
*/
14+
separator?: boolean;
15+
customFontStyle?: string;
16+
onClick?: ()=>void;
17+
href?: string;
18+
}
19+
20+
function ContextMenu(props: ContextMenuProps) {
21+
const [expanded, setExpanded] = useState(false);
22+
const toggleExpanded = () => {
23+
setExpanded(!expanded);
24+
}
25+
26+
if (expanded) {
27+
// HACK! I want to skip the bubbling phase of the current click
28+
setTimeout(() => {
29+
window.addEventListener('click', () => setExpanded(false), { once: true });
30+
}, 0);
31+
}
32+
33+
const enhancedEntries = props.menuEntries.map(e => {
34+
return {
35+
... e,
36+
onClick: () => {
37+
e.onClick && e.onClick();
38+
toggleExpanded();
39+
}
40+
}
41+
})
42+
const font = "text-gray-400 hover:text-gray-800"
43+
return (
44+
<div className="relative cursor-pointer">
45+
<div onClick={(e) => {
46+
toggleExpanded();
47+
e.preventDefault();
48+
}}>
49+
{props.children}
50+
</div>
51+
{expanded?
52+
<div className={`z-50 w-40 bg-white absolute py-2 right-0 flex flex-col border border-gray-200 rounded-lg space-y-2`}>
53+
{enhancedEntries.map(e => {
54+
const entry = <div key={e.title} className={`px-4 flex py-2 text-gray-600 hover:bg-gray-200 text-sm leading-1 ${e.customFontStyle || font} ${e.separator? ' border-b border-gray-200':''}`} >
55+
<div>{e.title}</div><div className="flex-1"></div>{e.active ? <div className="pl-1 font-semibold">&#x2713;</div>: null}
56+
</div>
57+
return <a href={e.href} onClick={e.onClick}>
58+
{entry}
59+
</a>
60+
})}
61+
</div>
62+
:
63+
null
64+
}
65+
</div>
66+
);
67+
}
68+
69+
export default ContextMenu;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useState } from 'react';
2+
import ContextMenu from './ContextMenu';
3+
4+
export interface DropDownProps {
5+
entries: {
6+
title: string,
7+
onClick: ()=>void
8+
}[];
9+
}
10+
11+
function Arrow(props: {up: boolean}) {
12+
return <span className="mx-2 border-gray-400" style={{ margin: 2, padding: 3, border: 'solid black', borderWidth: '0 2px 2px 0', display: 'inline-block', transform: `rotate(${props.up ? '-135deg' : '45deg'})`}}></span>
13+
}
14+
15+
function DropDown(props: DropDownProps) {
16+
const [current, setCurrent] = useState(props.entries[0].title);
17+
const enhancedEntries = props.entries.map(e => {
18+
return {
19+
...e,
20+
active: e.title === current,
21+
onClick: () => {
22+
e.onClick();
23+
setCurrent(e.title);
24+
}
25+
}
26+
})
27+
const font = "text-gray-400 text-sm leading-1"
28+
return (
29+
<ContextMenu menuEntries={enhancedEntries}>
30+
<span className={`py-2 cursor-pointer ${font}`}>{current}<Arrow up={false}/></span>
31+
</ContextMenu>
32+
);
33+
}
34+
35+
export default DropDown;

components/dashboard/src/components/Modal.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
1-
import { useState } from "react";
2-
31
export default function Modal(props: {
42
children: React.ReactChild[] | React.ReactChild,
53
visible: boolean,
64
closeable?: boolean,
7-
onClose?: () => void
5+
onClose: () => void
86
}) {
9-
const [visible, setVisible] = useState(props.visible);
10-
const hide = () => setVisible(false);
11-
if (!visible) {
7+
if (!props.visible) {
128
return null;
139
}
10+
setTimeout(() => window.addEventListener('click', props.onClose, {once: true}), 0);
1411
return (
15-
<div className="absolute top-0 left-0 bg-black bg-opacity-40 z-50 w-screen h-screen" >
12+
<div className="fixed top-0 -left-2 bg-black bg-opacity-70 z-50 w-screen h-screen" >
1613
<div className="bg-transparent h-1/3" />
17-
<div className="bg-white rounded-md px-6 py-4 max-w-lg mx-auto">
14+
<div className="bg-white border rounded-xl p-6 max-w-lg mx-auto">
1815
{props.closeable !== false && (
19-
<div className="float-right cursor-pointer" onClick={hide}>&#10006;</div>
16+
<div className="float-right cursor-pointer" onClick={props.onClose}>&#10006;</div>
2017
)}
21-
2218
{props.children}
2319
</div>
2420
</div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export default function Separator() {
2-
return <div className="bg-gray-200 h-0.5 absolute left-0 w-screen"></div>;
2+
return <div className="border-gray-200 border-b h-0.5 absolute left-0 w-screen"></div>;
33
}

components/dashboard/src/components/Toggle.tsx

Lines changed: 0 additions & 28 deletions
This file was deleted.

components/dashboard/src/index.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
}
2929

3030
input[type=text] {
31-
@apply text-xs block w-56 font-medium text-gray-500 rounded-md bg-gray-100 border-gray-300 border-2 focus:border-gray-300 focus:bg-white focus:ring-0;
31+
@apply text-xs block w-56 text-sm text-gray-600 rounded-md focus:border-gray-300 focus:bg-white focus:ring-0;
3232
}
33+
34+
input[type=text]::placeholder {
35+
@apply text-gray-400
36+
}
37+
3338
}

components/dashboard/src/settings/Account.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default function Account() {
1212

1313
const close = () => setModal(false);
1414
return <div>
15-
<Modal visible={modal}>
15+
<Modal visible={modal} onClose={close}>
1616
<h3>Do you really want to delete your account?</h3>
1717
<p>This action will remove all the data associated with your account in Gitpod and cannot be reversed.</p>
1818
<div className="flex justify-end pt-6">

components/dashboard/src/start/CreateWorkspace.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class CreateWorkspace extends React.Component<CreateWorkspaceProps, Creat
8181
}
8282

8383
else if (result?.existingWorkspaces) {
84-
statusMessage = <Modal visible={true} closeable={false}>
84+
statusMessage = <Modal visible={true} closeable={false} onClose={()=>{}}>
8585
<h3>Running Workspaces</h3>
8686
<div className="border-t border-b border-gray-200 mt-2 -mx-6 px-6 py-2">
8787
<p className="mt-1 mb-2 text-base">You already have running workspaces with the same context. You can open an existing one or open a new workspace.</p>

components/dashboard/src/tailwind.output.css

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -858,33 +858,20 @@ button {
858858
color: rgba(255, 255, 255, var(--tw-text-opacity));
859859
}
860860

861-
input[type=text] {
862-
--tw-bg-opacity: 1;
863-
background-color: rgba(245, 245, 244, var(--tw-bg-opacity));
864-
}
865-
866861
input[type=text]:focus {
867862
--tw-bg-opacity: 1;
868863
background-color: rgba(255, 255, 255, var(--tw-bg-opacity));
869-
}
870-
871-
input[type=text] {
872-
--tw-border-opacity: 1;
873-
border-color: rgba(214, 211, 209, var(--tw-border-opacity));
874-
}
875-
876-
input[type=text]:focus {
877864
--tw-border-opacity: 1;
878865
border-color: rgba(214, 211, 209, var(--tw-border-opacity));
879866
}
880867

881868
input[type=text] {
882869
border-radius: 0.375rem;
883-
border-width: 2px;
884870
display: block;
885-
font-weight: 500;
886871
font-size: 0.75rem;
887872
line-height: 1rem;
873+
font-size: 0.875rem;
874+
line-height: 1.25rem;
888875
}
889876

890877
input[type=text]:focus {
@@ -895,10 +882,30 @@ input[type=text]:focus {
895882

896883
input[type=text] {
897884
--tw-text-opacity: 1;
898-
color: rgba(120, 113, 108, var(--tw-text-opacity));
885+
color: rgba(87, 83, 78, var(--tw-text-opacity));
899886
width: 14rem;
900887
}
901888

889+
input[type=text]::-webkit-input-placeholder {
890+
--tw-text-opacity: 1;
891+
color: rgba(168, 162, 158, var(--tw-text-opacity))
892+
}
893+
894+
input[type=text]:-ms-input-placeholder {
895+
--tw-text-opacity: 1;
896+
color: rgba(168, 162, 158, var(--tw-text-opacity))
897+
}
898+
899+
input[type=text]::-ms-input-placeholder {
900+
--tw-text-opacity: 1;
901+
color: rgba(168, 162, 158, var(--tw-text-opacity))
902+
}
903+
904+
input[type=text]::placeholder {
905+
--tw-text-opacity: 1;
906+
color: rgba(168, 162, 158, var(--tw-text-opacity))
907+
}
908+
902909
.space-y-0 > :not([hidden]) ~ :not([hidden]) {
903910
--tw-space-y-reverse: 0 !important;
904911
margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))) !important;

0 commit comments

Comments
 (0)