routes erweitert
This commit is contained in:
8
electron/.idea/.gitignore
generated
vendored
Normal file
8
electron/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
57
electron/.idea/codeStyles/Project.xml
generated
Normal file
57
electron/.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,57 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
electron/.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
electron/.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
12
electron/.idea/electron.iml
generated
Normal file
12
electron/.idea/electron.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
electron/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
electron/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
8
electron/.idea/modules.xml
generated
Normal file
8
electron/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/electron.iml" filepath="$PROJECT_DIR$/.idea/electron.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
electron/.idea/prettier.xml
generated
Normal file
6
electron/.idea/prettier.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
</component>
|
||||
</project>
|
||||
6
electron/.idea/vcs.xml
generated
Normal file
6
electron/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 886 KiB |
42
electron/src/client/useMachines.tsx
Normal file
42
electron/src/client/useMachines.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
type UseMachine = {
|
||||
machine_identification_unique: MachineIdentificationUnique;
|
||||
name: MachineProperties["name"];
|
||||
version: MachineProperties["version"];
|
||||
slug: MachineProperties["slug"];
|
||||
vendor: VendorProperties["name"];
|
||||
icon: MachineProperties["icon"];
|
||||
};
|
||||
|
||||
// returns only valid machines
|
||||
export function useMachines(): UseMachine[] {
|
||||
const { machines } = useMainNamespace();
|
||||
|
||||
if (machines?.data)
|
||||
return (
|
||||
machines.data.machines
|
||||
.filter((machine) => machine.error === null)
|
||||
.map((machine) => {
|
||||
const machinePreset = getMachineProperties(
|
||||
machine.machine_identification_unique.machine_identification,
|
||||
);
|
||||
const vendorPreset = getVendorProperties(
|
||||
machinePreset!.machine_identification.vendor,
|
||||
);
|
||||
if (!machinePreset || !vendorPreset) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
machine_identification_unique:
|
||||
machine.machine_identification_unique,
|
||||
name: machinePreset.name,
|
||||
version: machinePreset.version,
|
||||
slug: machinePreset.slug,
|
||||
vendor: vendorPreset.name,
|
||||
icon: machinePreset.icon,
|
||||
};
|
||||
})
|
||||
.filter((machine) => machine !== undefined) || []
|
||||
);
|
||||
|
||||
return [];
|
||||
}
|
||||
21
electron/src/components/BackButton.tsx
Normal file
21
electron/src/components/BackButton.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
import React from "react";
|
||||
import { Button } from "./ui/button";
|
||||
import { useRouter } from "@tanstack/react-router";
|
||||
import { Icon } from "./Icon";
|
||||
|
||||
export function BackButton() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
router.history.back();
|
||||
}}
|
||||
className="h-full bg-neutral-100 text-black"
|
||||
variant="ghost"
|
||||
>
|
||||
<Icon name="lu:ChevronLeft" />
|
||||
Back
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
37
electron/src/components/FullscreenButton.tsx
Normal file
37
electron/src/components/FullscreenButton.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { Icon } from "@/components/Icon";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useEffectAsync } from "@/lib/useEffectAsync";
|
||||
|
||||
export function FullscreenButton() {
|
||||
const [isFullscreen, setIsFullscreen] = React.useState(false);
|
||||
|
||||
// We initalize button as fullscreen true if we are on QiTechOS
|
||||
useEffectAsync(async () => {
|
||||
const envInfo = await window.environment.getInfo();
|
||||
if (envInfo.qitechOs) {
|
||||
setIsFullscreen(true);
|
||||
} else {
|
||||
setIsFullscreen(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (isFullscreen) {
|
||||
window.electronWindow.fullscreen(false);
|
||||
setIsFullscreen(false);
|
||||
} else {
|
||||
window.electronWindow.fullscreen(true);
|
||||
setIsFullscreen(true);
|
||||
}
|
||||
}}
|
||||
className="px-6 py-7"
|
||||
variant="ghost"
|
||||
>
|
||||
<Icon name={isFullscreen ? "lu:Minimize" : "lu:Maximize"} />
|
||||
{isFullscreen ? null : "Fullscreen"}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
14
electron/src/components/Page.tsx
Normal file
14
electron/src/components/Page.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function Page({ children, className }: Props) {
|
||||
return (
|
||||
<div className={`flex w-full flex-col gap-6 p-6 ${className || ""}`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
electron/src/components/SectionTitle.tsx
Normal file
15
electron/src/components/SectionTitle.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export function SectionTitle({children, title}: Props) {
|
||||
return (
|
||||
<div className="flex gap-4">
|
||||
<h1 className="text-2xl">{title}</h1>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import {Icon, IconName} from "./Icon";
|
||||
import { useOnSubpath } from "@/lib/useOnSubpath";
|
||||
import { OutsideCorner } from "./OutsideCorner";
|
||||
import {Link} from "@tanstack/react-router";
|
||||
import React from "react";
|
||||
import {Link, Outlet} from "@tanstack/react-router";
|
||||
import React, { Fragment } from "react";
|
||||
|
||||
type SideBarItemContent = {
|
||||
link: string;
|
||||
@@ -26,7 +26,7 @@ export function SidebarItem({
|
||||
return (
|
||||
<Link
|
||||
to={link}
|
||||
classname={`relative h-18 w-full ${isActive ? "pl-2": "px-2"}`}
|
||||
className={`relative h-18 w-full ${isActive ? "pl-2": "px-2"}`}
|
||||
>
|
||||
<div className={`text-md relative z-10 flex h-full w-full items-center justify-center gap-2 ${isActive ? "rounded-l-lg bg-white pr-2": "rounded-lg bg-neutral-100"}`}>
|
||||
{icon && <Icon name={icon} />}
|
||||
@@ -37,8 +37,57 @@ export function SidebarItem({
|
||||
);
|
||||
}
|
||||
|
||||
const SidebarlessWidthContext = React.createContext<number | null>(null);
|
||||
|
||||
export function useSidebarlessWidth() {
|
||||
const width = React.useContext(SidebarlessWidthContext);
|
||||
if(width === null) {
|
||||
throw new Error("useWidth must be used within a WidthProvider");
|
||||
}
|
||||
return width;
|
||||
|
||||
}
|
||||
|
||||
export function SidebarLayout() {
|
||||
return (
|
||||
<h1>Hello World</h1>
|
||||
const [contentWidth, setContentWidth] = React.useState<number>(0);
|
||||
|
||||
|
||||
const outletRef = React.useRef<HTMLDivElement>(null);
|
||||
React.useEffect(() => {
|
||||
if (outletRef.current) {
|
||||
// Set initial width
|
||||
setContentWidth(outletRef.current.offsetWidth);
|
||||
|
||||
// Create a ResizeObserver to track width changes
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
setContentWidth(entry.contentRect.width);
|
||||
}
|
||||
});
|
||||
|
||||
resizeObserver.observe(outletRef.current);
|
||||
|
||||
// Clean up observer on unmount
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
return (
|
||||
<SidebarlessWidthContext.Provider value={contentWidth}>
|
||||
<div className="fixed flex h-full w-48 flex-col bg-neutral-200">
|
||||
<div className="flex h-18 flex-col items-center justify-center gap-0 pt-2">
|
||||
<div className="line-clamp-none text-3xl">Maxlan</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Fragment key="localhost">
|
||||
<SidebarItem link="localhost" title="Cool" activeLink="dada" isFirst={true} />
|
||||
</Fragment>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-48 " ref={outletRef}>
|
||||
<Outlet />
|
||||
</div>
|
||||
</SidebarlessWidthContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Icon, IconName } from "@/components/Icon";
|
||||
import { useOnSubpath } from "@/lib/useOnSubpath";
|
||||
import { Link, Outlet } from "@tanstack/react-router";
|
||||
import { OutsideCorner } from "@/components/OutsideCorner";
|
||||
import React, {Fragment} from "react";
|
||||
import { BackButton } from "@/components/BackButton";
|
||||
import { FullscreenButton } from "@/components/FullscreenButton";
|
||||
import { useSidebarlessWidth } from "@/components/SidebarLayout";
|
||||
|
||||
export type TopbarItemContent ={
|
||||
link: string;
|
||||
activeLink: string;
|
||||
icon?: IconName;
|
||||
title: string;
|
||||
};
|
||||
|
||||
type TopbarItemProps = TopbarItemContent;
|
||||
export function TopbarItem({ icon, title, link, activeLink }: TopbarItemProps) {
|
||||
const isActive = useOnSubpath(activeLink);
|
||||
return (
|
||||
<Link className={`relative h-full ${isActive ? "" : "pb-2"}`} to={link}>
|
||||
<div
|
||||
className={`text-md relative z-10 flex h-full items-center justify-center gap-2 px-6 ${
|
||||
isActive ? "rounded-t-lg bg-white pb-2" : "rounded-lg bg-neutral-100"
|
||||
}`}
|
||||
>
|
||||
{icon && <Icon name={icon} />}
|
||||
{title}
|
||||
</div>
|
||||
<OutsideCorner bottomLeft={isActive} bottomRight={isActive} />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
type TopbarProps = {
|
||||
items: TopbarItemContent[];
|
||||
pathname: string;
|
||||
};
|
||||
|
||||
export function Topbar({ items, pathname }: TopbarProps) {
|
||||
const sidebarlessWidth = useSidebarlessWidth();
|
||||
return (
|
||||
<div className="flex h-screen w-full flex-col">
|
||||
<div
|
||||
className="fixed top-0 flex h-18 gap-2 bg-neutral-200 pt-2 pr-2"
|
||||
// 50 is below popup dialogs
|
||||
style={{ zIndex: 50, width: sidebarlessWidth }}
|
||||
>
|
||||
<div className="flexflex-col z-10 pb-2">
|
||||
<BackButton />
|
||||
</div>
|
||||
{items.map((item, index) => {
|
||||
let link = item.link;
|
||||
if (!item.link.startsWith("/")) {
|
||||
link = pathname + "/" + item.link;
|
||||
}
|
||||
let activelink = item.activeLink;
|
||||
if (!item.activeLink.startsWith("/")) {
|
||||
activelink = pathname + "/" + item.activeLink;
|
||||
}
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<TopbarItem {...item} link={link} activeLink={activelink} />
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
<div className="flex-grow" />
|
||||
<FullscreenButton />
|
||||
</div>
|
||||
|
||||
<div className="mt-18 h-full overflow-y-auto">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
22
electron/src/lib/useEffectAsync.tsx
Normal file
22
electron/src/lib/useEffectAsync.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
export function useEffectAsync(
|
||||
effect: () => Promise<void>,
|
||||
deps: React.DependencyList,
|
||||
): void {
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
const executeEffect = async () => {
|
||||
if (isMounted) {
|
||||
await effect();
|
||||
}
|
||||
};
|
||||
|
||||
executeEffect();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, deps);
|
||||
}
|
||||
@@ -8,6 +8,6 @@ declare module "@tanstack/react-router" {
|
||||
}
|
||||
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ["/_sidebar/"],
|
||||
initialEntries: ["/_sidebar/setup/ethercat"],
|
||||
});
|
||||
export const router = createRouter({ routeTree: rootTree, history: history });
|
||||
|
||||
@@ -2,6 +2,8 @@ import { createRoute } from "@tanstack/react-router";
|
||||
import { RootRoute } from "./__root";
|
||||
import { SidebarLayout } from "@/components/SidebarLayout";
|
||||
import React from "react";
|
||||
import { SetupPage } from "@/setup/SetupPage";
|
||||
import { EthercatPage } from "@/setup/EthercatPage";
|
||||
|
||||
|
||||
// TODO: Steps to add a new route:
|
||||
@@ -29,4 +31,23 @@ export const sidebarRoute = createRoute({
|
||||
component:() => <SidebarLayout />,
|
||||
});
|
||||
|
||||
export const rootTree = RootRoute.addChildren([sidebarRoute]);
|
||||
export const setupRoute = createRoute({
|
||||
getParentRoute: () => sidebarRoute,
|
||||
path: "setup",
|
||||
component: () => <SetupPage />
|
||||
});
|
||||
|
||||
export const ethercatRoute = createRoute({
|
||||
getParentRoute: () => setupRoute,
|
||||
path: "ethercat",
|
||||
component: () => <EthercatPage />,
|
||||
})
|
||||
|
||||
export const rootTree = RootRoute.addChildren([
|
||||
sidebarRoute.addChildren([
|
||||
setupRoute.addChildren([
|
||||
ethercatRoute,
|
||||
])
|
||||
]),
|
||||
|
||||
]);
|
||||
|
||||
15
electron/src/setup/EthercatPage.tsx
Normal file
15
electron/src/setup/EthercatPage.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Page } from "@/components/Page";
|
||||
import { SectionTitle } from "@/components/SectionTitle";
|
||||
import React from "react";
|
||||
|
||||
export function EthercatPage() {
|
||||
return (
|
||||
<Page>
|
||||
<SectionTitle title="Interface"></SectionTitle>
|
||||
<p>
|
||||
Ethernet Interface{" "}
|
||||
<span>Discovering...</span>
|
||||
</p>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
18
electron/src/setup/SetupPage.tsx
Normal file
18
electron/src/setup/SetupPage.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Topbar } from "@/components/Topbar";
|
||||
import React from "react";
|
||||
|
||||
export function SetupPage() {
|
||||
return (
|
||||
<Topbar
|
||||
pathname="/_sidebar/setup"
|
||||
items={[
|
||||
{
|
||||
link: "machines",
|
||||
activeLink: "machines",
|
||||
title: "Person",
|
||||
icon: "lu:Factory",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
10
electron/src/types.d.ts
vendored
10
electron/src/types.d.ts
vendored
@@ -16,9 +16,19 @@ interface ElectronWindow {
|
||||
minimize: () => Promise<void>;
|
||||
maximize: () => Promise<void>;
|
||||
close: () => Promise<void>;
|
||||
fullscreen: (value: boolean) => Promise<void>;
|
||||
}
|
||||
|
||||
interface EnvironmentInfo {
|
||||
qitechOs: boolean;
|
||||
}
|
||||
|
||||
interface EnvironmentContext {
|
||||
getInfo: () => Promise<EnvironmentInfo>;
|
||||
}
|
||||
|
||||
declare interface Window {
|
||||
themeMode: ThemeModeContext;
|
||||
electronWindow: ElectronWindow;
|
||||
environment: EnvironmentContext;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user