Handle `GroupMenu` (#792)
This commit is contained in:
parent
ef7ce25c03
commit
7582896174
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue