Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import "@vscode-elements/elements/dist/vscode-table-row/index.js";
import "@vscode-elements/elements/dist/vscode-table-cell/index.js";


import { Dispatch, useEffect } from "react";
import { Dispatch, useCallback, useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateCompilerSettings, updateAvailableComplianceLevels } from "./compilerConfigurationViewSlice";
import { CompilerRequest } from "../../vscode/utils";
Expand Down Expand Up @@ -54,6 +54,10 @@ const CompilerConfigurationView = (): JSX.Element | null => {
Number(sourceLevel) > currentJdkComplianceLevel ||
Number(targetLevel) > currentJdkComplianceLevel;

const complianceRef = useRef<HTMLElement>(null);
const sourceRef = useRef<HTMLElement>(null);
const targetRef = useRef<HTMLElement>(null);

const onMessage = (event: any) => {
const message = event.data;
if (message.command === "compiler.onDidGetAvailableComplianceLevels") {
Expand All @@ -73,16 +77,68 @@ const CompilerConfigurationView = (): JSX.Element | null => {
}
}, []);

const jdkLevels = (selectedLevel: string, label: string, onClick: (value: string) => void) => {
// The vscode-single-select element renders its options in the shadow DOM and
// only emits a native `change` event on the host element, so the click handlers
// on the slotted vscode-option children never fire. Listen to `change` instead.
const useSelectChange = (ref: React.RefObject<HTMLElement | null>, onChange: (value: string) => void) => {
useEffect(() => {
const el = ref.current;
if (!el) {
return;
}
const handler = (e: Event) => {
const value = (e.target as any).value;
if (value) {
onChange(value);
}
};
el.addEventListener("change", handler);
return () => el.removeEventListener("change", handler);
}, [ref, onChange]);
};

const onChangeComplianceLevel = useCallback((value: string) => {
dispatch(updateCompilerSettings({ activeProjectIndex, complianceLevel: value }));
}, [dispatch, activeProjectIndex]);
const onChangeSourceLevel = useCallback((value: string) => {
dispatch(updateCompilerSettings({ activeProjectIndex, sourceLevel: value }));
}, [dispatch, activeProjectIndex]);
const onChangeTargetLevel = useCallback((value: string) => {
dispatch(updateCompilerSettings({ activeProjectIndex, targetLevel: value }));
}, [dispatch, activeProjectIndex]);

useSelectChange(complianceRef, onChangeComplianceLevel);
useSelectChange(sourceRef, onChangeSourceLevel);
useSelectChange(targetRef, onChangeTargetLevel);

// Keep the rendered selection in sync with the redux state. Set the value
// unconditionally (even when the level is "") so the dropdown always reflects
// state instead of keeping the previous project's selection.
useEffect(() => {
if (complianceRef.current) {
(complianceRef.current as any).value = complianceLevel;
}
}, [complianceLevel, availableComplianceLevels]);
useEffect(() => {
if (sourceRef.current) {
(sourceRef.current as any).value = sourceLevel;
}
}, [sourceLevel, availableComplianceLevels]);
useEffect(() => {
if (targetRef.current) {
(targetRef.current as any).value = targetLevel;
}
}, [targetLevel, availableComplianceLevels]);

const jdkLevels = (selectedLevel: string, label: string) => {
return availableComplianceLevels.map((level) => {

return (
<vscode-option
className="setting-section-option"
key={`${label}-${level}`}
value={level}
selected={level === selectedLevel}
onClick={() => onClick(level)}
selected={level === selectedLevel ? true : undefined}
>
<span>{level}</span>
</vscode-option>
Expand All @@ -104,27 +160,6 @@ const CompilerConfigurationView = (): JSX.Element | null => {
}));
};

const onClickComplianceLevel = (value: string) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
complianceLevel: value
}));
};

const onClickSourceLevel = (value: string) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
sourceLevel: value
}));
};

const onClickTargetLevel = (value: string) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
targetLevel: value
}));
};

const onClickGenerateDebugInfo = (e: any) => {
dispatch(updateCompilerSettings({
activeProjectIndex,
Expand Down Expand Up @@ -158,8 +193,8 @@ const CompilerConfigurationView = (): JSX.Element | null => {
<span>Bytecode version:</span>
</vscode-table-cell>
<vscode-table-cell className="flex-center pl-0 pr-0" >
<vscode-single-select value={complianceLevel}>
{jdkLevels(complianceLevel, "compliance", onClickComplianceLevel)}
<vscode-single-select ref={complianceRef}>
{jdkLevels(complianceLevel, "compliance")}
</vscode-single-select>
</vscode-table-cell>
</vscode-table-row>
Expand All @@ -168,8 +203,8 @@ const CompilerConfigurationView = (): JSX.Element | null => {
<span>Source compatibility:</span>
</vscode-table-cell>
<vscode-table-cell className="flex-center pl-0 pr-0" >
<vscode-single-select value={sourceLevel}>
{jdkLevels(sourceLevel, "source", onClickSourceLevel)}
<vscode-single-select ref={sourceRef}>
{jdkLevels(sourceLevel, "source")}
</vscode-single-select>
</vscode-table-cell>
</vscode-table-row>
Expand All @@ -178,8 +213,8 @@ const CompilerConfigurationView = (): JSX.Element | null => {
<span>Target compatibility:</span>
</vscode-table-cell>
<vscode-table-cell className="flex-center pl-0 pr-0" >
<vscode-single-select value={targetLevel}>
{jdkLevels(targetLevel, "target", onClickTargetLevel)}
<vscode-single-select ref={targetRef}>
{jdkLevels(targetLevel, "target")}
</vscode-single-select>
</vscode-table-cell>
</vscode-table-row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import "@vscode-elements/elements/dist/vscode-single-select/index.js";
import "@vscode-elements/elements/dist/vscode-option/index.js";

import { useEffect } from "react";
import { useCallback, useEffect, useRef } from "react";
import { useSelector, useDispatch } from "react-redux";
import { ProjectInfo } from "../../../../types";
import { Dispatch } from "@reduxjs/toolkit";
Expand All @@ -17,10 +17,37 @@ const ProjectSelector = (): JSX.Element | null => {
const projects: ProjectInfo[] = useSelector((state: any) => state.commonConfig.data.projects);

const dispatch: Dispatch<any> = useDispatch();
const selectRef = useRef<HTMLElement>(null);

const handleActiveProjectChange = (index: number) => {
const handleActiveProjectChange = useCallback((index: number) => {
dispatch(activeProjectChange(index));
};
}, [dispatch]);

// The vscode-single-select element renders its options in the shadow DOM and
// only emits a native `change` event on the host element, so the click handlers
// on the slotted vscode-option children never fire. Listen to `change` instead.
useEffect(() => {
const el = selectRef.current;
if (!el) {
return;
}
const onChange = (e: Event) => {
const index = (e.target as any).selectedIndex;
if (typeof index === "number" && index >= 0) {
handleActiveProjectChange(index);
}
};
el.addEventListener("change", onChange);
return () => el.removeEventListener("change", onChange);
}, [handleActiveProjectChange]);

// Keep the rendered selection in sync with the active project index.
useEffect(() => {
const el = selectRef.current;
if (el && projects.length > 0) {
(el as any).selectedIndex = activeProjectIndex;
}
}, [activeProjectIndex, projects]);

useEffect(() => {
if (projects.length === 0) {
Expand All @@ -38,7 +65,12 @@ const ProjectSelector = (): JSX.Element | null => {
}

return (
<vscode-option className="setting-section-option" key={project.rootPath} onClick={() => handleActiveProjectChange(index)}>
<vscode-option
className="setting-section-option"
key={project.rootPath}
value={project.rootPath}
selected={index === activeProjectIndex ? true : undefined}
>
{project.name}
</vscode-option>
);
Expand All @@ -48,7 +80,7 @@ const ProjectSelector = (): JSX.Element | null => {
<div id="project-selector" className="setting-section">
<div className="flex-center mt-2 mb-2">
<span className="setting-section-description ml-1 mr-1">Project:</span>
<vscode-single-select className="setting-section-dropdown">
<vscode-single-select ref={selectRef} className="setting-section-dropdown">
{projectSelections}
</vscode-single-select>
</div>
Expand Down
Loading