Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
5f5cd6e1d9 |
13 changed files with 168 additions and 20 deletions
1
.rsw/rsw.crates
Normal file
1
.rsw/rsw.crates
Normal file
|
@ -0,0 +1 @@
|
||||||
|
common :~> common/pkg
|
|
@ -47,6 +47,7 @@
|
||||||
"sass": "^1.69.6",
|
"sass": "^1.69.6",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^5.0.8",
|
"vite": "^5.0.8",
|
||||||
|
"vite-plugin-rsw": "^2.0.11",
|
||||||
"vite-plugin-top-level-await": "^1.4.1",
|
"vite-plugin-top-level-await": "^1.4.1",
|
||||||
"vite-plugin-wasm": "^3.3.0"
|
"vite-plugin-wasm": "^3.3.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,9 @@ devDependencies:
|
||||||
vite:
|
vite:
|
||||||
specifier: ^5.0.8
|
specifier: ^5.0.8
|
||||||
version: 5.0.10(sass@1.69.6)
|
version: 5.0.10(sass@1.69.6)
|
||||||
|
vite-plugin-rsw:
|
||||||
|
specifier: ^2.0.11
|
||||||
|
version: 2.0.11(vite@5.0.10)
|
||||||
vite-plugin-top-level-await:
|
vite-plugin-top-level-await:
|
||||||
specifier: ^1.4.1
|
specifier: ^1.4.1
|
||||||
version: 1.4.1(vite@5.0.10)
|
version: 1.4.1(vite@5.0.10)
|
||||||
|
@ -3286,6 +3289,14 @@ packages:
|
||||||
resolution: {integrity: sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A==}
|
resolution: {integrity: sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/vite-plugin-rsw@2.0.11(vite@5.0.10):
|
||||||
|
resolution: {integrity: sha512-OqbGAtdDfK88gDoxA9wVdGowCmlzLSXEyRvD8CgTu9IP5O7tx8Tg2CtIzKOyOWZtHHZPhtdYiN+5Il6rTZCkzw==}
|
||||||
|
peerDependencies:
|
||||||
|
vite: '>2.8.0-0'
|
||||||
|
dependencies:
|
||||||
|
vite: 5.0.10(sass@1.69.6)
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vite-plugin-top-level-await@1.4.1(vite@5.0.10):
|
/vite-plugin-top-level-await@1.4.1(vite@5.0.10):
|
||||||
resolution: {integrity: sha512-hogbZ6yT7+AqBaV6lK9JRNvJDn4/IJvHLu6ET06arNfo0t2IsyCaon7el9Xa8OumH+ESuq//SDf8xscZFE0rWw==}
|
resolution: {integrity: sha512-hogbZ6yT7+AqBaV6lK9JRNvJDn4/IJvHLu6ET06arNfo0t2IsyCaon7el9Xa8OumH+ESuq//SDf8xscZFE0rWw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
|
@ -4,6 +4,7 @@ import "./App.css";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import { Provider as ReduxProvider } from "react-redux";
|
import { Provider as ReduxProvider } from "react-redux";
|
||||||
import { store } from "./store";
|
import { store } from "./store";
|
||||||
|
import { HotkeysProvider } from "@blueprintjs/core";
|
||||||
|
|
||||||
const router = createBrowserRouter(routes);
|
const router = createBrowserRouter(routes);
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
@ -12,7 +13,9 @@ function App() {
|
||||||
return (
|
return (
|
||||||
<ReduxProvider store={store}>
|
<ReduxProvider store={store}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<HotkeysProvider>
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
|
</HotkeysProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</ReduxProvider>
|
</ReduxProvider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,9 +6,13 @@ import "normalize.css";
|
||||||
import "@blueprintjs/core/lib/css/blueprint.css";
|
import "@blueprintjs/core/lib/css/blueprint.css";
|
||||||
import "@blueprintjs/icons/lib/css/blueprint-icons.css";
|
import "@blueprintjs/icons/lib/css/blueprint-icons.css";
|
||||||
|
|
||||||
|
import init from "common";
|
||||||
|
|
||||||
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
||||||
const el = document.getElementById("root")!;
|
const el = document.getElementById("root")!;
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
ReactDOM.createRoot(el).render(
|
ReactDOM.createRoot(el).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
import { useRef, useState } from "react";
|
import { useCallback, useState, type MouseEvent } from "react";
|
||||||
import { useQuery } from "react-query";
|
import { useQuery } from "react-query";
|
||||||
import { Canvas, type MeshProps, useLoader } from "@react-three/fiber";
|
import { Canvas, type MeshProps, useLoader } from "@react-three/fiber";
|
||||||
import styles from "./MapView.module.scss";
|
import styles from "./MapView.module.css";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { MapControls, Text } from "@react-three/drei";
|
import { MapControls, Text } from "@react-three/drei";
|
||||||
import { ColorRepresentation, DoubleSide, TextureLoader } from "three";
|
import { ColorRepresentation, DoubleSide, TextureLoader } from "three";
|
||||||
import { Bloom, EffectComposer } from "@react-three/postprocessing";
|
import { Bloom, EffectComposer } from "@react-three/postprocessing";
|
||||||
import { useAppDispatch } from "../store";
|
import { useAppDispatch } from "../store";
|
||||||
import { addWindow } from "../store/someShit";
|
import { setWindow } from "../store/someShit";
|
||||||
import { DragDropContext } from "react-beautiful-dnd";
|
import { DragDropContext } from "react-beautiful-dnd";
|
||||||
|
import {
|
||||||
|
Menu,
|
||||||
|
MenuDivider,
|
||||||
|
MenuItem,
|
||||||
|
showContextMenu,
|
||||||
|
} from "@blueprintjs/core";
|
||||||
|
import StatusWindow from "./StatusWindow";
|
||||||
|
|
||||||
export default function MapView({}) {
|
export default function MapView() {
|
||||||
const { universeId } = useParams();
|
const { universeId } = useParams();
|
||||||
const { isLoading, data: mapData } = useQuery(
|
const { isLoading, data: mapData } = useQuery(
|
||||||
`universe ${universeId} points`,
|
`universe ${universeId} points`,
|
||||||
|
@ -74,6 +81,8 @@ export default function MapView({}) {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
|
<StatusWindow />
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
@ -100,6 +109,36 @@ function StarSystem({
|
||||||
color = parseInt(owner.color, 16);
|
color = parseInt(owner.color, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
icon="cross-circle"
|
||||||
|
intent="danger"
|
||||||
|
text="Click me to close"
|
||||||
|
// onClick={handleClose}
|
||||||
|
/>
|
||||||
|
<MenuItem icon="search-around" text="Search around..." />
|
||||||
|
<MenuItem icon="search" text="Object viewer" />
|
||||||
|
<MenuItem icon="graph-remove" text="Remove" />
|
||||||
|
<MenuItem icon="group-objects" text="Group" />
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuItem disabled={true} text="Clicked on node" />
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleContextMenu = useCallback((event: MouseEvent<HTMLElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
showContextMenu({
|
||||||
|
content: menu,
|
||||||
|
// onClose: handleClose,
|
||||||
|
targetOffset: {
|
||||||
|
left: event.clientX,
|
||||||
|
top: event.clientY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// setIsOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<mesh {...props} position={[x, 0, y]} scale={active ? 1.5 : 1}>
|
<mesh {...props} position={[x, 0, y]} scale={active ? 1.5 : 1}>
|
||||||
|
@ -115,7 +154,12 @@ function StarSystem({
|
||||||
{...props}
|
{...props}
|
||||||
position={[x, 0, y]}
|
position={[x, 0, y]}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
dispatch(addWindow(["planet", { type: "planet" }]));
|
dispatch(setWindow(["planet", { type: "planet" }]));
|
||||||
|
event.stopPropagation();
|
||||||
|
}}
|
||||||
|
onContextMenu={(event) => {
|
||||||
|
console.log("Hellosu", event);
|
||||||
|
handleContextMenu(event.nativeEvent);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}}
|
}}
|
||||||
onPointerOver={(event) => {
|
onPointerOver={(event) => {
|
||||||
|
|
60
frontend/src/routes/StatusWindow.tsx
Normal file
60
frontend/src/routes/StatusWindow.tsx
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import { HotkeyConfig, useHotkeys } from "@blueprintjs/core";
|
||||||
|
import { useCallback, useMemo } from "react";
|
||||||
|
|
||||||
|
export default function StatusWindow() {
|
||||||
|
const handleEscape = useCallback(() => {
|
||||||
|
console.log("helloge");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const hotkeys: HotkeyConfig[] = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
combo: "1",
|
||||||
|
global: true,
|
||||||
|
label: "Escape",
|
||||||
|
onkeydown: handleEscape,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[handleEscape],
|
||||||
|
);
|
||||||
|
|
||||||
|
const { handleKeyDown, handleKeyUp } = useHotkeys(hotkeys);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="bp5-dialog"
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onKeyUp={handleKeyUp}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
bottom: "10px",
|
||||||
|
left: "10px",
|
||||||
|
margin: "0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="bp5-dialog-header">
|
||||||
|
<span className="bp5-icon-large bp5-icon-inbox" />
|
||||||
|
<h5 className="bp5-heading">Dialog header</h5>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Close"
|
||||||
|
className="bp5-dialog-close-button bp5-button bp5-minimal bp5-icon-cross"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="bp5-dialog-body">
|
||||||
|
This dialog hasn't been wired up with any open or close interactions.
|
||||||
|
It's just an example of markup and styles.
|
||||||
|
</div>
|
||||||
|
<div className="bp5-dialog-footer">
|
||||||
|
<div className="bp5-dialog-footer-actions">
|
||||||
|
<button type="button" className="bp5-button">
|
||||||
|
Secondary button
|
||||||
|
</button>
|
||||||
|
<button type="submit" className="bp5-button bp5-intent-primary">
|
||||||
|
Primary button
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,20 +1,18 @@
|
||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
type WindowDef = {
|
type WindowDef = { type: string } & object;
|
||||||
type: "planet";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define a type for the slice state
|
// Define a type for the slice state
|
||||||
interface SomeShitState {
|
interface SomeShitState {
|
||||||
activeEmpireId: number | null;
|
activeEmpireId: number | null;
|
||||||
windows: { [_: string]: WindowDef };
|
activeWindow: WindowDef | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the initial state using that type
|
// Define the initial state using that type
|
||||||
const initialState: SomeShitState = {
|
const initialState: SomeShitState = {
|
||||||
activeEmpireId: null,
|
activeEmpireId: null,
|
||||||
windows: {},
|
activeWindow: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const someShitSlice = createSlice({
|
export const someShitSlice = createSlice({
|
||||||
|
@ -25,18 +23,17 @@ export const someShitSlice = createSlice({
|
||||||
state.activeEmpireId = action.payload;
|
state.activeEmpireId = action.payload;
|
||||||
},
|
},
|
||||||
|
|
||||||
addWindow: (state, action: PayloadAction<[string, WindowDef]>) => {
|
setWindow: (state, action: PayloadAction<WindowDef>) => {
|
||||||
const [name, def] = action.payload;
|
state.activeWindow = action.payload;
|
||||||
state.windows = { ...state.windows, [name]: def };
|
|
||||||
},
|
},
|
||||||
|
|
||||||
closeWindow: (state, action: PayloadAction<string>) => {
|
closeWindow: (state) => {
|
||||||
delete state.windows[action.payload];
|
state.activeWindow = null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { changeActiveEmpire, addWindow, closeWindow } =
|
export const { changeActiveEmpire, setWindow, closeWindow } =
|
||||||
someShitSlice.actions;
|
someShitSlice.actions;
|
||||||
|
|
||||||
export default someShitSlice.reducer;
|
export default someShitSlice.reducer;
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import reactPlugin from "@vitejs/plugin-react-swc";
|
import reactPlugin from "@vitejs/plugin-react-swc";
|
||||||
import wasmPlugin from "vite-plugin-wasm";
|
// import wasmPlugin from "vite-plugin-wasm";
|
||||||
import topLevelAwait from "vite-plugin-top-level-await";
|
// import topLevelAwait from "vite-plugin-top-level-await";
|
||||||
|
import { ViteRsw } from "vite-plugin-rsw";
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [reactPlugin(), wasmPlugin(), topLevelAwait()],
|
plugins: [
|
||||||
|
// wasmPlugin(["./common"], []),
|
||||||
|
// topLevelAwait(),
|
||||||
|
// ViteRsw(),
|
||||||
|
reactPlugin(),
|
||||||
|
],
|
||||||
server: {
|
server: {
|
||||||
|
fs: { allow: [".."] },
|
||||||
proxy: {
|
proxy: {
|
||||||
"/api": {
|
"/api": {
|
||||||
target: "http://localhost:1440",
|
target: "http://localhost:1440",
|
||||||
|
|
5
package.json
Normal file
5
package.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"common": "link:/Users/michael/Projects/openstellaris/common/pkg"
|
||||||
|
}
|
||||||
|
}
|
10
pnpm-lock.yaml
Normal file
10
pnpm-lock.yaml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
lockfileVersion: '6.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
common:
|
||||||
|
specifier: link:/Users/michael/Projects/openstellaris/common/pkg
|
||||||
|
version: link:common/pkg
|
5
rsw.toml
Normal file
5
rsw.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
cli = "pnpm"
|
||||||
|
|
||||||
|
[[crates]]
|
||||||
|
name = "common"
|
||||||
|
link = true
|
Loading…
Reference in a new issue