mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-08-29 01:08:54 +00:00
Adds usable, unselectable, disableRightArrow
This commit is contained in:
+18
-18
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -7,8 +7,8 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ESX Menu Default</title>
|
||||
<script type="module" crossorigin src="./assets/index-DFrU2HgH.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-DfU8TAxG.css">
|
||||
<script type="module" crossorigin src="./assets/index-Begvl3m4.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-DB0yiboF.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -14,7 +14,9 @@ export interface Element {
|
||||
min: number;
|
||||
max: number;
|
||||
options?: string[];
|
||||
usable?: boolean;
|
||||
unselectable?: boolean;
|
||||
disableRightArrow?: boolean;
|
||||
}
|
||||
|
||||
export interface MenuData {
|
||||
@@ -63,6 +65,8 @@ const App: React.FC = () => {
|
||||
};
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(data))
|
||||
|
||||
setCurrentMenu(data);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { Element } from "@/app/App";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
description?: string;
|
||||
icon?: IconProp;
|
||||
element: Element;
|
||||
isSelected?: boolean;
|
||||
}
|
||||
|
||||
const MenuButton: React.FC<Props> = ({
|
||||
label,
|
||||
description,
|
||||
icon,
|
||||
isSelected,
|
||||
}) => {
|
||||
const MenuButton: React.FC<Props> = ({ element, isSelected }) => {
|
||||
const { label, description, icon, usable, disableRightArrow, unselectable } =
|
||||
element;
|
||||
const base = "rounded-[4px] flex items-center p-4 gap-4 justify-between";
|
||||
const sel = isSelected
|
||||
? "bg-[#FB9B041A] border border-[#FB9B04]"
|
||||
: "bg-[#252525] border border-transparent";
|
||||
|
||||
const unSelectable = unselectable ? "opacity-80" : "";
|
||||
|
||||
return (
|
||||
<div className={`${base} ${sel}`}>
|
||||
<div className={`${base} ${sel} ${unSelectable}`}>
|
||||
<div className="flex items-center gap-4">
|
||||
{icon && (
|
||||
<FontAwesomeIcon
|
||||
@@ -58,7 +55,11 @@ const MenuButton: React.FC<Props> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ArrowRight color={isSelected ? "#FB9B04" : "white"} size="1.6rem" />
|
||||
{usable !== false &&
|
||||
disableRightArrow !== true &&
|
||||
unselectable !== true && (
|
||||
<ArrowRight color={isSelected ? "#FB9B04" : "white"} size="1.6rem" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const MenuSlider: React.FC<Props> = ({ element, isSelected }) => {
|
||||
const { label, description, icon } = element;
|
||||
const { label, description, icon, unselectable } = element;
|
||||
|
||||
const options = (element as Element).options as string[] | undefined;
|
||||
|
||||
@@ -24,8 +24,10 @@ const MenuSlider: React.FC<Props> = ({ element, isSelected }) => {
|
||||
: "bg-[#252525] border border-transparent";
|
||||
const text = isSelected ? "text-[#FB9B04]" : "text-neutral-100";
|
||||
|
||||
const unSelectable = unselectable ? "opacity-50" : ""
|
||||
|
||||
return (
|
||||
<div className={`${base} ${sel}`}>
|
||||
<div className={`${base} ${sel} ${unSelectable}`}>
|
||||
<div className="flex items-center gap-4">
|
||||
{icon && (
|
||||
<FontAwesomeIcon
|
||||
|
||||
@@ -24,13 +24,27 @@ const Menu: React.FC<Props> = ({ data }) => {
|
||||
elements,
|
||||
});
|
||||
|
||||
const submit = () => fetchNui("menu_submit", payload());
|
||||
const submit = () => {
|
||||
const el = elements[position];
|
||||
if (el?.usable === false) return;
|
||||
|
||||
fetchNui("menu_submit", payload());
|
||||
};
|
||||
|
||||
const cancel = () =>
|
||||
fetchNui("menu_cancel", { _namespace: data.namespace, _name: data.name });
|
||||
const change = () => fetchNui("menu_change", payload());
|
||||
|
||||
const change = () => {
|
||||
const el = elements[position];
|
||||
if (el?.usable === false) return;
|
||||
|
||||
fetchNui("menu_change", payload());
|
||||
};
|
||||
|
||||
const stepSelectedSlider = (delta: number) => {
|
||||
const el = elements[position];
|
||||
|
||||
if (el?.usable === false) return;
|
||||
if (el?.type !== "slider") return;
|
||||
|
||||
const options = (el as any).options as string[] | undefined;
|
||||
@@ -53,7 +67,7 @@ const Menu: React.FC<Props> = ({ data }) => {
|
||||
};
|
||||
|
||||
useControls({
|
||||
length: elements.length,
|
||||
elements,
|
||||
position,
|
||||
setPosition: (updater) => {
|
||||
setPosition(updater);
|
||||
@@ -76,6 +90,13 @@ const Menu: React.FC<Props> = ({ data }) => {
|
||||
}
|
||||
}, [position]);
|
||||
|
||||
useEffect(() => {
|
||||
if (elements[position]?.unselectable) {
|
||||
const firstSelectable = elements.findIndex((el) => !el.unselectable);
|
||||
if (firstSelectable !== -1) setPosition(firstSelectable);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`absolute ${GetPosition(data.align)}`}>
|
||||
<div className="w-[26rem] h-fit max-h-[42rem] bg-[#161616] p-4 rounded-[10px] flex flex-col gap-4">
|
||||
@@ -105,12 +126,7 @@ const Menu: React.FC<Props> = ({ data }) => {
|
||||
{element.type === "slider" ? (
|
||||
<MenuSlider element={element} isSelected={isSelected} />
|
||||
) : (
|
||||
<MenuButton
|
||||
label={element.label}
|
||||
description={element.description}
|
||||
icon={element.icon}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
<MenuButton element={element} isSelected={isSelected} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// hooks/useControls.ts
|
||||
import { useEffect } from "react";
|
||||
|
||||
export type Control = "TOP" | "DOWN" | "ENTER" | "BACKSPACE" | "LEFT" | "RIGHT";
|
||||
|
||||
interface Props {
|
||||
length: number;
|
||||
elements: any[];
|
||||
position: number;
|
||||
setPosition: (p: number | ((prev: number) => number)) => void; // you'll wrap this to call change()
|
||||
setPosition: (p: number | ((prev: number) => number)) => void;
|
||||
submit: () => void;
|
||||
cancel: () => void;
|
||||
changeLeft: () => void;
|
||||
@@ -14,7 +13,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const useControls = ({
|
||||
length,
|
||||
elements,
|
||||
position,
|
||||
setPosition,
|
||||
submit,
|
||||
@@ -28,15 +27,31 @@ export const useControls = ({
|
||||
) => {
|
||||
if (ev.data.action !== "controlPressed") return;
|
||||
|
||||
const skipUp = (p: number) => {
|
||||
let next = p === 0 ? elements.length - 1 : p - 1;
|
||||
while (elements[next]?.unselectable) {
|
||||
next = next === 0 ? elements.length - 1 : next - 1;
|
||||
}
|
||||
return next;
|
||||
};
|
||||
|
||||
const skipDown = (p: number) => {
|
||||
let next = p === elements.length - 1 ? 0 : p + 1;
|
||||
while (elements[next]?.unselectable) {
|
||||
next = next === elements.length - 1 ? 0 : next + 1;
|
||||
}
|
||||
return next;
|
||||
};
|
||||
|
||||
switch (ev.data.control) {
|
||||
case "TOP":
|
||||
setPosition((p) => (p === 0 ? length - 1 : p - 1));
|
||||
setPosition((p) => skipUp(typeof p === "number" ? p : position));
|
||||
break;
|
||||
case "DOWN":
|
||||
setPosition((p) => (p === length - 1 ? 0 : p + 1));
|
||||
setPosition((p) => skipDown(typeof p === "number" ? p : position));
|
||||
break;
|
||||
case "ENTER":
|
||||
submit();
|
||||
if (!elements[position]?.unselectable) submit();
|
||||
break;
|
||||
case "BACKSPACE":
|
||||
cancel();
|
||||
@@ -52,5 +67,13 @@ export const useControls = ({
|
||||
|
||||
window.addEventListener("message", onMessage);
|
||||
return () => window.removeEventListener("message", onMessage);
|
||||
}, [length, position, setPosition, submit, cancel, changeLeft, changeRight]);
|
||||
}, [
|
||||
elements,
|
||||
position,
|
||||
setPosition,
|
||||
submit,
|
||||
cancel,
|
||||
changeLeft,
|
||||
changeRight,
|
||||
]);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user