mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-17 15:06:02 +01:00
feat(nui): Initial NUI files
This commit is contained in:
9
web/src/components/App.tsx
Normal file
9
web/src/components/App.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<></>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
49
web/src/hooks/useNuiEvent.ts
Normal file
49
web/src/hooks/useNuiEvent.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import {MutableRefObject, useEffect, useRef} from "react";
|
||||
import {noop} from "../utils/misc";
|
||||
|
||||
interface NuiMessageData<T = unknown> {
|
||||
action: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
type NuiHandlerSignature<T> = (data: T) => void;
|
||||
|
||||
/**
|
||||
* A hook that manage events listeners for receiving data from the client scripts
|
||||
* @param action The specific `action` that should be listened for.
|
||||
* @param handler The callback function that will handle data relayed by this hook
|
||||
*
|
||||
* @example
|
||||
* useNuiEvent<{visibility: true, wasVisible: 'something'}>('setVisible', (data) => {
|
||||
* // whatever logic you want
|
||||
* })
|
||||
*
|
||||
**/
|
||||
|
||||
export const useNuiEvent = <T = any>(
|
||||
action: string,
|
||||
handler: (data: T) => void
|
||||
) => {
|
||||
const savedHandler: MutableRefObject<NuiHandlerSignature<T>> = useRef(noop);
|
||||
|
||||
// Make sure we handle for a reactive handler
|
||||
useEffect(() => {
|
||||
savedHandler.current = handler;
|
||||
}, [handler]);
|
||||
|
||||
useEffect(() => {
|
||||
const eventListener = (event: MessageEvent<NuiMessageData<T>>) => {
|
||||
const { action: eventAction, data } = event.data;
|
||||
|
||||
if (savedHandler.current) {
|
||||
if (eventAction === action) {
|
||||
savedHandler.current(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", eventListener);
|
||||
// Remove Event Listener on component cleanup
|
||||
return () => window.removeEventListener("message", eventListener);
|
||||
}, [action]);
|
||||
};
|
||||
19
web/src/index.css
Normal file
19
web/src/index.css
Normal file
@@ -0,0 +1,19 @@
|
||||
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;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
18
web/src/index.tsx
Normal file
18
web/src/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import App from "./components/App";
|
||||
import { VisibilityProvider } from "./providers/VisibilityProvider";
|
||||
import { ChakraProvider } from "@chakra-ui/react";
|
||||
import { theme } from "./theme";
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<VisibilityProvider>
|
||||
<ChakraProvider theme={theme}>
|
||||
<App />
|
||||
</ChakraProvider>
|
||||
</VisibilityProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
37
web/src/providers/VisibilityProvider.tsx
Normal file
37
web/src/providers/VisibilityProvider.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React, { Context, createContext, useContext, useState } from "react";
|
||||
import { useNuiEvent } from "../hooks/useNuiEvent";
|
||||
|
||||
const VisibilityCtx = createContext<VisibilityProviderValue | null>(null);
|
||||
|
||||
interface VisibilityProviderValue {
|
||||
setVisible: (visible: boolean) => void;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
// This should be mounted at the top level of your application, it is currently set to
|
||||
// apply a CSS visibility value. If this is non-performant, this should be customized.
|
||||
export const VisibilityProvider: React.FC = ({ children }) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
useNuiEvent<boolean>("setVisible", setVisible);
|
||||
|
||||
return (
|
||||
<VisibilityCtx.Provider
|
||||
value={{
|
||||
visible,
|
||||
setVisible,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{ visibility: visible ? "visible" : "hidden", height: "100%" }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</VisibilityCtx.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useVisibility = () =>
|
||||
useContext<VisibilityProviderValue>(
|
||||
VisibilityCtx as Context<VisibilityProviderValue>
|
||||
);
|
||||
1
web/src/react-app-env.d.ts
vendored
Normal file
1
web/src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
8
web/src/theme/index.ts
Normal file
8
web/src/theme/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { extendTheme, type ThemeConfig } from "@chakra-ui/react";
|
||||
|
||||
const config: ThemeConfig = {
|
||||
initialColorMode: 'dark',
|
||||
useSystemColorMode: false
|
||||
}
|
||||
|
||||
export const theme = extendTheme({config})
|
||||
30
web/src/utils/debugData.ts
Normal file
30
web/src/utils/debugData.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {isEnvBrowser} from "./misc";
|
||||
|
||||
interface DebugEvent<T = any> {
|
||||
action: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emulates dispatching an event using SendNuiMessage in the lua scripts.
|
||||
* This is used when developing in browser
|
||||
*
|
||||
* @param events - The event you want to cover
|
||||
* @param timer - How long until it should trigger (ms)
|
||||
*/
|
||||
export const debugData = <P>(events: DebugEvent<P>[], timer = 1000): void => {
|
||||
if (process.env.NODE_ENV === "development" && isEnvBrowser()) {
|
||||
for (const event of events) {
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(
|
||||
new MessageEvent("message", {
|
||||
data: {
|
||||
action: event.action,
|
||||
data: event.data,
|
||||
},
|
||||
})
|
||||
);
|
||||
}, timer);
|
||||
}
|
||||
}
|
||||
};
|
||||
28
web/src/utils/fetchNui.ts
Normal file
28
web/src/utils/fetchNui.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Simple wrapper around fetch API tailored for CEF/NUI use. This abstraction
|
||||
* can be extended to include AbortController if needed or if the response isn't
|
||||
* JSON. Tailor it to your needs.
|
||||
*
|
||||
* @param eventName - The endpoint eventname to target
|
||||
* @param data - Data you wish to send in the NUI Callback
|
||||
*
|
||||
* @return returnData - A promise for the data sent back by the NuiCallbacks CB argument
|
||||
*/
|
||||
|
||||
export async function fetchNui<T = any>(eventName: string, data?: any): Promise<T> {
|
||||
const options = {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
|
||||
const resourceName = (window as any).GetParentResourceName ? (window as any).GetParentResourceName() : 'nui-frame-app';
|
||||
|
||||
const resp = await fetch(`https://${resourceName}/${eventName}`, options);
|
||||
|
||||
const respFormatted = await resp.json()
|
||||
|
||||
return respFormatted
|
||||
}
|
||||
6
web/src/utils/misc.ts
Normal file
6
web/src/utils/misc.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// Will return whether the current environment is in a regular browser
|
||||
// and not CEF
|
||||
export const isEnvBrowser = (): boolean => !(window as any).invokeNative
|
||||
|
||||
// Basic no operation function
|
||||
export const noop = () => {}
|
||||
Reference in New Issue
Block a user