--[[
Menu Opening Module
---------------------
This module provides a unified function to open menus using the configured menu system.
Supported systems include:
• jim_bridge (built-in nui menu that works on any framework)
• ox (or ox_context)
• qb (using QBMenuExport)
• gta (using WarMenu)
• esx (using ESX.UI.Menu)
]]
local contextFunc = {
["ox"] =
function(Menu, data)
local index = nil
if data.onBack and not data.onSelected then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
title = "Return",
onSelect = data.onBack,
label = "Return",
})
end
for k in pairs(Menu) do
if data.onSelected and Menu[k].arrow then
Menu[k].icon = "fas fa-angle-right"
end
-- If no title, use header or txt as title/label.
if not Menu[k].title then
if Menu[k].header ~= nil and Menu[k].header ~= "" then
Menu[k].title = Menu[k].header
Menu[k].label = Menu[k].header
if Menu[k].txt then Menu[k].description = Menu[k].txt else Menu[k].description = "" end
else
Menu[k].title = Menu[k].txt
Menu[k].label = Menu[k].txt
end
end
-- Copy parameters from 'params' if available.
if Menu[k].params then
Menu[k].event = Menu[k].params.event
Menu[k].args = Menu[k].params.args or {}
end
if Menu[k].isMenuHeader then
Menu[k].readOnly = true
end
end
local menuID = 'Menu'
(data.onSelected and lib.registerMenu or lib.registerContext)({
id = menuID,
title = data.header..br..br..(data.headertxt and data.headertxt or ""),
position = 'top-right',
options = Menu,
canClose = data.canClose and data.canClose or nil,
onClose = (data.onBack and data.onBack) or (data.onExit and data.onExit) or nil,
onExit = data.onExit and data.onExit or nil,
onSelected = data.onSelected and (function(selected) index = selected end) or nil,
}, data.onSelected and (function(x, y, args)
if Menu[x].refresh then
if Menu[x].onSelect then
Menu[x].onSelect()
end
lib.showMenu(menuID, index)
else
if Menu[x].onSelect then
Menu[x].onSelect()
else
lib.showMenu(menuID, index)
end
end
end) or nil)
if data.onSelected then
lib.showMenu(menuID, 1)
else
lib.showContext(menuID)
end
end,
["qb"] =
function(Menu, data)
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
header = " ",
txt = "Return",
params = {
isAction = true,
event = data.onBack,
},
})
elseif data.canClose then
table.insert(Menu, 1, {
icon = "fas fa-circle-xmark",
header = " ",
txt = "Close",
params = {
isAction = true,
event = data.onExit and data.onExit or (function() exports[QBMenuExport]:closeMenu() end),
},
})
end
if data.header ~= nil then
local tempMenu = {}
for k, v in pairs(Menu) do tempMenu[k + 1] = v end
tempMenu[1] = { header = data.header, txt = data.headertxt or "", isMenuHeader = true }
Menu = tempMenu
end
for k in pairs(Menu) do
if not Menu[k].params or not Menu[k].params.event then
Menu[k].params = {
isAction = true,
event = Menu[k].onSelect or function() end,
}
end
if not Menu[k].header then Menu[k].header = " " end
if Menu[k].arrow then Menu[k].icon = "fas fa-angle-right" end
Menu[k].isMenuHeader = Menu[k].isMenuHeader or Menu[k].disable
end
exports[QBMenuExport]:openMenu(Menu)
end,
["gta"] =
function(Menu, data)
WarMenu.CreateMenu(tostring(Menu), data.header, " ", {
titleColor = { 222, 255, 255 },
maxOptionCountOnScreen = 15,
width = 0.25,
x = 0.7,
})
if WarMenu.IsAnyMenuOpened() then return end
WarMenu.OpenMenu(tostring(Menu))
CreateThread(function()
local close = true
while true do
if WarMenu.Begin(tostring(Menu)) then
if data.onBack then
if WarMenu.SpriteButton("Return", 'commonmenu', "arrowleft", 127, 127, 127) then
WarMenu.CloseMenu()
Wait(10)
data.onBack()
end
end
for k in pairs(Menu) do
local pressed = WarMenu.Button(Menu[k].header)
if not Menu[k].header then
Menu[k].header = Menu[k].txt
Menu[k].txt = nil
end
if Menu[k].txt and Menu[k].txt ~= "" and WarMenu.IsItemHovered() then
if Menu[k].disabled or Menu[k].isMenuHeader then
WarMenu.ToolTip("~r~"..Menu[k].txt, 0.18, true)
else
WarMenu.ToolTip(
(Menu[k].blip and "~BLIP_".."8".."~ " or "")..
Menu[k].txt:gsub("%:", ":~g~"):gsub("%\n", "\n~s~"), 0.18,
true)
end
end
if pressed and not Menu[k].isMenuHeader then
WarMenu.CloseMenu()
close = false
Wait(10)
Menu[k].onSelect()
end
end
WarMenu.End()
else
return
end
if not WarMenu.IsAnyMenuOpened() and close then
stopTempCam(cam)
if data.onExit then data.onExit() end
end
Wait(0)
end
end)
end,
["esx"] =
function(Menu, data)
for k in pairs(Menu) do
Menu[k].label = Menu[k].header
Menu[k].name = "button"..k
end
if data.canClose then
table.insert(Menu, 1, {
icon = "fas fa-circle-xmark",
label = "Close",
name = "close",
onSelect = data.onExit,
})
end
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
label = "Return",
name = "return",
onSelect = data.onBack,
})
end
ESX.UI.Menu.Open("default", getScript(), "Example_Menu", {
title = data.header,
align = 'top-right',
elements = Menu,
},
function(menuData, menu)
for k in pairs(Menu) do
if menuData.current.name == Menu[k].name then
menu.close()
Wait(10)
Menu[k].onSelect()
end
end
end,
function(data, menu)
menu.close()
end)
end,
["lation"] =
function(Menu, data)
if data.onBack then
table.insert(Menu, 1, {
icon = "fas fa-circle-arrow-left",
onSelect = data.onBack,
header = "Return",
})
end
for k in pairs(Menu) do
if data.onSelected and Menu[k].arrow then
Menu[k].icon = "fas fa-angle-right"
end
-- If no title, use header or txt as title/label.
if not Menu[k].title then
if Menu[k].header ~= nil and Menu[k].header ~= "" then
Menu[k].title = Menu[k].header
Menu[k].label = Menu[k].header
if Menu[k].txt then Menu[k].description = Menu[k].txt else Menu[k].description = "" end
else
Menu[k].title = Menu[k].txt
Menu[k].label = Menu[k].txt
end
end
-- Copy parameters from 'params' if available.
if Menu[k].params then
Menu[k].event = Menu[k].params.event
Menu[k].args = Menu[k].params.args or {}
end
if Menu[k].isMenuHeader then
Menu[k].readOnly = true
end
end
exports.lation_ui:registerMenu({
id = 'menu',
title = data.header,
onExit = data.onExit and data.onExit or nil,
subtitle = (data.headertxt and data.headertxt or ""),
options = Menu,
})
-- Show menu
exports.lation_ui:showMenu('menu')
end,
}
--- Opens a menu using the configured menu system.
---
--- This function translates code and creates menus in several different menu scripts, depending on the configured menu system specified in `Config.System.Menu`.
---
---@param Menu table A table containing the menu options to display.
--- Each menu item can include:
--- - header (`string`): The text to display for the menu item.
--- - txt (`string`, optional): Additional text or description.
--- - icon (`string`, optional): Icon to display with the menu item.
--- - onSelect (`function`, optional): Function to execute when the menu item is selected.
--- - arrow (`boolean`, optional): Whether to display an arrow next to the item (for certain menus).
--- - params (`table`, optional): Additional parameters, such as events and arguments.
--- - isMenuHeader (`boolean`, optional): Marks the item as a header.
--- - disabled (`boolean`, optional): Disables the menu item if `true`.
---
---@param data table A table containing configuration data for the menu.
--- - header (`string`): The header/title of the menu.
--- - headertxt (`string`, optional): Additional header text.
--- - onBack (`function`, optional): Function to call when the "Return" option is selected.
--- - onExit (`function`, optional): Function to call when the menu is exited.
--- - onSelected (`function`, optional): Function to call when a menu item is selected (for certain menu systems).
--- - canClose (`boolean`, optional): Whether the menu can be closed by the user.
---
---@usage
--- ```lua
--- openMenu({
--- { header = "Option 1", txt = "Description 1", onSelect = function() print("Option 1 selected") end },
--- { header = "Option 2", txt = "Description 2", onSelect = function() print("Option 2 selected") end },
--- }, {
--- header = "Main Menu",
--- headertxt = "Select an option",
--- onBack = function() print("Return selected") end,
--- onExit = function() print("Menu closed") end,
--- canClose = true,
--- })
--- ```
function openMenu(Menu, data)
contextFunc[Config.System.Menu](Menu, data)
end
-- Line break conversion table, basically, to automate the linebreak in functions `br`
-- This is declared and automatically sets it for what menu you are using (depends if one is uding MD formatting or HTML formatting)
local lineBreakConversion = {
["ox"] = "\n",
["gta"] = "\n",
["lation"] = "\n",
["qb"] = "
",
["esx"] = "
",
}
--- A line break constant used for menu header formatting.
br = lineBreakConversion[Config.System.Menu]
--- Checks if the current menu system is 'ox' or 'gta' for formatting purposes.
--- @return boolean boolean True if using ox or gta menus, otherwise false.
--- @usage
--- ```lua
--- if isOx() then
--- -- Use specific formatting
--- end
--- ```
function isOx() return (Config.System.Menu == "ox" or Config.System.Menu == "gta") end
--- Checks if any WarMenu menu is currently open.
---
--- @return boolean boolean Returns `true` if a WarMenu menu is open; otherwise, `false`.
---
--- @usage
--- ```lua
--- if isWarMenuOpen() then
--- -- Do something
--- end
--- ```
function isWarMenuOpen() if Config.System.Menu == "gta" then return WarMenu.IsAnyMenuOpened() else return false end end