Skip to content

Commit ca237f8

Browse files
authored
refactor: Adopt vscode-webview-ui-toolkit to the configure classpath view (#1262)
1 parent 16c7fb2 commit ca237f8

File tree

10 files changed

+185
-254
lines changed

10 files changed

+185
-254
lines changed

src/classpath/assets/features/classpathConfiguration/ClasspathConfigurationView.tsx

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT license.
33

44
import React, { useEffect } from "react";
5-
import { Col, Container, Row, Spinner } from "react-bootstrap";
65
import { useSelector, useDispatch } from "react-redux";
76
import { Dispatch } from "@reduxjs/toolkit";
87
import Output from "./components/Output";
@@ -15,6 +14,7 @@ import { ClasspathViewException, ProjectInfo } from "../../../types";
1514
import { catchException, listProjects, loadClasspath } from "./classpathConfigurationViewSlice";
1615
import JdkRuntime from "./components/JdkRuntime";
1716
import { onWillListProjects } from "../../utils";
17+
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react";
1818

1919
const ClasspathConfigurationView = (): JSX.Element => {
2020
const projects: ProjectInfo[] = useSelector((state: any) => state.classpathConfig.projects);
@@ -24,31 +24,15 @@ const ClasspathConfigurationView = (): JSX.Element => {
2424
if (exception) {
2525
content = <Exception />;
2626
} else if (projects.length === 0) {
27-
content = <Spinner animation="border" role="status" size="sm"><span className="sr-only">Loading...</span></Spinner>;
27+
content = <VSCodeProgressRing></VSCodeProgressRing>;
2828
} else {
2929
content = (
3030
<div>
3131
<ProjectSelector />
32-
<Row className="setting-section">
33-
<Col>
34-
<Sources />
35-
</Col>
36-
</Row>
37-
<Row className="setting-section">
38-
<Col>
39-
<Output />
40-
</Col>
41-
</Row>
42-
<Row className="setting-section">
43-
<Col>
44-
<JdkRuntime />
45-
</Col>
46-
</Row>
47-
<Row className="setting-section">
48-
<Col>
49-
<ReferencedLibraries />
50-
</Col>
51-
</Row>
32+
<Sources />
33+
<Output />
34+
<JdkRuntime />
35+
<ReferencedLibraries />
5236
</div>
5337
);
5438
}
@@ -73,14 +57,10 @@ const ClasspathConfigurationView = (): JSX.Element => {
7357
}, []);
7458

7559
return (
76-
<Container className="root mt-4">
77-
<Row className="setting-header">
78-
<Col>
79-
<Header />
80-
</Col>
81-
</Row>
60+
<div className="root">
61+
<Header />
8262
{content}
83-
</Container>
63+
</div>
8464
);
8565
};
8666

src/classpath/assets/features/classpathConfiguration/components/Header.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import React from "react";
55

66
const Header = (): JSX.Element => {
77
return (
8-
<h2 className="mb-0">Configure Classpath</h2>
8+
<div className="setting-section">
9+
<h1 className="setting-header">Configure Classpath</h1>
10+
</div>
911
);
1012
};
1113

src/classpath/assets/features/classpathConfiguration/components/JdkRuntime.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import React from "react";
55
import { encodeCommandUriWithTelemetry } from "../../../../../utils/webview";
66
import { WEBVIEW_ID } from "../../../utils";
7+
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react";
8+
import SectionHeader from "./common/SectionHeader";
79

810
const JdkRuntime = (): JSX.Element => {
911
return (
10-
<div>
11-
<h4 className="setting-section-header mb-1">JDK Runtime</h4>
12-
<span className="setting-section-description">To configure JDK runtimes, please edit in <a href={encodeCommandUriWithTelemetry(WEBVIEW_ID, "classpath.runtime", "java.runtime")}>Configure Java Runtime</a>.</span>
12+
<div className="setting-section">
13+
<SectionHeader title="JDK Runtime" subTitle={undefined}/>
14+
<span className="setting-section-description">To configure JDK runtimes, please edit in <VSCodeLink href={encodeCommandUriWithTelemetry(WEBVIEW_ID, "classpath.runtime", "java.runtime")}>Configure Java Runtime</VSCodeLink>.</span>
1315
</div>
1416
);
1517
};

src/classpath/assets/features/classpathConfiguration/components/Output.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { useSelector, useDispatch } from "react-redux";
77
import { ProjectType } from "../../../../../utils/webview";
88
import { onWillSelectOutputPath } from "../../../utils";
99
import { setOutputPath } from "../classpathConfigurationViewSlice";
10+
import { VSCodeButton, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react";
11+
import SectionHeader from "./common/SectionHeader";
1012

1113
const Output = (): JSX.Element => {
1214
const output: string = useSelector((state: any) => state.classpathConfig.output);
@@ -29,19 +31,20 @@ const Output = (): JSX.Element => {
2931
}, []);
3032

3133
return (
32-
<div>
33-
<div className="setting-section-header mb-1">
34-
<h4 className="mb-0">Output</h4>
35-
{projectType !== ProjectType.UnmanagedFolder &&
36-
<span className="ml-2">(Read-only)</span>
37-
}
38-
</div>
34+
<div className="setting-section">
35+
<SectionHeader title="Output" subTitle={projectType !== ProjectType.UnmanagedFolder ? "(Read-only)" : undefined} />
3936
<span className="setting-section-description">Specify compile output path location.</span>
40-
<div className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} input text-break pl-1 mt-1`}>{output}</div>
37+
<div className="setting-section-target">
38+
<VSCodeTextArea
39+
className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} setting-section-text`}
40+
readOnly
41+
value={output}
42+
resize="both"
43+
rows={1}
44+
/>
45+
</div>
4146
{projectType === ProjectType.UnmanagedFolder &&
42-
<a role="button" className="btn btn-action mt-2" onClick={() => handleClick()}>
43-
Browse
44-
</a>
47+
<VSCodeButton onClick={() => handleClick()}>Browse</VSCodeButton>
4548
}
4649
</div>
4750
);

src/classpath/assets/features/classpathConfiguration/components/ProjectSelector.tsx

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@
22
// Licensed under the MIT license.
33

44
import React, { useEffect } from "react";
5-
import { Dropdown } from "react-bootstrap";
65
import { useSelector, useDispatch } from "react-redux";
76
import { ProjectInfo } from "../../../../types";
8-
import { Col, Row } from "react-bootstrap";
97
import { Dispatch } from "@reduxjs/toolkit";
108
import { activeProjectChange } from "../classpathConfigurationViewSlice";
119
import { onClickGotoProjectConfiguration, onWillLoadProjectClasspath, WEBVIEW_ID } from "../../../utils";
1210
import { encodeCommandUriWithTelemetry, ProjectType } from "../../../../../utils/webview";
13-
import { Icon } from "@iconify/react";
14-
import chevronDownIcon from "@iconify-icons/codicon/chevron-down";
11+
import { VSCodeDropdown, VSCodeLink, VSCodeOption } from "@vscode/webview-ui-toolkit/react";
1512

1613
const ProjectSelector = (): JSX.Element | null => {
1714
const activeProjectIndex: number = useSelector((state: any) => state.classpathConfig.activeProjectIndex);
@@ -43,35 +40,29 @@ const ProjectSelector = (): JSX.Element | null => {
4340

4441
const projectSelections = projects.map((project, index) => {
4542
return (
46-
<Dropdown.Item className="dropdown-item py-0 pl-1" key={project.rootPath} onSelect={() => handleActiveProjectChange(index)}>
43+
<VSCodeOption key={project.rootPath} onClick={() => handleActiveProjectChange(index)}>
4744
{project.name}
48-
</Dropdown.Item>
45+
</VSCodeOption>
4946
);
5047
});
5148

5249
return (
53-
<Row className="setting-section">
54-
<Col>
55-
<span className="setting-section-description">Select the project folder.</span>
56-
<Dropdown className="mt-1">
57-
<Dropdown.Toggle className="dropdown-button flex-vertical-center text-left">
58-
<span>{projects[activeProjectIndex].name}</span>
59-
<Icon className="codicon" icon={chevronDownIcon} />
60-
</Dropdown.Toggle>
50+
<div className="setting-section">
51+
<span className="setting-section-description">Select the project folder.</span>
52+
<div className="setting-section-target">
53+
<VSCodeDropdown className="setting-section-dropdown">
54+
{projectSelections}
55+
</VSCodeDropdown>
56+
</div>
6157

62-
<Dropdown.Menu className="dropdown-menu mt-0 p-0">
63-
{projectSelections}
64-
</Dropdown.Menu>
65-
</Dropdown>
66-
{(projectType === ProjectType.Gradle || projectType === ProjectType.Maven) &&
67-
<div className="mt-1">
68-
<span className="warning">
69-
Below settings are only editable for projects without build tools. For {projectType} project, please edit the <a href={encodeCommandUriWithTelemetry(WEBVIEW_ID, `classpath.open${projectType}Doc`, "java.helper.openUrl", [buildFileDocUrl])}>entries</a> in <a href="" onClick={() => handleOpenBuildFile()}>{buildFile}</a>.
70-
</span>
71-
</div>
72-
}
73-
</Col>
74-
</Row>
58+
{(projectType === ProjectType.Gradle || projectType === ProjectType.Maven) &&
59+
<div className="setting-section-target">
60+
<span className="setting-section-warning">
61+
Below settings are only editable for projects without build tools. For {projectType} project, please edit the <VSCodeLink href={encodeCommandUriWithTelemetry(WEBVIEW_ID, `classpath.open${projectType}Doc`, "java.helper.openUrl", [buildFileDocUrl])}>entries</VSCodeLink> in <VSCodeLink href="" onClick={() => handleOpenBuildFile()}>{buildFile}</VSCodeLink>.
62+
</span>
63+
</div>
64+
}
65+
</div>
7566
);
7667
};
7768

src/classpath/assets/features/classpathConfiguration/components/ReferencedLibraries.tsx

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22
// Licensed under the MIT license.
33

44
import { Dispatch } from "@reduxjs/toolkit";
5-
import React, { useEffect } from "react";
5+
import React, { useEffect, useState } from "react";
66
import { useSelector, useDispatch } from "react-redux";
7-
import { ListGroup } from "react-bootstrap";
8-
import { Icon } from "@iconify/react";
9-
import closeIcon from "@iconify-icons/codicon/chrome-close";
107
import { removeReferencedLibrary, addReferencedLibraries } from "../classpathConfigurationViewSlice";
118
import { onWillAddReferencedLibraries, onWillRemoveReferencedLibraries } from "../../../utils";
129
import { ProjectType } from "../../../../../utils/webview";
10+
import { VSCodeButton, VSCodeDataGrid, VSCodeDataGridRow } from "@vscode/webview-ui-toolkit/react";
11+
import SectionHeader from "./common/SectionHeader";
1312

1413
const ReferencedLibraries = (): JSX.Element => {
14+
15+
const [hoveredRow, setHoveredRow] = useState<string | null>(null);
16+
1517
const referencedLibraries: string[] = useSelector((state: any) => state.classpathConfig.referencedLibraries);
1618
const projectType: ProjectType = useSelector((state: any) => state.classpathConfig.projectType);
1719
const dispatch: Dispatch<any> = useDispatch();
@@ -40,42 +42,37 @@ const ReferencedLibraries = (): JSX.Element => {
4042
let referencedLibrariesSections: JSX.Element | JSX.Element[];
4143
if (referencedLibraries.length === 0) {
4244
referencedLibrariesSections = (
43-
<ListGroup.Item className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} list-row-body pl-0 py-0`}>
44-
<span className="ml-1"><em>No referenced libraries are configured.</em></span>
45-
</ListGroup.Item>
45+
<VSCodeDataGridRow className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} setting-section-grid-row`}>
46+
<span><em>No referenced libraries are configured.</em></span>
47+
</VSCodeDataGridRow>
4648
);
4749
} else {
4850
referencedLibrariesSections = referencedLibraries.map((library, index) => (
49-
<ListGroup.Item className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} list-row-body flex-vertical-center pl-0 py-0`} key={library}>
50-
<span className="ml-1">{library}</span>
51-
{projectType === ProjectType.UnmanagedFolder &&
52-
<span className="scale-up float-right">
53-
<a onClick={() => handleRemove(index)}>
54-
<Icon className="codicon cursor-pointer" icon={closeIcon} />
55-
</a>
56-
</span>
57-
}
58-
</ListGroup.Item>
51+
<VSCodeDataGridRow className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} setting-section-grid-row`} id={`library-${index}`} onMouseEnter={() => setHoveredRow(`library-${index}`)} onMouseLeave={() => setHoveredRow(null)} key={library}>
52+
<span>{library}</span>
53+
{hoveredRow === `library-${index}` && projectType === ProjectType.UnmanagedFolder && (
54+
<VSCodeButton appearance='icon' onClick={() => handleRemove(index)}>
55+
<span className="codicon codicon-close"></span>
56+
</VSCodeButton>
57+
)}
58+
</VSCodeDataGridRow>
5959
));
6060
}
6161

6262
return (
63-
<div>
64-
<div className="setting-section-header mb-1">
65-
<h4 className="mb-0">Referenced Libraries</h4>
66-
{projectType !== ProjectType.UnmanagedFolder &&
67-
<span className="ml-2">(Read-only)</span>
68-
}
69-
</div>
63+
<div className="setting-section">
64+
<SectionHeader title="Referenced Libraries" subTitle={projectType !== ProjectType.UnmanagedFolder ? "(Read-only)" : undefined} />
7065
<span className="setting-section-description">Specify referenced libraries of the project.</span>
71-
<ListGroup className="list mt-1">
72-
<ListGroup.Item className="list-row-header pr-2 pl-0 py-0">
73-
<span className="ml-1">Path</span>
74-
</ListGroup.Item>
75-
{referencedLibrariesSections}
76-
</ListGroup>
66+
<div className="setting-section-target">
67+
<VSCodeDataGrid>
68+
<VSCodeDataGridRow className="setting-section-grid-row" rowType="header">
69+
<span className=" setting-section-grid-row-header">Path</span>
70+
</VSCodeDataGridRow>
71+
{referencedLibrariesSections}
72+
</VSCodeDataGrid>
73+
</div>
7774
{projectType === ProjectType.UnmanagedFolder &&
78-
<a role="button" className="btn btn-action mt-2" onClick={() => handleAdd()}>Add</a>
75+
<VSCodeButton onClick={() => handleAdd()}>Add</VSCodeButton>
7976
}
8077
</div>
8178
);

src/classpath/assets/features/classpathConfiguration/components/Sources.tsx

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
import { Icon } from "@iconify/react";
5-
import closeIcon from "@iconify-icons/codicon/chrome-close";
6-
import React, { useEffect } from "react";
4+
import React, { useEffect, useState } from "react";
75
import { useSelector, useDispatch } from "react-redux";
8-
import { ListGroup } from "react-bootstrap";
96
import { Dispatch } from "@reduxjs/toolkit";
107
import { updateSource } from "../classpathConfigurationViewSlice";
118
import { onWillAddSourcePath, onWillRemoveSourcePath } from "../../../utils";
129
import { ProjectType } from "../../../../../utils/webview";
10+
import { VSCodeButton, VSCodeDataGrid, VSCodeDataGridRow } from "@vscode/webview-ui-toolkit/react";
11+
import SectionHeader from "./common/SectionHeader";
1312

1413
const Sources = (): JSX.Element => {
14+
15+
const [hoveredRow, setHoveredRow] = useState<string | null>(null);
16+
1517
const sources: string[] = useSelector((state: any) => state.classpathConfig.sources);
1618
const projectType: ProjectType = useSelector((state: any) => state.classpathConfig.projectType);
1719
const dispatch: Dispatch<any> = useDispatch();
@@ -47,42 +49,37 @@ const Sources = (): JSX.Element => {
4749
let sourceSections: JSX.Element | JSX.Element[];
4850
if (sources.length === 0) {
4951
sourceSections = (
50-
<ListGroup.Item className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} list-row-body pl-0 py-0`}>
51-
<span className="ml-1"><em>No source paths are configured.</em></span>
52-
</ListGroup.Item>
52+
<VSCodeDataGridRow className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} setting-section-grid-row`}>
53+
<span><em>No source paths are configured.</em></span>
54+
</VSCodeDataGridRow>
5355
);
5456
} else {
55-
sourceSections = sources.map((source) => (
56-
<ListGroup.Item className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} list-row-body pl-0 py-0`} key={source}>
57-
<span className="ml-1">{source}</span>
58-
{projectType === ProjectType.UnmanagedFolder &&
59-
<span className="scale-up float-right">
60-
<a onClick={() => handleRemove(source)}>
61-
<Icon className="codicon cursor-pointer" icon={closeIcon} />
62-
</a>
63-
</span>
64-
}
65-
</ListGroup.Item>
57+
sourceSections = sources.map((source, index) => (
58+
<VSCodeDataGridRow className={`${projectType !== ProjectType.UnmanagedFolder ? "inactive" : ""} setting-section-grid-row`} id={`sources-${index}`} onMouseEnter={() => setHoveredRow(`sources-${index}`)} onMouseLeave={() => setHoveredRow(null)} key={source}>
59+
<span>{source}</span>
60+
{hoveredRow === `sources-${index}` && projectType === ProjectType.UnmanagedFolder && (
61+
<VSCodeButton appearance='icon' onClick={() => handleRemove(source)}>
62+
<span className="codicon codicon-close"></span>
63+
</VSCodeButton>
64+
)}
65+
</VSCodeDataGridRow>
6666
));
6767
}
6868

6969
return (
70-
<div>
71-
<div className="setting-section-header mb-1">
72-
<h4 className="mb-0">Sources</h4>
73-
{projectType !== ProjectType.UnmanagedFolder &&
74-
<span className="ml-2">(Read-only)</span>
75-
}
76-
</div>
70+
<div className="setting-section">
71+
<SectionHeader title="Sources" subTitle={projectType !== ProjectType.UnmanagedFolder ? "(Read-only)" : undefined} />
7772
<span className="setting-section-description">Specify the source locations.</span>
78-
<ListGroup className="list mt-1">
79-
<ListGroup.Item className="list-row-header flex-vertical-center pr-2 pl-0 py-0">
80-
<span className="ml-1">Path</span>
81-
</ListGroup.Item>
82-
{sourceSections}
83-
</ListGroup>
73+
<div className="setting-section-target">
74+
<VSCodeDataGrid>
75+
<VSCodeDataGridRow className="setting-section-grid-row" rowType="header">
76+
<span className=" setting-section-grid-row-header">Path</span>
77+
</VSCodeDataGridRow>
78+
{sourceSections}
79+
</VSCodeDataGrid>
80+
</div>
8481
{projectType === ProjectType.UnmanagedFolder &&
85-
<a role="button" className="btn btn-action mt-2" onClick={() => handleAdd()}>Add</a>
82+
<VSCodeButton onClick={() => handleAdd()}>Add</VSCodeButton>
8683
}
8784
</div>
8885
);

0 commit comments

Comments
 (0)