209 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| --[[
 | |
|     https://github.com/overextended/ox_lib
 | |
| 
 | |
|     This file is licensed under LGPL-3.0 or higher <https://www.gnu.org/licenses/lgpl-3.0.en.html>
 | |
| 
 | |
|     Copyright © 2025 Linden <https://github.com/thelindat>
 | |
| ]]
 | |
| 
 | |
| ---@type { [string]: MenuProps }
 | |
| local registeredMenus = {}
 | |
| ---@type MenuProps | nil
 | |
| local openMenu
 | |
| 
 | |
| ---@alias MenuPosition 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
 | |
| ---@alias MenuChangeFunction fun(selected: number, scrollIndex?: number, args?: any, checked?: boolean)
 | |
| 
 | |
| ---@class MenuOptions
 | |
| ---@field label string
 | |
| ---@field progress? number
 | |
| ---@field colorScheme? string
 | |
| ---@field icon? string | {[1]: IconProp, [2]: string};
 | |
| ---@field iconColor? string
 | |
| ---@field values? table<string | { label: string, description: string }>
 | |
| ---@field checked? boolean
 | |
| ---@field description? string
 | |
| ---@field defaultIndex? number
 | |
| ---@field args? {[any]: any}
 | |
| ---@field close? boolean
 | |
| 
 | |
| ---@class MenuProps
 | |
| ---@field id string
 | |
| ---@field title string
 | |
| ---@field options MenuOptions[]
 | |
| ---@field position? MenuPosition
 | |
| ---@field disableInput? boolean
 | |
| ---@field canClose? boolean
 | |
| ---@field onClose? fun(keyPressed?: 'Escape' | 'Backspace')
 | |
| ---@field onSelected? MenuChangeFunction
 | |
| ---@field onSideScroll? MenuChangeFunction
 | |
| ---@field onCheck? MenuChangeFunction
 | |
| ---@field cb? MenuChangeFunction
 | |
| 
 | |
| ---@param data MenuProps
 | |
| ---@param cb? MenuChangeFunction
 | |
| function lib.registerMenu(data, cb)
 | |
|     if not data.id then error('No menu id was provided.') end
 | |
|     if not data.title then error('No menu title was provided.') end
 | |
|     if not data.options then error('No menu options were provided.') end
 | |
|     data.cb = cb
 | |
|     registeredMenus[data.id] = data
 | |
| end
 | |
| 
 | |
| ---@param id string
 | |
| ---@param startIndex? number
 | |
| function lib.showMenu(id, startIndex)
 | |
|     local menu = registeredMenus[id]
 | |
|     if not menu then
 | |
|         error(('No menu with id %s was found'):format(id))
 | |
|     end
 | |
| 
 | |
|     if table.type(menu.options) == 'empty' then
 | |
|         error(('Can\'t open empty menu with id %s'):format(id))
 | |
|     end
 | |
|     
 | |
|     if not openMenu then
 | |
|         local control = cache.game == 'fivem' and 140 or 0xE30CD707
 | |
| 
 | |
|         CreateThread(function()
 | |
|             while openMenu do
 | |
|                 if openMenu.disableInput == nil or openMenu.disableInput then
 | |
|                     DisablePlayerFiring(cache.playerId, true)
 | |
|                     if cache.game == 'fivem' then
 | |
|                         HudWeaponWheelIgnoreSelection()  -- Not a REDM native
 | |
|                     end
 | |
|                     DisableControlAction(0, control, true)
 | |
|                 end
 | |
|                 Wait(0)
 | |
|             end
 | |
|         end)
 | |
|     end
 | |
| 
 | |
|     openMenu = menu
 | |
|     lib.setNuiFocus(not menu.disableInput, true)
 | |
| 
 | |
|     SendNUIMessage({
 | |
|         action = 'setMenu',
 | |
|         data = {
 | |
|             position = menu.position,
 | |
|             canClose = menu.canClose,
 | |
|             title = menu.title,
 | |
|             items = menu.options,
 | |
|             startItemIndex = startIndex and startIndex - 1 or 0
 | |
|         }
 | |
|     })
 | |
| end
 | |
| ---@param onExit boolean?
 | |
| function lib.hideMenu(onExit)
 | |
|     local menu = openMenu
 | |
|     openMenu = nil
 | |
| 
 | |
|     if not menu then return end
 | |
| 
 | |
|     lib.resetNuiFocus()
 | |
| 
 | |
|     if onExit and menu.onClose then
 | |
|         menu.onClose()
 | |
|     end
 | |
| 
 | |
|     SendNUIMessage({
 | |
|         action = 'closeMenu'
 | |
|     })
 | |
| end
 | |
| 
 | |
| ---@param id string
 | |
| ---@param options MenuOptions | MenuOptions[]
 | |
| ---@param index? number
 | |
| function lib.setMenuOptions(id, options, index)
 | |
|     if index then
 | |
|         registeredMenus[id].options[index] = options
 | |
|     else
 | |
|         if not options[1] then error('Invalid override format used, expected table of options.') end
 | |
|         registeredMenus[id].options = options
 | |
|     end
 | |
| end
 | |
| 
 | |
| ---@return string?
 | |
| function lib.getOpenMenu() return openMenu and openMenu.id end
 | |
| 
 | |
| RegisterNUICallback('confirmSelected', function(data, cb)
 | |
|     cb(1)
 | |
|     data[1] += 1 -- selected
 | |
| 
 | |
|     if data[2] then
 | |
|         data[2] += 1 -- scrollIndex
 | |
|     end
 | |
| 
 | |
|     local menu = openMenu
 | |
| 
 | |
|     if not menu then return end
 | |
| 
 | |
|     if menu.options[data[1]].close ~= false then
 | |
|         lib.resetNuiFocus()
 | |
|         openMenu = nil
 | |
|     end
 | |
| 
 | |
|     if menu.cb then
 | |
|         menu.cb(data[1], data[2], menu.options[data[1]].args, data[3])
 | |
|     end
 | |
| end)
 | |
| 
 | |
| RegisterNUICallback('changeIndex', function(data, cb)
 | |
|     cb(1)
 | |
|     if not openMenu or not openMenu.onSideScroll then return end
 | |
| 
 | |
|     data[1] += 1 -- selected
 | |
| 
 | |
|     if data[2] then
 | |
|         data[2] += 1 -- scrollIndex
 | |
|     end
 | |
| 
 | |
|     openMenu.onSideScroll(data[1], data[2], openMenu.options[data[1]].args)
 | |
| end)
 | |
| 
 | |
| RegisterNUICallback('changeSelected', function(data, cb)
 | |
|     cb(1)
 | |
|     if not openMenu or not openMenu.onSelected then return end
 | |
| 
 | |
|     data[1] += 1 -- selected
 | |
| 
 | |
| 
 | |
|     local args = openMenu.options[data[1]].args
 | |
| 
 | |
|     if args and type(args) ~= 'table' then
 | |
|         return error("Menu args must be passed as a table")
 | |
|     end
 | |
| 
 | |
|     if not args then args = {} end
 | |
|     if data[2] then args[data[3]] = true end
 | |
| 
 | |
|     if data[2] and not args.isCheck then
 | |
|         data[2] += 1 -- scrollIndex
 | |
|     end
 | |
| 
 | |
|     openMenu.onSelected(data[1], data[2], args)
 | |
| end)
 | |
| 
 | |
| RegisterNUICallback('changeChecked', function(data, cb)
 | |
|     cb(1)
 | |
|     if not openMenu or not openMenu.onCheck then return end
 | |
| 
 | |
|     data[1] += 1 -- selected
 | |
| 
 | |
|     openMenu.onCheck(data[1], data[2], openMenu.options[data[1]].args)
 | |
| end)
 | |
| 
 | |
| RegisterNUICallback('closeMenu', function(data, cb)
 | |
|     cb(1)
 | |
|     lib.resetNuiFocus()
 | |
| 
 | |
|     local menu = openMenu
 | |
| 
 | |
|     if not menu then return end
 | |
| 
 | |
|     openMenu = nil
 | |
| 
 | |
|     if menu.onClose then
 | |
|         menu.onClose(data --[[@as 'Escape' | 'Backspace' | nil]])
 | |
|     end
 | |
| end)
 | 
