mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-17 15:06:02 +01:00
feat(web): developer drawer
Move all debug functions into a single developer drawer, Not the best implementation as there is focus fighting going on between some elements and the drawer but will do for now without state management
This commit is contained in:
16
web/src/features/dev/debug/alert.ts
Normal file
16
web/src/features/dev/debug/alert.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
import { AlertProps } from "../../dialog/AlertDialog";
|
||||
|
||||
export const debugAlert = () => {
|
||||
debugData<AlertProps>([
|
||||
{
|
||||
action: "sendAlert",
|
||||
data: {
|
||||
header: "Hello there",
|
||||
content: "General kenobi \n Markdown works",
|
||||
centered: true,
|
||||
cancel: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
38
web/src/features/dev/debug/context.ts
Normal file
38
web/src/features/dev/debug/context.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { ContextMenuProps } from "../../../interfaces/context";
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
|
||||
export const debugContext = () => {
|
||||
debugData<ContextMenuProps>([
|
||||
{
|
||||
action: "showContext",
|
||||
data: {
|
||||
title: "Vehicle garage",
|
||||
options: [
|
||||
{ title: "Empty button" },
|
||||
{
|
||||
title: "Example button",
|
||||
description: "Example button description",
|
||||
icon: "inbox",
|
||||
image: "https://i.imgur.com/YAe7k17.jpeg",
|
||||
metadata: [{ label: "Value 1", value: 300 }],
|
||||
},
|
||||
{
|
||||
title: "Menu button",
|
||||
icon: "bars",
|
||||
menu: "other_example_menu",
|
||||
description: "Takes you to another menu",
|
||||
metadata: ["It also has metadata support"],
|
||||
},
|
||||
{
|
||||
title: "Event button",
|
||||
description: "Open a menu and send event data",
|
||||
icon: "check",
|
||||
arrow: true,
|
||||
event: "some_event",
|
||||
args: { value1: 300, value2: "Other value" },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
30
web/src/features/dev/debug/input.ts
Normal file
30
web/src/features/dev/debug/input.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
import {InputProps} from "../../dialog/InputDialog";
|
||||
|
||||
export const debugInput = () => {
|
||||
debugData<InputProps>([
|
||||
{
|
||||
action: "openDialog",
|
||||
data: {
|
||||
heading: "Police locker",
|
||||
rows: [
|
||||
{ type: "input", label: "Locker number", placeholder: "420" },
|
||||
{ type: "checkbox", label: "Some checkbox" },
|
||||
{ type: "input", label: "Locker PIN", password: true, icon: "lock" },
|
||||
{ type: "checkbox", label: "Some other checkbox", checked: true },
|
||||
{
|
||||
type: "select",
|
||||
label: "Locker type",
|
||||
options: [
|
||||
{ value: "option1" },
|
||||
{ value: "option2", label: "Option 2" },
|
||||
{ value: "option3", label: "Option 3" },
|
||||
],
|
||||
},
|
||||
{ type: "number", label: "Number counter", default: 12 },
|
||||
{ type: "slider", label: "Slide bar", min: 10, max: 50, step: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
41
web/src/features/dev/debug/menu.ts
Normal file
41
web/src/features/dev/debug/menu.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
import { MenuSettings } from "../../menu/list";
|
||||
|
||||
export const debugMenu = () => {
|
||||
debugData<MenuSettings>([
|
||||
{
|
||||
action: "setMenu",
|
||||
data: {
|
||||
// position: "bottom-left",
|
||||
title: "Vehicle garage",
|
||||
items: [
|
||||
{ label: "Option 1", icon: "heart" },
|
||||
{
|
||||
label: "Option 2",
|
||||
icon: "basket-shopping",
|
||||
description: "Tooltip description 1",
|
||||
},
|
||||
{
|
||||
label: "Vehicle class",
|
||||
values: ["Nice", "Super nice", "Extra nice"],
|
||||
icon: "tag",
|
||||
description: "Tooltip description 2",
|
||||
},
|
||||
{ label: "Option 1" },
|
||||
{ label: "Option 2" },
|
||||
{
|
||||
label: "Vehicle class",
|
||||
values: ["Nice", "Super nice", "Extra nice"],
|
||||
defaultIndex: 1,
|
||||
},
|
||||
{ label: "Option 1" },
|
||||
{ label: "Option 2" },
|
||||
{
|
||||
label: "Vehicle class",
|
||||
values: ["Nice", "Super nice", "Extra nice"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
34
web/src/features/dev/debug/notification.ts
Normal file
34
web/src/features/dev/debug/notification.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
CustomNotifiactionProps,
|
||||
NotificationProps,
|
||||
} from "../../notifications/NotificationWrapper";
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
|
||||
export const debugNotification = () => {
|
||||
debugData<NotificationProps>([
|
||||
{
|
||||
action: "notify",
|
||||
data: {
|
||||
description: "Dunak is nerd",
|
||||
title: "Dunak",
|
||||
id: 1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
export const debugCustomNotification = () => {
|
||||
debugData<CustomNotifiactionProps>([
|
||||
{
|
||||
action: "customNotify",
|
||||
data: {
|
||||
description: "Dunak is nerd",
|
||||
icon: "basket-shopping",
|
||||
style: {
|
||||
backgroundColor: "#2D3748",
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
26
web/src/features/dev/debug/progress.ts
Normal file
26
web/src/features/dev/debug/progress.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
import { ProgressbarProps } from "../../progress/Progressbar";
|
||||
|
||||
export const debugProgressbar = () => {
|
||||
debugData<ProgressbarProps>([
|
||||
{
|
||||
action: "progress",
|
||||
data: {
|
||||
label: "Using Lockpick",
|
||||
duration: 8000,
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
export const debugCircleProgressbar = () => {
|
||||
debugData([
|
||||
{
|
||||
action: "circleProgress",
|
||||
data: {
|
||||
duration: 8000,
|
||||
label: "Using Armour",
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
10
web/src/features/dev/debug/settings.ts
Normal file
10
web/src/features/dev/debug/settings.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
|
||||
export const debugSettings = () => {
|
||||
debugData([
|
||||
{
|
||||
action: "openSettings",
|
||||
data: true,
|
||||
},
|
||||
]);
|
||||
}
|
||||
15
web/src/features/dev/debug/textui.ts
Normal file
15
web/src/features/dev/debug/textui.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { TextUiProps } from "../../textui/TextUI";
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
|
||||
export const debugTextUI = () => {
|
||||
debugData<TextUiProps>([
|
||||
{
|
||||
action: "textUi",
|
||||
data: {
|
||||
text: "[E] - Access locker inventory \n [G] - Do something else",
|
||||
position: "right-center",
|
||||
icon: "door-open",
|
||||
},
|
||||
},
|
||||
]);
|
||||
};
|
||||
96
web/src/features/dev/index.tsx
Normal file
96
web/src/features/dev/index.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
Button,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerContent,
|
||||
DrawerHeader,
|
||||
DrawerOverlay,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
VStack,
|
||||
Divider,
|
||||
useDisclosure,
|
||||
} from "@chakra-ui/react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { debugAlert } from "./debug/alert";
|
||||
import { debugContext } from "./debug/context";
|
||||
import { debugInput } from "./debug/input";
|
||||
import { debugMenu } from "./debug/menu";
|
||||
import {
|
||||
debugCustomNotification,
|
||||
debugNotification,
|
||||
} from "./debug/notification";
|
||||
import { debugCircleProgressbar, debugProgressbar } from "./debug/progress";
|
||||
import { debugTextUI } from "./debug/textui";
|
||||
import { debugSettings } from "./debug/settings";
|
||||
|
||||
const Dev: React.FC = () => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip label="Developer drawer">
|
||||
<IconButton
|
||||
position="absolute"
|
||||
bottom={0}
|
||||
right={0}
|
||||
mr={20}
|
||||
mb={20}
|
||||
borderRadius="50%"
|
||||
icon={<FontAwesomeIcon icon="wrench" fixedWidth size="lg" />}
|
||||
colorScheme="orange"
|
||||
size="lg"
|
||||
aria-label="Dev tools"
|
||||
onClick={() => onOpen()}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Drawer placement="left" onClose={onClose} isOpen={isOpen}>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent>
|
||||
<DrawerHeader>Developer drawer</DrawerHeader>
|
||||
<DrawerBody>
|
||||
<VStack>
|
||||
<Divider />
|
||||
<Button isFullWidth onClick={() => debugInput()}>
|
||||
Open input dialog
|
||||
</Button>
|
||||
<Button isFullWidth onClick={() => debugAlert()}>
|
||||
Open alert dialog
|
||||
</Button>
|
||||
<Divider />
|
||||
<Button isFullWidth onClick={() => debugContext()}>
|
||||
Open context menu
|
||||
</Button>
|
||||
<Button isFullWidth onClick={() => debugMenu()}>
|
||||
Open list menu
|
||||
</Button>
|
||||
<Divider />
|
||||
<Button isFullWidth onClick={() => debugCustomNotification()}>
|
||||
Send custom notification
|
||||
</Button>
|
||||
<Button isFullWidth onClick={() => debugNotification()}>
|
||||
Send default notification
|
||||
</Button>
|
||||
<Divider />
|
||||
<Button isFullWidth onClick={() => debugProgressbar()}>
|
||||
Activate progress bar
|
||||
</Button>
|
||||
<Button isFullWidth onClick={() => debugCircleProgressbar()}>
|
||||
Activate progress circle
|
||||
</Button>
|
||||
<Divider />
|
||||
<Button isFullWidth onClick={() => debugSettings()}>
|
||||
Open settings page
|
||||
</Button>
|
||||
<Button isFullWidth onClick={() => debugTextUI()}>
|
||||
Show TextUI
|
||||
</Button>
|
||||
</VStack>
|
||||
</DrawerBody>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dev;
|
||||
@@ -11,34 +11,21 @@ import {
|
||||
import { useRef, useState } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { useNuiEvent } from "../../hooks/useNuiEvent";
|
||||
import { debugData } from "../../utils/debugData";
|
||||
import { fetchNui } from "../../utils/fetchNui";
|
||||
import { useLocales } from "../../providers/LocaleProvider";
|
||||
|
||||
interface DialogProps {
|
||||
export interface AlertProps {
|
||||
header: string;
|
||||
content: string;
|
||||
centered?: boolean;
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
// debugData<DialogProps>([
|
||||
// {
|
||||
// action: "sendAlert",
|
||||
// data: {
|
||||
// header: "Hello there",
|
||||
// content: "General kenobi \n Markdown works",
|
||||
// centered: true,
|
||||
// cancel: true,
|
||||
// },
|
||||
// },
|
||||
// ]);
|
||||
|
||||
const AlertDialog: React.FC = () => {
|
||||
const { locale } = useLocales();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const cancelRef = useRef(null);
|
||||
const [dialogData, setDialogData] = useState<DialogProps>({
|
||||
const [dialogData, setDialogData] = useState<AlertProps>({
|
||||
header: "",
|
||||
content: "",
|
||||
});
|
||||
@@ -48,7 +35,7 @@ const AlertDialog: React.FC = () => {
|
||||
fetchNui("closeAlert", button);
|
||||
};
|
||||
|
||||
useNuiEvent("sendAlert", (data: DialogProps) => {
|
||||
useNuiEvent("sendAlert", (data: AlertProps) => {
|
||||
setDialogData(data);
|
||||
onOpen();
|
||||
});
|
||||
@@ -77,7 +64,10 @@ const AlertDialog: React.FC = () => {
|
||||
{locale.ui.cancel}
|
||||
</Button>
|
||||
)}
|
||||
<Button colorScheme={dialogData.cancel ? "blue" : undefined} onClick={() => closeAlert("confirm")}>
|
||||
<Button
|
||||
colorScheme={dialogData.cancel ? "blue" : undefined}
|
||||
onClick={() => closeAlert("confirm")}
|
||||
>
|
||||
{locale.ui.confirm}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@@ -1,54 +1,42 @@
|
||||
import { Modal, ModalOverlay, ModalContent, ModalFooter, ModalHeader, ModalBody, Button } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { useNuiEvent } from '../../hooks/useNuiEvent';
|
||||
import { useKeyPress } from '../../hooks/useKeyPress';
|
||||
import { useLocales } from '../../providers/LocaleProvider';
|
||||
import { debugData } from '../../utils/debugData';
|
||||
import { fetchNui } from '../../utils/fetchNui';
|
||||
import { IInput, ICheckbox, ISelect, INumber, ISlider } from '../../interfaces/dialog';
|
||||
import InputField from './components/input';
|
||||
import CheckboxField from './components/checkbox';
|
||||
import SelectField from './components/select';
|
||||
import NumberField from './components/number';
|
||||
import SliderField from './components/slider';
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
Button,
|
||||
} from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
import { useNuiEvent } from "../../hooks/useNuiEvent";
|
||||
import { useLocales } from "../../providers/LocaleProvider";
|
||||
import { fetchNui } from "../../utils/fetchNui";
|
||||
import {
|
||||
IInput,
|
||||
ICheckbox,
|
||||
ISelect,
|
||||
INumber,
|
||||
ISlider,
|
||||
} from "../../interfaces/dialog";
|
||||
import InputField from "./components/input";
|
||||
import CheckboxField from "./components/checkbox";
|
||||
import SelectField from "./components/select";
|
||||
import NumberField from "./components/number";
|
||||
import SliderField from "./components/slider";
|
||||
|
||||
interface Props {
|
||||
export interface InputProps {
|
||||
heading: string;
|
||||
rows: Array<IInput | ICheckbox | ISelect | INumber | ISlider>;
|
||||
}
|
||||
|
||||
// debugData<Props>([
|
||||
// {
|
||||
// action: "openDialog",
|
||||
// data: {
|
||||
// heading: "Police locker",
|
||||
// rows: [
|
||||
// { type: "input", label: "Locker number", placeholder: "420" },
|
||||
// { type: "checkbox", label: "Some checkbox" },
|
||||
// { type: "input", label: "Locker PIN", password: true, icon: "lock" },
|
||||
// { type: "checkbox", label: "Some other checkbox", checked: true },
|
||||
// {
|
||||
// type: "select",
|
||||
// label: "Locker type",
|
||||
// options: [
|
||||
// { value: "option1" },
|
||||
// { value: "option2", label: "Option 2" },
|
||||
// { value: "option3", label: "Option 3" },
|
||||
// ],
|
||||
// },
|
||||
// { type: "number", label: "Number counter", default: 12 },
|
||||
// { type: "slider", label: "Slide bar", min: 10, max: 50, step: 2 },
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// ]);
|
||||
|
||||
const InputDialog: React.FC = () => {
|
||||
const [fields, setFields] = React.useState<Props>({
|
||||
heading: '',
|
||||
rows: [{ type: 'input', label: '' }],
|
||||
const [fields, setFields] = React.useState<InputProps>({
|
||||
heading: "",
|
||||
rows: [{ type: "input", label: "" }],
|
||||
});
|
||||
const [inputData, setInputData] = React.useState<Array<string | number | boolean>>([]);
|
||||
const [inputData, setInputData] = React.useState<
|
||||
Array<string | number | boolean>
|
||||
>([]);
|
||||
const [passwordStates, setPasswordStates] = React.useState<boolean[]>([]);
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
const { locale } = useLocales();
|
||||
@@ -60,7 +48,7 @@ const InputDialog: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
useNuiEvent<Props>('openDialog', (data) => {
|
||||
useNuiEvent<InputProps>("openDialog", (data) => {
|
||||
setPasswordStates([]);
|
||||
setFields(data);
|
||||
setInputData([]);
|
||||
@@ -69,7 +57,7 @@ const InputDialog: React.FC = () => {
|
||||
|
||||
const handleClose = () => {
|
||||
setVisible(false);
|
||||
fetchNui('inputData');
|
||||
fetchNui("inputData");
|
||||
};
|
||||
|
||||
const handleChange = (value: string | number | boolean, index: number) => {
|
||||
@@ -81,38 +69,74 @@ const InputDialog: React.FC = () => {
|
||||
|
||||
const handleConfirm = () => {
|
||||
setVisible(false);
|
||||
fetchNui('inputData', inputData);
|
||||
fetchNui("inputData", inputData);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal isOpen={visible} onClose={handleClose} isCentered closeOnEsc closeOnOverlayClick={false} size="xs">
|
||||
<Modal
|
||||
isOpen={visible}
|
||||
onClose={handleClose}
|
||||
isCentered
|
||||
closeOnEsc
|
||||
closeOnOverlayClick={false}
|
||||
size="xs"
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && visible) return handleConfirm();
|
||||
if (e.key === "Enter" && visible) return handleConfirm();
|
||||
}}
|
||||
>
|
||||
<ModalHeader textAlign="center">{fields.heading}</ModalHeader>
|
||||
<ModalBody fontFamily="Poppins" textAlign="left">
|
||||
{fields.rows.map((row: IInput | ICheckbox | ISelect | INumber | ISlider, index) => (
|
||||
<React.Fragment key={`row-${index}`}>
|
||||
{row.type === 'input' && (
|
||||
<InputField
|
||||
key={`input-${index}`}
|
||||
row={row}
|
||||
index={index}
|
||||
handleChange={handleChange}
|
||||
passwordStates={passwordStates}
|
||||
handlePasswordStates={handlePasswordStates}
|
||||
/>
|
||||
)}
|
||||
{row.type === 'checkbox' && <CheckboxField row={row} index={index} handleChange={handleChange} />}
|
||||
{row.type === 'select' && <SelectField row={row} index={index} handleChange={handleChange} />}
|
||||
{row.type === 'number' && <NumberField row={row} index={index} handleChange={handleChange} />}
|
||||
{row.type === 'slider' && <SliderField row={row} index={index} handleChange={handleChange} />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
{fields.rows.map(
|
||||
(
|
||||
row: IInput | ICheckbox | ISelect | INumber | ISlider,
|
||||
index
|
||||
) => (
|
||||
<React.Fragment key={`row-${index}`}>
|
||||
{row.type === "input" && (
|
||||
<InputField
|
||||
key={`input-${index}`}
|
||||
row={row}
|
||||
index={index}
|
||||
handleChange={handleChange}
|
||||
passwordStates={passwordStates}
|
||||
handlePasswordStates={handlePasswordStates}
|
||||
/>
|
||||
)}
|
||||
{row.type === "checkbox" && (
|
||||
<CheckboxField
|
||||
row={row}
|
||||
index={index}
|
||||
handleChange={handleChange}
|
||||
/>
|
||||
)}
|
||||
{row.type === "select" && (
|
||||
<SelectField
|
||||
row={row}
|
||||
index={index}
|
||||
handleChange={handleChange}
|
||||
/>
|
||||
)}
|
||||
{row.type === "number" && (
|
||||
<NumberField
|
||||
row={row}
|
||||
index={index}
|
||||
handleChange={handleChange}
|
||||
/>
|
||||
)}
|
||||
{row.type === "slider" && (
|
||||
<SliderField
|
||||
row={row}
|
||||
index={index}
|
||||
handleChange={handleChange}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button mr={3} onClick={handleClose}>
|
||||
|
||||
@@ -1,46 +1,11 @@
|
||||
import { useNuiEvent } from "../../../hooks/useNuiEvent";
|
||||
import { Box, Text, Flex, ScaleFade } from "@chakra-ui/react";
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ContextMenuProps } from "../../../interfaces/context";
|
||||
import Item from "./Item";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { fetchNui } from "../../../utils/fetchNui";
|
||||
|
||||
debugData<ContextMenuProps>([
|
||||
{
|
||||
action: "showContext",
|
||||
data: {
|
||||
title: "Vehicle garage",
|
||||
options: [
|
||||
{ title: "Empty button" },
|
||||
{
|
||||
title: "Example button",
|
||||
description: "Example button description",
|
||||
icon: "inbox",
|
||||
image: "https://i.imgur.com/YAe7k17.jpeg",
|
||||
metadata: [{ label: "Value 1", value: 300 }],
|
||||
},
|
||||
{
|
||||
title: "Menu button",
|
||||
icon: "bars",
|
||||
menu: "other_example_menu",
|
||||
description: "Takes you to another menu",
|
||||
metadata: ["It also has metadata support"],
|
||||
},
|
||||
{
|
||||
title: "Event button",
|
||||
description: "Open a menu and send event data",
|
||||
icon: "check",
|
||||
arrow: true,
|
||||
event: "some_event",
|
||||
args: { value1: 300, value2: "Other value" },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const openMenu = (id: string | undefined) => {
|
||||
fetchNui<ContextMenuProps>("openContext", id);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Box, Stack, Tooltip } from "@chakra-ui/react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useNuiEvent } from "../../../hooks/useNuiEvent";
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
import ListItem from "./ListItem";
|
||||
import Header from "./Header";
|
||||
import FocusTrap from "focus-trap-react";
|
||||
@@ -19,42 +18,12 @@ export interface MenuItem {
|
||||
close?: boolean;
|
||||
}
|
||||
|
||||
interface MenuSettings {
|
||||
export interface MenuSettings {
|
||||
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
||||
title: string;
|
||||
items: Array<MenuItem>;
|
||||
}
|
||||
|
||||
debugData<MenuSettings>([
|
||||
{
|
||||
action: "setMenu",
|
||||
data: {
|
||||
// position: "bottom-left",
|
||||
title: "Vehicle garage",
|
||||
items: [
|
||||
{ label: "Option 1", icon: "heart" },
|
||||
{
|
||||
label: "Option 2",
|
||||
icon: "basket-shopping",
|
||||
description: "Tooltip description 1",
|
||||
},
|
||||
{
|
||||
label: "Vehicle class",
|
||||
values: ["Nice", "Super nice", "Extra nice"],
|
||||
icon: "tag",
|
||||
description: "Tooltip description 2",
|
||||
},
|
||||
{ label: "Option 1" },
|
||||
{ label: "Option 2" },
|
||||
{ label: "Vehicle class", values: ["Nice", "Super nice", "Extra nice"], defaultIndex: 1 },
|
||||
{ label: "Option 1" },
|
||||
{ label: "Option 2" },
|
||||
{ label: "Vehicle class", values: ["Nice", "Super nice", "Extra nice"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const ListMenu: React.FC = () => {
|
||||
const [menu, setMenu] = useState<MenuSettings>({
|
||||
position: "top-left",
|
||||
@@ -90,7 +59,8 @@ const ListMenu: React.FC = () => {
|
||||
setIndexStates({
|
||||
...indexStates,
|
||||
[selected]:
|
||||
indexStates[selected] + 1 <= menu.items[selected].values?.length! - 1
|
||||
indexStates[selected] + 1 <=
|
||||
menu.items[selected].values?.length! - 1
|
||||
? indexStates[selected] + 1
|
||||
: (indexStates[selected] = 0),
|
||||
});
|
||||
@@ -102,13 +72,18 @@ const ListMenu: React.FC = () => {
|
||||
[selected]:
|
||||
indexStates[selected] - 1 >= 0
|
||||
? indexStates[selected] - 1
|
||||
: (indexStates[selected] = menu.items[selected].values?.length! - 1),
|
||||
: (indexStates[selected] =
|
||||
menu.items[selected].values?.length! - 1),
|
||||
});
|
||||
break;
|
||||
case "Enter":
|
||||
if (!menu.items[selected]) return;
|
||||
fetchNui("confirmSelected", [selected, indexStates[selected]]).catch();
|
||||
if (menu.items[selected].close === undefined || menu.items[selected].close) setVisible(false);
|
||||
if (
|
||||
menu.items[selected].close === undefined ||
|
||||
menu.items[selected].close
|
||||
)
|
||||
setVisible(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -157,7 +132,8 @@ const ListMenu: React.FC = () => {
|
||||
setVisible(true);
|
||||
let arrayIndexes: { [key: number]: number } = {};
|
||||
for (let i = 0; i < data.items.length; i++) {
|
||||
if (Array.isArray(data.items[i].values)) arrayIndexes[i] = (data.items[i].defaultIndex || 1) - 1;
|
||||
if (Array.isArray(data.items[i].values))
|
||||
arrayIndexes[i] = (data.items[i].defaultIndex || 1) - 1;
|
||||
}
|
||||
setIndexStates(arrayIndexes);
|
||||
listRefs.current[0]?.focus();
|
||||
@@ -178,13 +154,39 @@ const ListMenu: React.FC = () => {
|
||||
<Box
|
||||
position="absolute"
|
||||
pointerEvents="none"
|
||||
mt={menu.position === "top-left" || menu.position === "top-right" ? 5 : 0}
|
||||
ml={menu.position === "top-left" || menu.position === "bottom-left" ? 5 : 0}
|
||||
mr={menu.position === "top-right" || menu.position === "bottom-right" ? 5 : 0}
|
||||
mb={menu.position === "bottom-left" || menu.position === "bottom-right" ? 5 : 0}
|
||||
right={menu.position === "top-right" || menu.position === "bottom-right" ? 1 : undefined}
|
||||
mt={
|
||||
menu.position === "top-left" || menu.position === "top-right"
|
||||
? 5
|
||||
: 0
|
||||
}
|
||||
ml={
|
||||
menu.position === "top-left" || menu.position === "bottom-left"
|
||||
? 5
|
||||
: 0
|
||||
}
|
||||
mr={
|
||||
menu.position === "top-right" || menu.position === "bottom-right"
|
||||
? 5
|
||||
: 0
|
||||
}
|
||||
mb={
|
||||
menu.position === "bottom-left" ||
|
||||
menu.position === "bottom-right"
|
||||
? 5
|
||||
: 0
|
||||
}
|
||||
right={
|
||||
menu.position === "top-right" || menu.position === "bottom-right"
|
||||
? 1
|
||||
: undefined
|
||||
}
|
||||
left={menu.position === "bottom-left" ? 1 : undefined}
|
||||
bottom={menu.position === "bottom-left" || menu.position === "bottom-right" ? 1 : undefined}
|
||||
bottom={
|
||||
menu.position === "bottom-left" ||
|
||||
menu.position === "bottom-right"
|
||||
? 1
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Header title={menu.title} />
|
||||
<Box
|
||||
@@ -192,7 +194,11 @@ const ListMenu: React.FC = () => {
|
||||
height="fit-content"
|
||||
maxHeight={415}
|
||||
overflow="hidden"
|
||||
borderRadius={menu.items.length <= 6 || selected === menu.items.length - 1 ? 'md' : undefined}
|
||||
borderRadius={
|
||||
menu.items.length <= 6 || selected === menu.items.length - 1
|
||||
? "md"
|
||||
: undefined
|
||||
}
|
||||
bg="#141517"
|
||||
fontFamily="Nunito"
|
||||
borderTopLeftRadius="none"
|
||||
@@ -204,7 +210,12 @@ const ListMenu: React.FC = () => {
|
||||
{menu.items.map((item, index) => (
|
||||
<React.Fragment key={`menu-item-${index}`}>
|
||||
{item.label && (
|
||||
<ListItem index={index} item={item} scrollIndex={indexStates[index]} ref={listRefs} />
|
||||
<ListItem
|
||||
index={index}
|
||||
item={item}
|
||||
scrollIndex={indexStates[index]}
|
||||
ref={listRefs}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
@@ -212,8 +223,18 @@ const ListMenu: React.FC = () => {
|
||||
</FocusTrap>
|
||||
</Box>
|
||||
{menu.items.length > 6 && selected !== menu.items.length - 1 && (
|
||||
<Box bg="#141517" textAlign="center" borderBottomLeftRadius="md" borderBottomRightRadius="md" height={25}>
|
||||
<FontAwesomeIcon icon="chevron-down" color="#909296" fontSize={20} />
|
||||
<Box
|
||||
bg="#141517"
|
||||
textAlign="center"
|
||||
borderBottomLeftRadius="md"
|
||||
borderBottomRightRadius="md"
|
||||
height={25}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon="chevron-down"
|
||||
color="#909296"
|
||||
fontSize={20}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -6,11 +6,10 @@ import {
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { useNuiEvent } from "../../hooks/useNuiEvent";
|
||||
import { debugData } from "../../utils/debugData";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
interface Props {
|
||||
export interface NotificationProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
duration?: number;
|
||||
@@ -19,7 +18,7 @@ interface Props {
|
||||
id?: number;
|
||||
}
|
||||
|
||||
interface CustomProps {
|
||||
export interface CustomNotificationProps {
|
||||
style?: React.CSSProperties;
|
||||
description?: string;
|
||||
title?: string;
|
||||
@@ -31,35 +30,10 @@ interface CustomProps {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
debugData<Props>([
|
||||
{
|
||||
action: "notify",
|
||||
data: {
|
||||
description: "Dunak is nerd",
|
||||
title: "Dunak",
|
||||
id: 1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
debugData<CustomProps>([
|
||||
{
|
||||
action: "customNotify",
|
||||
data: {
|
||||
description: "Dunak is nerd",
|
||||
icon: "basket-shopping",
|
||||
style: {
|
||||
backgroundColor: "#2D3748",
|
||||
color: "white",
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const Notifications: React.FC = () => {
|
||||
const toast = useToast();
|
||||
|
||||
useNuiEvent<CustomProps>("customNotify", (data) => {
|
||||
useNuiEvent<CustomNotificationProps>("customNotify", (data) => {
|
||||
if (!data.title && !data.description) return;
|
||||
if (data.id && toast.isActive(data.id)) return;
|
||||
if (!data.icon) {
|
||||
@@ -71,7 +45,7 @@ const Notifications: React.FC = () => {
|
||||
: "circle-info";
|
||||
}
|
||||
|
||||
const id = data.id
|
||||
const id = data.id;
|
||||
toast({
|
||||
id,
|
||||
duration: data.duration || 3000,
|
||||
@@ -104,10 +78,10 @@ const Notifications: React.FC = () => {
|
||||
});
|
||||
});
|
||||
|
||||
useNuiEvent<Props>("notify", (data) => {
|
||||
useNuiEvent<NotificationProps>("notify", (data) => {
|
||||
if (!data.title && !data.description) return;
|
||||
if (data.id && toast.isActive(data.id)) return;
|
||||
const id = data.id
|
||||
const id = data.id;
|
||||
toast({
|
||||
id,
|
||||
title: data.title,
|
||||
|
||||
@@ -6,26 +6,15 @@ import {
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { useNuiEvent } from "../../hooks/useNuiEvent";
|
||||
import { debugData } from "../../utils/debugData";
|
||||
import { fetchNui } from "../../utils/fetchNui";
|
||||
|
||||
interface Props {
|
||||
export interface CircleProgressbarProps {
|
||||
label?: string;
|
||||
duration: number;
|
||||
position?: "middle" | "bottom";
|
||||
percent?: boolean;
|
||||
}
|
||||
|
||||
debugData([
|
||||
{
|
||||
action: "circleProgress",
|
||||
data: {
|
||||
duration: 8000,
|
||||
label: "Using Armour",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const CircleProgressbar: React.FC = () => {
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
const [progressDuration, setProgressDuration] = React.useState(0);
|
||||
@@ -47,7 +36,7 @@ const CircleProgressbar: React.FC = () => {
|
||||
|
||||
useNuiEvent("progressCancel", progressCancel);
|
||||
|
||||
useNuiEvent<Props>("circleProgress", (data) => {
|
||||
useNuiEvent<CircleProgressbarProps>("circleProgress", (data) => {
|
||||
if (visible) return;
|
||||
setCancelled(false);
|
||||
setVisible(true);
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
import React from "react";
|
||||
import { Text, Flex, Box } from "@chakra-ui/react";
|
||||
import { useNuiEvent } from "../../hooks/useNuiEvent";
|
||||
import { debugData } from "../../utils/debugData";
|
||||
import { fetchNui } from "../../utils/fetchNui";
|
||||
|
||||
interface Props {
|
||||
export interface ProgressbarProps {
|
||||
label: string;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
debugData([
|
||||
{
|
||||
action: "progress",
|
||||
data: {
|
||||
label: "Using Lockpick",
|
||||
duration: 8000,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const Progressbar: React.FC = () => {
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
const [label, setLabel] = React.useState("");
|
||||
@@ -37,7 +26,7 @@ const Progressbar: React.FC = () => {
|
||||
|
||||
useNuiEvent("progressCancel", progressCancel);
|
||||
|
||||
useNuiEvent<Props>("progress", (data) => {
|
||||
useNuiEvent<ProgressbarProps>("progress", (data) => {
|
||||
setCancelled(false);
|
||||
setVisible(true);
|
||||
setLabel(data.label);
|
||||
|
||||
@@ -11,17 +11,9 @@ import {
|
||||
import LocaleSetting from "./components/locale";
|
||||
import { useState } from "react";
|
||||
import { useNuiEvent } from "../../hooks/useNuiEvent";
|
||||
// import { debugData } from "../../utils/debugData";
|
||||
import { fetchNui } from "../../utils/fetchNui";
|
||||
import { useLocales } from "../../providers/LocaleProvider";
|
||||
|
||||
// debugData([
|
||||
// {
|
||||
// action: "openSettings",
|
||||
// data: true,
|
||||
// },
|
||||
// ]);
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { locale } = useLocales();
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React from "react";
|
||||
import { useNuiEvent } from "../../hooks/useNuiEvent";
|
||||
import { Box, Flex, ScaleFade } from "@chakra-ui/react";
|
||||
import { debugData } from "../../utils/debugData";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
|
||||
interface Props {
|
||||
export interface TextUiProps {
|
||||
text: string;
|
||||
position?: "right-center" | "left-center" | "top-center";
|
||||
icon?: IconProp;
|
||||
@@ -14,25 +13,14 @@ interface Props {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
debugData([
|
||||
{
|
||||
action: "textUi",
|
||||
data: {
|
||||
text: "[E] - Access locker inventory \n [G] - Do something else",
|
||||
position: "right-center",
|
||||
icon: "door-open",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const TextUI: React.FC = () => {
|
||||
const [data, setData] = React.useState<Props>({
|
||||
const [data, setData] = React.useState<TextUiProps>({
|
||||
text: "",
|
||||
position: "right-center",
|
||||
});
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
|
||||
useNuiEvent<Props>("textUi", (data) => {
|
||||
useNuiEvent<TextUiProps>("textUi", (data) => {
|
||||
if (!data.position) data.position = "right-center"; // Default right position
|
||||
setData(data);
|
||||
setVisible(true);
|
||||
|
||||
Reference in New Issue
Block a user