Main/resources/[standalone]/menuv/menuv/components/item.lua
2025-06-07 08:51:21 +02:00

273 lines
No EOL
9.2 KiB
Lua

----------------------- [ MenuV ] -----------------------
-- GitHub: https://github.com/ThymonA/menuv/
-- License: GNU General Public License v3.0
-- https://choosealicense.com/licenses/gpl-3.0/
-- Author: Thymon Arens <contact@arens.io>
-- Name: MenuV
-- Version: 1.4.1
-- Description: FiveM menu library for creating menu's
----------------------- [ MenuV ] -----------------------
local assert = assert
---@type Utilities
local U = assert(Utilities)
local type = assert(type)
local pairs = assert(pairs)
local lower = assert(string.lower)
local upper = assert(string.upper)
local sub = assert(string.sub)
local pack = assert(table.pack)
local unpack = assert(table.unpack)
local insert = assert(table.insert)
local rawset = assert(rawset)
local rawget = assert(rawget)
local setmetatable = assert(setmetatable)
--- FiveM globals
local CreateThread = assert(Citizen.CreateThread)
--- Create a new menu item
---@param info table Menu information
---@return Item New item
function CreateMenuItem(info)
info = U:Ensure(info, {})
local item = {
---@type Menu|nil
__menu = U:Ensure(info.__Menu or info.__menu, { __class = 'Menu', __type = 'Menu' }, true) or nil,
---@type string
__event = U:Ensure(info.PrimaryEvent or info.primaryEvent, 'unknown'),
---@type string
UUID = U:UUID(),
---@type string
Icon = U:Ensure(info.Icon or info.icon, 'none'),
---@type string
Label = U:Ensure(info.Label or info.label, ''),
---@type string
Description = U:Ensure(info.Description or info.description, ''),
---@type any
Value = info.Value or info.value,
---@type table[]
Values = {},
---@type number
Min = U:Ensure(info.Min or info.min, 0),
---@type number
Max = U:Ensure(info.Max or info.max, 0),
---@type boolean
Disabled = U:Ensure(info.Disabled or info.disabled, false),
---@type table
Events = U:Ensure(info.Events or info.events, {}),
---@type boolean
SaveOnUpdate = U:Ensure(info.SaveOnUpdate or info.saveOnUpdate, false),
---@param t Item
---@param event string Name of Event
Trigger = function(t, event, ...)
event = lower(U:Ensure(event, 'unknown'))
if (event == 'unknown') then return end
if (U:StartsWith(event, 'on')) then
event = 'On' .. sub(event, 3):gsub('^%l', upper)
else
event = 'On' .. event:gsub('^%l', upper)
end
if (not U:Any(event, (t.Events or {}), 'key')) then
return
end
local args = pack(...)
for _, v in pairs(t.Events[event]) do
CreateThread(function()
v(t, unpack(args))
end)
end
end,
---@param t Item
---@param event string Name of event
---@param func function|Menu Function or Menu to trigger
On = function(t, event, func)
event = lower(U:Ensure(event, 'unknown'))
if (event == 'unknown') then return end
if (U:StartsWith(event, 'on')) then
event = 'On' .. sub(event, 3):gsub('^%l', upper)
else
event = 'On' .. event:gsub('^%l', upper)
end
if (not U:Any(event, (t.Events or {}), 'key')) then
return
end
local _type = U:Typeof(func)
if (_type == 'Menu') then
local menu_t = {
__class = 'function',
__type = 'function',
func = function(t) MenuV:OpenMenu(t.uuid) end,
uuid = func.UUID or func.uuid or U:UUID()
}
local menu_mt = { __index = menu_t, __call = function(t) t:func() end }
local menu_item = setmetatable(menu_t, menu_mt)
insert(t.Events[event], menu_item)
return
end
func = U:Ensure(func, function() end)
insert(t.Events[event], func)
end,
---@param t Item
---@param k string
---@param v string
Validate = U:Ensure(info.Validate or info.validate, function(t, k, v)
return true
end),
---@param t Item
---@param k string
---@param v string
Parser = U:Ensure(info.Parser or info.parser, function(t, k, v)
return v
end),
---@param t Item
---@param k string
---@param v string
NewIndex = U:Ensure(info.NewIndex or info.newIndex, function(t, k, v)
end),
---@param t Item
---@return any
GetValue = function(t)
local itemType = U:Ensure(t.__type, 'unknown')
if (itemType == 'button' or itemType == 'menu' or itemType == 'unknown') then
return t.Value
end
if (itemType == 'checkbox' or itemType == 'confirm') then
return U:Ensure(t.Value, false)
end
if (itemType == 'slider') then
for _, item in pairs(t.Values) do
if (item.Value == t.Value) then
return item.Value
end
end
return nil
end
if (itemType == 'range') then
local rawValue = U:Ensure(t.Value, 0)
if (t.Min > rawValue) then
return t.Min
end
if (t.Max < rawValue) then
return t.Max
end
return rawValue
end
end,
---@return Menu|nil
GetParentMenu = function(t)
return t.__menu or nil
end
}
item.Events.OnEnter = {}
item.Events.OnLeave = {}
item.Events.OnUpdate = {}
item.Events.OnDestroy = {}
local mt = {
__index = function(t, k)
return rawget(t.data, k)
end,
__tostring = function(t)
return t.UUID
end,
__call = function(t, ...)
if (t.Trigger ~= nil and type(t.Trigger) == 'function') then
t:Trigger(t.__event, ...)
end
end,
__newindex = function(t, k, v)
local key = U:Ensure(k, 'unknown')
local oldValue = rawget(t.data, k)
local checkInput = t.Validate ~= nil and type(t.Validate) == 'function'
local inputParser = t.Parser ~= nil and type(t.Parser) == 'function'
local updateIndexTrigger = t.NewIndex ~= nil and type(t.NewIndex) == 'function'
if (checkInput) then
local result = t:Validate(key, v)
result = U:Ensure(result, true)
if (not result) then
return
end
end
if (inputParser) then
local parsedValue = t:Parser(key, v)
v = parsedValue or v
end
rawset(t.data, k, v)
if (updateIndexTrigger) then
t:NewIndex(key, v)
end
if (t.__menu ~= nil and U:Typeof(t.__menu) == 'Menu' and t.__menu.Trigger ~= nil and U:Typeof( t.__menu.Trigger) == 'function') then
t.__menu:Trigger('update', 'UpdateItem', t)
end
if (key == 'Value' and t.Trigger ~= nil and type(t.Trigger) == 'function') then
t:Trigger('update', key, v, oldValue)
end
end,
__metatable = 'MenuV'
}
---@class Item
---@filed private __event string Name of primary event
---@field public UUID string UUID of Item
---@field public Icon string Icon/Emoji for Item
---@field public Label string Label of Item
---@field public Description string Description of Item
---@field public Value any Value of Item
---@field public Values table[] List of values
---@field public Min number Min range value
---@field public Max number Max range value
---@field public Disabled boolean Disabled state of Item
---@field public SaveOnUpdate boolean Save on `update`
---@field private Events table<string, function[]> List of registered `on` events
---@field public Trigger fun(t: Item, event: string)
---@field public On fun(t: Item, event: string, func: function|Menu)
---@field public Validate fun(t: Item, k: string, v:any)
---@field public NewIndex fun(t: Item, k: string, v: any)
---@field public Parser fun(t: Item, k: string, v: any)
---@field public GetValue fun(t: Item):any
---@field public GetParentMenu func(t: Item):Menu|nil
local i = setmetatable({ data = item, __class = 'Item', __type = U:Ensure(info.Type or info.type, 'unknown') }, mt)
for k, v in pairs(info or {}) do
local key = U:Ensure(k, 'unknown')
if (key == 'unknown') then return end
i:On(key, v)
end
return i
end
_ENV.CreateMenuItem = CreateMenuItem
_G.CreateMenuItem = CreateMenuItem