Skip to content

Commit

Permalink
HPCC-27646 Create theme editor
Browse files Browse the repository at this point in the history
Signed-off-by: Jeremy Clements <[email protected]>
  • Loading branch information
jeclrsg committed Mar 6, 2024
1 parent 8c8662c commit 8d43ba4
Show file tree
Hide file tree
Showing 19 changed files with 2,169 additions and 96 deletions.
5 changes: 0 additions & 5 deletions esp/src/eclwatch/css/hpcc.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ body {
padding: 0;
}

button:hover {
color: rgb(0, 0, 0);
background-color: rgb(175, 217, 255);
}

h1 {
font-size: 1.5em;
}
Expand Down
2 changes: 1 addition & 1 deletion esp/src/eclwatch/templates/HPCCPlatformWidget.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
<div data-dojo-type="hpcc.TableContainer">
<input id="${id}EnvironmentTextCB" title="${i18n.EnableBannerText}" data-dojo-type="dijit.form.CheckBox" />
<input id="${id}EnvironmentText" title="${i18n.NameOfEnvironment}" data-dojo-props="placeHolder: '${i18n.NameOfEnvironment}', trim: true" data-dojo-type="dijit.form.TextBox" />
<input id="${id}ToolbarColor" title="${i18n.BannerColor}:" style="width:100%;" value="red" data-dojo-props="trim: true" data-dojo-type="HPCCColorPicker" />
<input id="${id}ToolbarColor" title="${i18n.ToolbarColor}:" style="width:100%;" value="red" data-dojo-props="trim: true" data-dojo-type="HPCCColorPicker" />
</div>
</div>
</div>
Expand Down
11 changes: 10 additions & 1 deletion esp/src/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 12 additions & 7 deletions esp/src/src-react/components/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { IconButton, IContextualMenuItem, INavLink, INavLinkGroup, Link, mergeSt
import { useConst } from "@fluentui/react-hooks";
import nlsHPCC from "src/nlsHPCC";
import { hasLogAccess } from "src/ESPLog";
import { containerized, bare_metal } from "src/BuildInfo";
import { cmake_build_type, containerized, bare_metal } from "src/BuildInfo";
import { MainNav, routes } from "../routes";
import { useFavorite, useFavorites, useHistory } from "../hooks/favorite";
import { useUserTheme } from "../hooks/theme";
import { usePivotItemDisable } from "../layouts/pivot";
import { Breadcrumbs } from "./Breadcrumbs";
import { Settings } from "./Settings";

// Top Level Nav ---
function navLinkGroups(): INavLinkGroup[] {
Expand Down Expand Up @@ -98,30 +99,34 @@ export const MainNavigation: React.FunctionComponent<MainNavigationProps> = ({
hashPath
}) => {

const menu = useConst(() => [...navLinkGroups()]);
const { theme, setTheme, isDark } = useUserTheme();
const menu = useConst([...navLinkGroups()]);
const { theme, setThemeDark, isDark } = useUserTheme();

const [showSettings, setShowSettings] = React.useState(false);

const selKey = React.useMemo(() => {
return navSelectedKey(hashPath);
}, [hashPath]);

return <Stack verticalAlign="space-between" styles={{ root: { width: `${FIXED_WIDTH}px`, height: "100%", position: "relative", backgroundColor: theme.palette.themeLighterAlt } }}>
return <Stack verticalAlign="space-between" styles={{ root: { width: `${FIXED_WIDTH}px`, height: "100%", position: "relative", backgroundColor: theme?.palette?.themeLighterAlt ?? "white" } }}>
<Stack.Item>
<Nav selectedKey={selKey} groups={menu} />
</Stack.Item>
<Stack.Item>
<IconButton
iconProps={{ iconName: isDark ? "Sunny" : "ClearNight" }}
onClick={() => {
setTheme(isDark ? "light" : "dark");
setThemeDark(isDark ? "light" : "dark");
const themeChangeEvent = new CustomEvent("eclwatch-theme-toggle", {
detail: { dark: !isDark }
});
document.dispatchEvent(themeChangeEvent);
}}
/>
{/* Disable Theme editor button for launch of 9.0 */}
{/* <IconButton iconProps={{ iconName: "Equalizer" }} onClick={() => { }} /> */}
{cmake_build_type === "Debug" &&
<IconButton iconProps={{ iconName: "Equalizer" }} onClick={() => setShowSettings(true)} />
}
<Settings show={showSettings} onClose={() => setShowSettings(false)} />
</Stack.Item>
</Stack>;
};
Expand Down
80 changes: 80 additions & 0 deletions esp/src/src-react/components/Settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as React from "react";
import { DefaultButton, Pivot, PivotItem, PrimaryButton } from "@fluentui/react";
import { FluentProvider } from "@fluentui/react-components";
import { ThemeEditor } from "./ThemeEditor";
import { useUserTheme } from "../hooks/theme";
import { MessageBox } from "../layouts/MessageBox";
import nlsHPCC from "src/nlsHPCC";

interface SettingsProps {
show?: boolean;
onClose?: () => void;
}

export const Settings: React.FunctionComponent<SettingsProps> = ({
show = false,
onClose = () => { }
}) => {

const { themeV9, primaryColor, setPrimaryColor, hueTorsion, setHueTorsion, vibrancy, setVibrancy, resetTheme } = useUserTheme();
const [previousColor, setPreviousColor] = React.useState(primaryColor);
const [previousHueTorsion, setPreviousHueTorsion] = React.useState(hueTorsion);
const [previousVibrancy, setPreviousVibrancy] = React.useState(vibrancy);
const [activePivot, setActivePivot] = React.useState("theme");

React.useEffect(() => {
// cache the previous color at dialog open, used to reset upon close
if (show) {
setPreviousColor(primaryColor);
setPreviousHueTorsion(hueTorsion);
setPreviousVibrancy(vibrancy);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [show]);

return <MessageBox show={show} setShow={onClose} blocking={true} modeless={false} title={nlsHPCC.Settings} minWidth={400}
footer={<>
<PrimaryButton onClick={() => {
if (onClose) {
onClose();
}
}} text={nlsHPCC.OK} />
<DefaultButton onClick={() => {
switch (activePivot) {
case "theme":
default:
setPrimaryColor(previousColor);
setHueTorsion(previousHueTorsion);
setVibrancy(previousVibrancy);
break;

}
if (onClose) {
onClose();
}
}} text={nlsHPCC.Cancel} />
<DefaultButton onClick={() => {
switch (activePivot) {
case "theme":
default:
resetTheme();
break;

}
if (onClose) {
onClose();
}
}} text={nlsHPCC.Reset} />
</>}>
<FluentProvider theme={themeV9}>
<Pivot onLinkClick={item => {
setActivePivot(item.props.itemKey);
}}>
<PivotItem itemKey="theme" headerText={nlsHPCC.Theme}>
<ThemeEditor />
</PivotItem>
</Pivot>
</FluentProvider >
</MessageBox>;

};
141 changes: 141 additions & 0 deletions esp/src/src-react/components/ThemeEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import * as React from "react";
import { Callout, ColorPicker, getColorFromString, IColor, Label, mergeStyles, mergeStyleSets, Stack, TextField } from "@fluentui/react";
import { Slider, tokens } from "@fluentui/react-components";
import { useUserTheme } from "../hooks/theme";
import nlsHPCC from "src/nlsHPCC";

const colorBoxClassName = mergeStyles({
width: 20,
height: 20,
display: "inline-block",
position: "absolute",
left: 5,
top: 5,
border: "1px solid black",
flexShrink: 0,
});

const textBoxClassName = mergeStyles({
width: 100,
});

const colorPanelClassName = mergeStyles({
position: "relative",
});

interface ThemeEditorColorPickerProps {
color: IColor;
onColorChange: (color: IColor | undefined) => void;
label: string;
}

export const ThemeEditorColorPicker: React.FunctionComponent<ThemeEditorColorPickerProps> = ({
color,
onColorChange,
label
}) => {
const [pickerColor, setPickerColor] = React.useState(color);
const _colorPickerRef = React.useRef(null);
const [editingColorStr, setEditingColorStr] = React.useState(pickerColor?.str ?? "");
const [isColorPickerVisible, setIsColorPickerVisible] = React.useState<boolean>(false);

const _onTextFieldValueChange = React.useCallback((ev: any, newValue: string | undefined) => {
const newColor = getColorFromString(newValue);
if (newColor) {
onColorChange(newColor);
setEditingColorStr(newColor.str);
} else {
setEditingColorStr(newValue);
}
}, [onColorChange, setEditingColorStr]);

React.useEffect(() => {
setPickerColor(color);
}, [color]);

React.useEffect(() => {
setEditingColorStr(pickerColor?.str);
}, [pickerColor]);

return <div>
<Stack horizontal horizontalAlign={"space-between"} tokens={{ childrenGap: 20 }}>
<Label>{label}</Label>
<Stack horizontal className={colorPanelClassName} tokens={{ childrenGap: 35 }}>
<div
ref={_colorPickerRef}
id="colorbox"
className={colorBoxClassName}
style={{ backgroundColor: color?.str }}
onClick={() => setIsColorPickerVisible(true)}
/>
<TextField
id="textfield"
className={textBoxClassName}
value={editingColorStr}
onChange={_onTextFieldValueChange}
/>
</Stack>
</Stack>
{isColorPickerVisible && (
<Callout
gapSpace={10}
target={_colorPickerRef.current}
setInitialFocus={true}
onDismiss={() => setIsColorPickerVisible(false)}
>
<ColorPicker color={pickerColor} onChange={(evt, color) => onColorChange(color)} alphaType={"none"} />
</Callout>
)}
</div>;
};

interface ThemeEditorProps {
}

export const ThemeEditor: React.FunctionComponent<ThemeEditorProps> = () => {

const { primaryColor, setPrimaryColor, hueTorsion, setHueTorsion, vibrancy, setVibrancy } = useUserTheme();

const handleHueTorsionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const hueTorsion = parseInt(e.target.value || "0", 10);
setHueTorsion(hueTorsion);
};

const handleVibrancyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const vibrancy = parseInt(e.target.value || "0", 10);
setVibrancy(vibrancy);
};

const sliderStyles = mergeStyleSets({
root: {
backgroundColor: tokens.colorNeutralBackground3,
},
wrapper: {
display: "flex",
justifyContent: "space-between",
marginTop: 6
},
});

return <div style={{ minHeight: "208px", paddingTop: "32px" }}>
<ThemeEditorColorPicker
color={getColorFromString(primaryColor)}
onColorChange={(newColor: IColor | undefined) => {
setPrimaryColor(newColor.str);
}}
label={nlsHPCC.Theme_PrimaryColor}
/>

<div className={sliderStyles.wrapper}>
<Label style={{ width: 80 }}>{nlsHPCC.Theme_HueTorsion}</Label>
<Slider size="small" min={-50} max={50} value={hueTorsion} onChange={handleHueTorsionChange} />
<TextField type="number" min={-50} max={50} value={hueTorsion?.toString() ?? ""} onChange={handleHueTorsionChange} />
</div>
<div className={sliderStyles.wrapper}>
<Label style={{ width: 80 }}>{nlsHPCC.Theme_Vibrancy}</Label>
<Slider size="small" min={-50} max={50} value={vibrancy} onChange={handleVibrancyChange} />
<TextField type="number" min={-50} max={50} value={vibrancy?.toString() ?? ""} onChange={handleVibrancyChange} />
</div>
</div>;

};
Loading

0 comments on commit 8d43ba4

Please sign in to comment.