diff --git a/.editorconfig b/.editorconfig index deef933..71cadc6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,7 @@ charset = utf-8 trim_trailing_whitespace = true indent_size = 2 indent_style = space +max_line_length = 120 [*.lua] indent_size = 4 diff --git a/web/src/App.tsx b/web/src/App.tsx index b170822..e697d38 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -3,12 +3,13 @@ import CircleProgressbar from "./features/progress/CircleProgressbar"; import Progressbar from "./features/progress/Progressbar"; import TextUI from "./features/textui/TextUI"; import InputDialog from "./features/dialog/InputDialog"; -import ContextMenu from "./features/menu/ContextMenu"; +import ContextMenu from "./features/menu/context/ContextMenu"; import Settings from "./features/settings"; import { useNuiEvent } from "./hooks/useNuiEvent"; import { setClipboard } from "./utils/setClipboard"; import { fetchNui } from "./utils/fetchNui"; import AlertDialog from "./features/dialog/AlertDialog"; +import ListMenu from "./features/menu/list"; const App: React.FC = () => { useNuiEvent("setClipboard", (data: string) => { @@ -27,6 +28,7 @@ const App: React.FC = () => { + ); }; diff --git a/web/src/features/menu/ContextMenu.tsx b/web/src/features/menu/context/ContextMenu.tsx similarity index 94% rename from web/src/features/menu/ContextMenu.tsx rename to web/src/features/menu/context/ContextMenu.tsx index 300015d..8e2f6d5 100644 --- a/web/src/features/menu/ContextMenu.tsx +++ b/web/src/features/menu/context/ContextMenu.tsx @@ -1,11 +1,11 @@ -import { useNuiEvent } from "../../hooks/useNuiEvent"; +import { useNuiEvent } from "../../../hooks/useNuiEvent"; import { Box, Text, Flex, ScaleFade } from "@chakra-ui/react"; -import { debugData } from "../../utils/debugData"; +import { debugData } from "../../../utils/debugData"; import { useEffect, useState } from "react"; -import { ContextMenuProps } from "../../interfaces/context"; +import { ContextMenuProps } from "../../../interfaces/context"; import Item from "./Item"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { fetchNui } from "../../utils/fetchNui"; +import { fetchNui } from "../../../utils/fetchNui"; debugData([ { diff --git a/web/src/features/menu/Item.tsx b/web/src/features/menu/context/Item.tsx similarity index 97% rename from web/src/features/menu/Item.tsx rename to web/src/features/menu/context/Item.tsx index 082146e..28d3cfb 100644 --- a/web/src/features/menu/Item.tsx +++ b/web/src/features/menu/context/Item.tsx @@ -12,8 +12,8 @@ import { HStack, } from "@chakra-ui/react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Option, ContextMenuProps } from "../../interfaces/context"; -import { fetchNui } from "../../utils/fetchNui"; +import { Option, ContextMenuProps } from "../../../interfaces/context"; +import { fetchNui } from "../../../utils/fetchNui"; const openMenu = (id: string | undefined) => { fetchNui("openContext", id); diff --git a/web/src/features/menu/list/index.tsx b/web/src/features/menu/list/index.tsx new file mode 100644 index 0000000..8f88030 --- /dev/null +++ b/web/src/features/menu/list/index.tsx @@ -0,0 +1,119 @@ +import { Box, Spacer, Flex, Stack, Text } from "@chakra-ui/react"; +import { useEffect, useRef, useState } from "react"; +import { useNuiEvent } from "../../../hooks/useNuiEvent"; +import { debugData } from "../../../utils/debugData"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +interface MenuSettings { + position?: "top-left" | "top-right" | "bottom-left" | "bottom-right"; + title: string; + items: Array<{ label: string; value: string | string[] }>; +} + +debugData([ + { + action: "setMenu", + data: { + // position: "bottom-left", + title: "Vehicle garage", + items: [ + { label: "Option 1", value: "option1" }, + { label: "Option 2", value: "option2" }, + { label: "Vehicle class", value: ["Nice", "Super nice", "Extra nice"] }, + ], + }, + }, +]); + +const ListMenu: React.FC = () => { + const [menu, setMenu] = useState({ + position: "top-left", + title: "", + items: [], + }); + const [selected, setSelected] = useState(0); + const [visible, setVisible] = useState(false); + + const moveMenu = (e: KeyboardEvent) => { + switch (e.code) { + case "ArrowDown": + setSelected((selected) => { + if (selected >= menu.items.length - 1) return selected; + return selected + 1; + }); + break; + case "ArrowUp": + setSelected((selected) => { + if (selected <= 0) return selected; + return selected - 1; + }); + break; + } + }; + + useEffect(() => { + if (!visible) return document.removeEventListener("keyup", moveMenu); + + document.addEventListener("keyup", moveMenu); + }, [visible]); + + useNuiEvent("setMenu", (data: MenuSettings) => { + if (!data.position) data.position = "top-left"; + setMenu(data); + setVisible(true); + }); + + return ( + <> + + + + {menu.title} + + + + {menu.items.map((item, index) => ( + + {Array.isArray(item.value) ? ( + + + + {item.label} + + Nice cool + + + + + ) : ( + item.label + )} + + ))} + + + + ); +}; + +export default ListMenu; diff --git a/web/src/index.css b/web/src/index.css index 5692f71..42c1c45 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -2,13 +2,13 @@ @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Fira+Mono&display=swap"); @import url("https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap"); body { background: none !important; margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", + "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; height: 100vh; @@ -21,8 +21,7 @@ body { } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } @keyframes progress { @@ -52,13 +51,13 @@ code { } .toast-inform { - background-color: #2980b9; + background-color: #2980b9; } .toast-success { - background-color: #27ae60; + background-color: #27ae60; } .toast-error { - background-color: #c0392b; + background-color: #c0392b; }