Files
red-valley/cache/files/qb-menu/resource.rpf

595 lines
28 KiB
Plaintext
Raw Normal View History

2026-03-29 21:41:17 +03:00
RPF2<00><00><00>(<00>!<00> <00><00>&l0l18;zPz/clientmain.luafxmanifest.luahtmlindex.htmlscript.jsstyle.css
RegisterNetEvent('QBCore:Client:UpdateObject', function() QBCore = exports['qb-core']:GetCoreObject() end)
local headerShown = false
local sendData = nil
-- Functions
local function sortData(data, skipfirst)
local header = data[1]
local tempData = data
if skipfirst then table.remove(tempData,1) end
table.sort(tempData, function(a,b) return a.header < b.header end)
if skipfirst then table.insert(tempData,1,header) end
return tempData
end
local function openMenu(data, sort, skipFirst)
if not data or not next(data) then return end
if sort then data = sortData(data, skipFirst) end
for _,v in pairs(data) do
if v["icon"] then
if QBCore.Shared.Items[tostring(v["icon"])] then
if not string.find(QBCore.Shared.Items[tostring(v["icon"])].image, "//") and not string.find(v["icon"], "//") then
v["icon"] = "nui://qs-inventory/html/images/"..QBCore.Shared.Items[tostring(v["icon"])].image
end
end
end
end
SetNuiFocus(true, true)
headerShown = false
sendData = data
SendNUIMessage({
action = 'OPEN_MENU',
data = table.clone(data),
time = GetClockHours()
})
end
local function openMenuPrison(data, sort, skipFirst)
if not data or not next(data) then return end
if sort then data = sortData(data, skipFirst) end
for _,v in pairs(data) do
if v["icon"] then
if QBCore.Shared.Items[tostring(v["icon"])] then
if not string.find(QBCore.Shared.Items[tostring(v["icon"])].image, "//") and not string.find(v["icon"], "//") then
v["icon"] = "nui://qs-inventory/html/images/"..QBCore.Shared.Items[tostring(v["icon"])].image
end
end
end
end
SetNuiFocus(false, false)
headerShown = false
sendData = data
SendNUIMessage({
action = 'OPEN_MENU',
data = table.clone(data),
time = GetClockHours()
})
end
local function closeMenu()
sendData = nil
headerShown = false
SetNuiFocus(false)
SendNUIMessage({
action = 'CLOSE_MENU'
})
end
local function showHeader(data)
if not data or not next(data) then return end
headerShown = true
sendData = data
SendNUIMessage({
action = 'SHOW_HEADER',
data = table.clone(data),
time = GetClockHours()
})
end
-- Events
RegisterNetEvent('qb-menu:client:openMenu', function(data, sort, skipFirst)
openMenu(data, sort, skipFirst)
end)
RegisterNetEvent('qb-menu:client:closeMenu', function()
closeMenu()
end)
-- NUI Callbacks
RegisterNUICallback('clickedButton', function(option, cb)
if headerShown then headerShown = false end
PlaySoundFrontend(-1, 'Highlight_Cancel', 'DLC_HEIST_PLANNING_BOARD_SOUNDS', 1)
SetNuiFocus(false)
if sendData then
local data = sendData[tonumber(option)]
sendData = nil
if data then
if data.params.event then
if data.params.isServer then
TriggerServerEvent(data.params.event, data.params.args)
elseif data.params.isCommand then
ExecuteCommand(data.params.event)
elseif data.params.isQBCommand then
TriggerServerEvent('QBCore:CallCommand', data.params.event, data.params.args)
elseif data.params.isAction then
data.params.event(data.params.args)
else
TriggerEvent(data.params.event, data.params.args)
end
end
end
end
cb('ok')
end)
RegisterNUICallback('closeMenu', function(_, cb)
headerShown = false
sendData = nil
SetNuiFocus(false)
cb('ok')
TriggerEvent("qb-menu:client:menuClosed")
end)
-- Command and Keymapping
RegisterCommand('playerfocus', function()
if headerShown then
SetNuiFocus(true, true)
end
end)
RegisterKeyMapping('playerFocus', 'Give Menu Focus', 'keyboard', 'LMENU')
-- Exports
exports('openMenu', openMenu)
exports('closeMenu', closeMenu)
exports('showHeader', showHeader)
-- test1
fx_version 'cerulean'
game 'gta5'
description 'Nmsh QBCore Menu'
version '1.2.0'
client_script 'client/main.lua'
ui_page 'html/index.html'
files {
'html/index.html',
'html/script.js',
'html/style.css'
}
lua54 'yes'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nmsh QBCore Menu</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<!-- Font Awesome Icons Import -->
<link rel="stylesheet" href="https://kit-pro.fontawesome.com/releases/v6.5.0/css/pro.min.css">
<link rel="stylesheet" href="./style.css" />
<script src="./script.js" defer></script>
</head>
<body>
<div class="background"></div>
<div id="container">
<div id="buttons"></div>
<div id="imageHover">
<img id="image" />
</div>
</div>
</body>
</html>
let buttonParams = [];
let Button = [];
let MenuHeaderCounter = 0;
function isValidHttpUrl(string) {
try {
const url = new URL(string);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch (err) {
return false;
}
}
const openMenu = (data = null) => {
let html = "";
data.forEach((item, index) => {
if (!item.hidden) {
let header = item.header;
let message = item.txt || item.text;
let isMenuHeader = item.isMenuHeader;
let isDisabled = item.disabled;
let icon = item.icon;
let progressbar = item.ProgressBar
let image = item.image;
html += getButtonRender(header, message, index, isMenuHeader, isDisabled, icon, progressbar);
if (item.params) buttonParams[index] = item.params;
if (item.image) Button[index] = { index, image };
}
});
$("#buttons").html(html);
$('.background').css('display', "block");
$('.button').click(function () {
const target = $(this)
if (!target.hasClass('title') && !target.hasClass('disabled')) {
postData(target.attr('id'));
}
});
};
const getButtonRender = (header, message = null, id, isMenuHeader, isDisabled, icon, progressbar) => {
// console.log(icon)
if (icon !== undefined) {
return `
<div class="${isMenuHeader ? "title" : "button"} ${isDisabled ? "disabled" : ""}" id="${id}">
<div class="icon"> <img src=${icon} width=30px onerror="this.onerror=null; this.remove();">
<i class="${icon}" onerror="this.onerror=null; this.remove();"></i>
</div>
<div class="column">
<div class="header"> ${header}</div>
${message ? `<div class="text">${message}</div>` : ""}
${progressbar ? `<div class='progressbar'><div class='progressbar-bar' style='width: ${progressbar.Value / progressbar.MaxValue * 100}%;'></div></div> <div class='progressbar-info'>${progressbar.Value}/${progressbar.MaxValue}</div>` : ``}
</div>
</div>
`;
} else {
return `
<div class="${isMenuHeader ? "title" : "button"} ${isDisabled ? "disabled" : ""}" id="${id}">
<div class="column">
<div class="header"> ${header}</div>
${message ? `<div class="text">${message}</div>` : ""}
${progressbar ? `<div class='progressbar'><div class='progressbar-bar' style='width: ${progressbar.Value / progressbar.MaxValue * 100}%;'></div></div> <div class='progressbar-info'>${progressbar.Value}/${progressbar.MaxValue}</div>` : ``}
</div>
</div>
`;
}
};
const closeMenu = () => {
$("#buttons").html(" ");
$('.background').css('display', "none");
document.getElementById('imageHover').style.display = 'none';
buttonParams = [];
Button = [];
};
const postData = (id) => {
$.post(`https://${GetParentResourceName()}/clickedButton`, JSON.stringify(parseInt(id) + 1));
return closeMenu();
};
const cancelMenu = () => {
$.post(`https://${GetParentResourceName()}/closeMenu`);
return closeMenu();
};
window.addEventListener("message", (event) => {
const data = event.data;
const buttons = data.data;
const action = data.action;
switch (action) {
case "OPEN_MENU":
case "SHOW_HEADER":
return openMenu(buttons);
case "CLOSE_MENU":
return closeMenu();
default:
return;
}
});
document.onkeyup = function (event) {
const charCode = event.key;
if (charCode == "Escape") {
cancelMenu();
}
};
window.addEventListener('mousemove', (event) => {
let $target = $(event.target);
if ($target.closest('.button:hover').length && $('.button').is(":visible")) {
let id = event.target.id;
if (!Button[id]) return
if (Button[id].image) {
document.getElementById('image').src = Button[id].image;
document.getElementById('imageHover').style.display = 'block';
}
}
else {
document.getElementById('imageHover').style.display = 'none';
}
})
/* Nice font for button from Google Fonts */
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;500&display=swap");
* {
padding: 0;
margin: 0;
font-family: "Poppins", sans-serif;
font-weight: 300;
transition: all 0.5s;
}
@media (width: 3840px) and (height: 2160px) {
#container {
position: absolute;
font-size: 25px !important;
height: auto;
top: 20%;
right: 20%;
border-radius: 5px;
background: transparent !important;
}
#buttons {
font-size: 25px !important;
max-height: 75vh;
width: 300px;
overflow-x: none;
overflow-y: auto;
padding: 10px;
}
div > .text {
flex-direction: column;
font-size: 23px !important;
overflow: hidden;
}
div > .header {
width: 100%;
max-width: 100%;
display: flex;
align-items: center;
position: relative;
justify-content: left;
overflow: wrap;
color: white;
font-size: 25px !important;
font-weight: 400;
overflow: hidden;
}
}
/* width */
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
box-shadow: inset 0 0 5px grey;
border-radius: 10px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #17171781;
border-radius: 10px;
}
#container {
position: absolute;
height: auto;
top: 20%;
right: 7%;
border-radius: 5px;
background: transparent !important;
}
.button {
cursor: pointer;
display: flex;
flex-direction: row !important;
gap: 10px;
}
.title {
cursor: default;
gap: 10px;
display: flex;
flex-direction: row !important;
}
#buttons {
max-height: 75vh;
width: 300px;
overflow-x: none;
overflow-y: auto;
padding: 10px;
}
html, body {
height: 100%;
margin: 0;
}
.background {
display: none;
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(to right, rgba(133,133,133,0) 10%, rgba(133, 133, 133, 0) 30%, rgb(43, 44, 54) 100%);
content: "";
}
.button {
width: auto;
height: 10%;
color: white;
margin: auto;
position: relative;
top: 10%;
margin-top: 0.5rem;
overflow: hidden;
padding: 0.45rem;
border-style: solid;
border-width: 0.15rem;
border-color: rgb(110 110 119 / 32%);
border-radius: 3px;
display: flex;
flex-direction: column;
cursor: pointer;
background: radial-gradient(circle, rgb(211 204 204 / 9%) 0%, rgb(22 22 22 / 7%) 100%);
}
.icon {
display: flex;
align-items: center;
position: relative;
justify-content: center;
}
.icon i {
display: none;
width: 17px;
height: 16px;
border-radius: 10%;
background: radial-gradient(circle, rgb(255 255 255 / 35%) 0%, rgb(77 79 87 / 59%) 100%);
border: 2px solid rgba(204, 204, 204, 0.205);
-webkit-box-shadow: 2px 13px 44px -7px rgba(204, 204, 204);
-moz-box-shadow: 2px 13px 44px -7px rgba(204, 204, 204);
box-shadow: 2px 13px 44px -7px rgba(204, 204, 204);
color: rgb(255, 255, 255, 0.782);
padding: 7px;
padding-left: 6.5px;
padding-right: 6.5px;
display: flex;
align-items: center;
position: relative;
justify-content: center;
}
.disabled > .icon {
color: rgb(186, 81, 84);
display: flex;
align-items: center;
position: relative;
justify-content: left;
}
.disabled > .icon i{
color: rgb(186, 81, 84);
background: radial-gradient(circle, rgba(255, 255, 255, 0.089) 0%, rgba(77, 79, 87, 0.177) 100%);
border: 2px solid rgba(186, 81, 84, 0.623);
background: radial-gradient(circle, rgba(186, 81, 84, 0.684) 0%, rgba(186, 81, 84, 0.256) 100%);
border-color: rgba(186, 81, 84, 0.623);
display: flex;
align-items: center;
position: relative;
justify-content: left;
}
.button:hover .icon > i{
transition: all 0.5s;
color: rgb(216, 124, 67, 0.725);
background: radial-gradient(circle, rgba(94, 59, 38, 0.684) 0%, rgba(79, 49, 31, 0.256) 100%);
border-style: solid;
border-color: rgba(164, 109, 77, 0.525);
-webkit-box-shadow: 13px 13px 44px -7px rgb(229, 99, 12);
-moz-box-shadow: 13px 13px 44px -7px rgb(229, 99, 12);
box-shadow: 13px 13px 44px -7px rgb(229, 99, 12);
}
.button:hover {
background: radial-gradient(circle, rgba(94, 59, 38, 0.684) 0%, rgba(79, 49, 31, 0.256) 100%);
border-color: rgba(164, 109, 77, 0.525);
-webkit-box-shadow: 13px 13px 44px -7px rgb(94, 59, 38, 0.684);
-moz-box-shadow: 13px 13px 44px -7px rgb(94, 59, 38, 0.684);
box-shadow: 13px 13px 44px -7px rgb(94, 59, 38, 0.684);
}
/* .title {
border-style: solid;
border-radius: 3px;
border-width: 0.15rem;
border-color: rgba(110, 110, 119, 0.925);
background: radial-gradient(circle, rgba(255, 255, 255, 0.089) 0%, rgba(77, 79, 87, 0.177) 100%);
width: auto;
height: 10%;
color: white;
margin: auto;
position: relative;
top: 10%;
margin-top: 0.5rem;
overflow: hidden;
padding: 1rem;
border-radius: 3px;
display: flex;
flex-direction: column;
} */
.title {
border-style: solid;
border-radius: 3px;
border-width: 0.15rem;
border-color: rgb(159 86 44 / 52%);
background: radial-gradient(circle, rgba(94, 59, 38, 0.684) 0%, rgb(66 45 32 / 50%) 100%);
width: auto;
height: 10%;
color: white;
margin: auto;
position: relative;
top: 10%;
margin-top: 0.5rem;
overflow: hidden;
padding: 1rem;
border-radius: 0.15rem;
display: flex;
flex-direction: column;
}
.title .icon > i{
transition: all 0.5s;
color: rgb(180, 102, 53);
background: radial-gradient(circle, rgba(94, 59, 38, 0.684) 0%, rgba(79, 49, 31, 0.256) 100%);
border-style: solid;
border-color: rgb(159 86 44 / 52%);
-webkit-box-shadow: 13px 13px 44px -7px rgb(229, 99, 12);
-moz-box-shadow: 13px 13px 44px -7px rgb(229, 99, 12);
box-shadow: 13px 13px 44px -7px rgb(229, 99, 12);
}
.disabled {
/* background: rgba(102, 102, 102, 0.9) !important; */
background: radial-gradient(circle, rgba(105,46,47, 0.089) 0%, rgba(105,46,47, 0.177) 100%) !important;
border-color: rgb(101, 44, 45);
cursor: not-allowed;
}
.disabled:hover {
background: radial-gradient(circle, rgba(133, 58, 59, 0.089) 0%, rgba(105,46,47, 0.177) 100%) !important;
cursor: not-allowed;
border-style: solid;
border-width: 0.15rem;
border-color: rgb(137, 60, 61);
}
div > .text {
flex-direction: column;
font-size: 0.85rem;
overflow: hidden;
}
.header {
width: 100%;
/* height: 100%; */
max-width: 180%;
display: flex;
align-items: center;
position: relative;
justify-content: left;
overflow: wrap;
color: white;
font-size: 1rem;
font-weight: 400;
overflow: hidden;
}