Handle `GroupMenu` (#792)

This commit is contained in:
Piotr Rogowski 2022-10-13 18:46:26 +02:00 committed by GitHub
parent ef7ce25c03
commit 7582896174
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 565 additions and 543 deletions

928
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -21,11 +21,11 @@
"dependencies": {
"@hyper-tuner/ini": "^0.4.0",
"@hyper-tuner/types": "^0.4.0",
"@reduxjs/toolkit": "^1.8.5",
"@sentry/react": "^7.14.1",
"@sentry/tracing": "^7.14.1",
"antd": "^4.23.4",
"appwrite": "^9.0.2",
"@reduxjs/toolkit": "^1.8.6",
"@sentry/react": "^7.15.0",
"@sentry/tracing": "^7.15.0",
"antd": "^4.23.5",
"appwrite": "9.0.2",
"kbar": "^0.1.0-beta.36",
"lodash.debounce": "^4.0.8",
"mlg-converter": "^0.5.1",
@ -37,30 +37,30 @@
"react-markdown": "^8.0.3",
"react-perfect-scrollbar": "^1.5.8",
"react-redux": "^8.0.4",
"react-router-dom": "^6.4.1",
"react-router-dom": "^6.4.2",
"uplot": "^1.6.22",
"uplot-react": "^1.1.1",
"vite": "^3.1.4"
"vite": "^3.1.8"
},
"devDependencies": {
"@hyper-tuner/eslint-config": "^0.1.6",
"@types/lodash.debounce": "^4.0.7",
"@types/node": "^18.8.0",
"@types/node": "^18.8.5",
"@types/pako": "^2.0.0",
"@types/react": "^18.0.21",
"@types/react-dom": "^18.0.6",
"@types/react-redux": "^7.1.24",
"@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^5.38.1",
"@typescript-eslint/parser": "^5.38.1",
"@typescript-eslint/eslint-plugin": "^5.40.0",
"@typescript-eslint/parser": "^5.40.0",
"@vitejs/plugin-react": "^2.1.0",
"eslint": "^8.24.0",
"eslint": "^8.25.0",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-modules-newline": "^0.0.6",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react": "^7.31.10",
"eslint-plugin-react-hooks": "^4.6.0",
"less": "^4.1.3",
"prettier": "^2.7.1",

View File

@ -39,6 +39,10 @@ import {
Config as ConfigType,
Tune as TuneType,
Menus as MenusType,
Menu as MenuType,
SubMenu as SubMenuType,
GroupMenu as GroupMenuType,
GroupChildMenu as GroupChildMenuType,
} from '@hyper-tuner/types';
import { Routes } from '../routes';
import { useAuth } from '../contexts/AuthContext';
@ -76,7 +80,7 @@ const mapStateToProps = (state: AppState) => ({
interface CommandPaletteProps {
config: ConfigType | null;
tune: TuneType | null;
tune: TuneType | null;
navigation: NavigationState;
// eslint-disable-next-line react/no-unused-prop-types
children?: ReactNode;
@ -260,29 +264,42 @@ const ActionsProvider = (props: CommandPaletteProps) => {
},
];
Object.keys(types).forEach((menuName: string) => {
if (SKIP_MENUS.includes(menuName)) {
const mapSubMenuItems = (rootMenuName: string, rootMenu: MenuType, subMenus: { [name: string]: SubMenuType | GroupMenuType | GroupChildMenuType }) => {
Object
.keys(subMenus)
.forEach((subMenuName: string) => {
if (SKIP_SUB_MENUS.includes(`${rootMenuName}/${subMenuName}`)) {
return;
}
if (subMenuName === 'std_separator') {
return;
}
const subMenu = subMenus[subMenuName];
if ((subMenu as GroupMenuType).type === 'groupMenu') {
mapSubMenuItems(rootMenuName, rootMenu, (subMenu as GroupMenuType).groupChildMenus);
return;
}
newActions.push({
id: buildUrl(navigation.tuneId!, rootMenuName, subMenuName),
section: rootMenu.title,
name: subMenu.title,
icon: <Icon name={subMenuName} />,
perform: () => navigate(buildUrl(navigation.tuneId!, rootMenuName, subMenuName)),
});
});
};
Object.keys(types).forEach((rootMenuName: string) => {
if (SKIP_MENUS.includes(rootMenuName)) {
return;
}
Object.keys(types[menuName].subMenus).forEach((subMenuName: string) => {
if (subMenuName === 'std_separator') {
return;
}
if (SKIP_SUB_MENUS.includes(`${menuName}/${subMenuName}`)) {
return;
}
const subMenu = types[menuName].subMenus[subMenuName];
newActions.push({
id: buildUrl(navigation.tuneId!, menuName, subMenuName),
section: types[menuName].title,
name: subMenu.title,
icon: <Icon name={subMenuName} />,
perform: () => navigate(buildUrl(navigation.tuneId!, menuName, subMenuName)),
});
});
mapSubMenuItems(rootMenuName, types[rootMenuName], types[rootMenuName].subMenus);
});
return newActions;

View File

@ -206,6 +206,7 @@ const Dialog = ({
return;
}
// TODO: Sentry?
console.info('Unable to resolve panel:', panelName);
return;
@ -277,13 +278,15 @@ const Dialog = ({
const help = config.help[field.name];
let input;
let enabled = true;
const fieldKey = `${panel.name}-${field.title}`;
if (field.condition) {
// TODO: optimize it
enabled = evaluateExpression(field.condition, tune.constants, config);
}
if (field.name === '_fieldText_' && enabled) {
return <TextField key={`${panel.name}-${field.title}`} title={field.title} />;
return <TextField key={fieldKey} title={field.title} />;
}
if (!tuneField) {
@ -319,7 +322,7 @@ const Dialog = ({
return (
<Form.Item
key={field.name}
key={fieldKey}
label={
<Space>
{field.title}

View File

@ -40,9 +40,9 @@ const SmartNumber = ({
step={10**-digits}
disabled={disabled}
marks={sliderMarks}
tipFormatter={(val) => `${val}${units}`}
// tooltipVisible
// tooltipPlacement="bottom"
tooltip={{
formatter: (val) => `${val}${units}`,
}}
/>
);
}

View File

@ -19,6 +19,9 @@ import {
Config as ConfigType,
Menus as MenusType,
Tune as TuneType,
SubMenu as SubMenuType,
GroupMenu as GroupMenuType,
GroupChildMenu as GroupChildMenuType,
} from '@hyper-tuner/types';
import store from '../../store';
import Icon from '../SideBar/Icon';
@ -78,39 +81,59 @@ const SideBar = ({ config, tune, ui, navigation, matchedPath }: SideBarProps) =>
const [menus, setMenus] = useState<ItemType[]>([]);
const navigate = useNavigate();
const menusList = useCallback((types: MenusType): ItemType[] => (
Object.keys(types).map((menuName: string) => {
const mapSubMenuItems = useCallback((rootMenuName: string, subMenus: { [name: string]: SubMenuType | GroupMenuType | GroupChildMenuType }): ItemType[] => {
const items: ItemType[] = [];
Object
.keys(subMenus)
.forEach((subMenuName: string) => {
if (SKIP_SUB_MENUS.includes(`${rootMenuName}/${subMenuName}`)) {
return;
}
if (subMenuName === 'std_separator') {
items.push({
type: 'divider',
});
return;
}
const subMenu = subMenus[subMenuName];
if ((subMenu as GroupMenuType).type === 'groupMenu') {
items.push(...mapSubMenuItems(rootMenuName, (subMenu as GroupMenuType).groupChildMenus));
return;
}
items.push({
key: buildUrl(navigation.tuneId!, rootMenuName, subMenuName),
icon: <Icon name={subMenuName} />,
label: subMenu.title,
onClick: () => navigate(buildUrl(navigation.tuneId!, rootMenuName, subMenuName)),
});
});
return items;
}, [navigate, navigation.tuneId]);
const menusList = useCallback((menusObject: MenusType): ItemType[] => (
Object.keys(menusObject).map((menuName: string) => {
if (SKIP_MENUS.includes(menuName)) {
return null;
}
const subMenuItems: ItemType[] = Object.keys(types[menuName].subMenus).map((subMenuName: string) => {
if (subMenuName === 'std_separator') {
return { type: 'divider' };
}
if (SKIP_SUB_MENUS.includes(`${menuName}/${subMenuName}`)) {
return null;
}
const subMenu = types[menuName].subMenus[subMenuName];
return {
key: buildUrl(navigation.tuneId!, menuName, subMenuName),
icon: <Icon name={subMenuName} />,
label: subMenu.title,
onClick: () => navigate(buildUrl(navigation.tuneId!, menuName, subMenuName)),
};
});
const subMenuItems: ItemType[] = mapSubMenuItems(menuName, menusObject[menuName].subMenus);
return {
key: `/${menuName}`,
icon: <Icon name={menuName} />,
label: types[menuName].title,
label: menusObject[menuName].title,
children: subMenuItems,
};
})
), [navigate, navigation.tuneId]);
), [mapSubMenuItems]);
useEffect(() => {
if (tune && config && Object.keys(tune.constants).length) {

View File

@ -5,6 +5,7 @@ import {
SimpleConstant as SimpleConstantType,
TuneConstants as TuneConstantsType,
} from '@hyper-tuner/types';
import * as Sentry from '@sentry/browser';
export const isExpression = (val: any) => `${val}`.startsWith('{') && `${val}`.endsWith('}');
@ -38,8 +39,11 @@ export const prepareConstDeclarations = (tuneConstants: TuneConstantsType, confi
val = `'${val}'`;
}
return `const ${constName} = ${val};`;
})
// some names may have invalid characters, we can fix it or skip it
const name = constName.replace('-', '_');
return `const ${name} = ${val};`;
}).filter((val) => val !== null)
);
const prepareChannelsDeclarations = (configOutputChannels: OutputChannelsType) => (
@ -76,7 +80,6 @@ export const evaluateExpression = (expression: string, tuneConstants: TuneConsta
boardFuelOutputs: 4,
boardIgnOutputs: 4,
};
const coolantRaw = 21;
const iatRaw = 21;
const fuelTempRaw = 21;
@ -96,13 +99,19 @@ export const evaluateExpression = (expression: string, tuneConstants: TuneConsta
const baro = 0;
const vss = 0;
const CLIdleTarget = 0;
const fuelPressure = 0;
const oilPressure = 0;
const halfSync = 0;
const sync = 0;
${constDeclarations.join('')}
${channelsDeclarations.join('')}
${stripExpression(expression)};
`);
} catch (error) {
console.info('Condition evaluation failed with:', (error as Error).message);
const msg = `Condition evaluation failed with: ${(error as Error).message}`;
console.warn(msg);
Sentry.captureMessage(msg);
}
return undefined;