Files
jim_bridge/shared/input.lua
2025-09-25 02:17:48 +01:00

334 lines
13 KiB
Lua

--[[
Input Dialog Module
---------------------
This module provides a function to create a simple input dialog compatible with
multiple menu systems (OX, QB, GTA/WarMenu, and ESX). It supports various input
types such as radio buttons, numbers, text, and select dropdowns.
]]
--- Creates a simple input dialog using the configured menu system.
---
--- @param title string The title or header of the input dialog.
--- @param opts table A table of input option definitions. Each option should include:
--- - type (string): The input type ("radio", "number", "text", "select").
--- - label (string, optional): A label for the input (used in radio/select for OX).
--- - text (string, optional): The text prompt for the input.
--- - name (string): The identifier for the input.
--- - isRequired (boolean, optional): Whether input is mandatory.
--- - default (any, optional): The default value.
--- - options (table, optional): A table of choices for "radio" and "select" types.
--- - min (number, optional): Minimum value (for "number" and "select").
--- - max (number, optional): Maximum value.
--- - txt (string, optional): Additional description.
---
--- @return table|nil table Returns the user's input as a table if submitted, otherwise nil.
---
---@usage
--- ```lua
--- local userInput = createInput("Enter Details", {
--- { type = "text", text = "Name", name = "playerName", isRequired = true },
--- { type = "number", text = "Age", name = "playerAge", min = 18, max = 99 },
--- { type = "radio", label = "Gender", name = "playerGender", options = {
--- { text = "Male", value = "male" },
--- { text = "Female", value = "female" },
--- { text = "Other", value = "other" },
--- }},
--- })
--- ```
function createInput(title, opts)
local dialog = nil
local options = {}
local currentNum = 0
if Config.System.Menu == "ox" then
for i = 1, #opts do
currentNum += 1
if opts[i] == nil then currentNum -= 1 goto skip end
if opts[i].type == "radio" then
-- Convert radio options to select type for OX
for k in pairs(opts[i].options) do
opts[i].options[k].label = opts[i].options[k].text
end
options[currentNum] = {
type = "select",
isRequired = opts[i].isRequired,
label = opts[i].label or opts[i].text,
name = opts[i].name,
default = opts[i].default or opts[i].options[1].value,
options = opts[i].options,
}
end
if opts[i].type == "number" then
options[currentNum] = {
type = opts[i].type,
label = (opts[i].label or opts[i].text)..(opts[i].txt and " - "..opts[i].txt or ""),
isRequired = opts[i].isRequired,
name = opts[i].name,
default = opts[i].default,
options = opts[i].options,
}
end
if opts[i].type == "text" then
options[currentNum] = {
type = "input",
label = opts[i].text..(opts[i].txt and " - "..opts[i].txt or ""),
default = opts[i].default,
isRequired = opts[i].isRequired,
}
end
if opts[i].type == "select" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].text..(opts[i].txt and " - "..opts[i].txt or ""),
isRequired = opts[i].isRequired,
name = opts[i].name,
options = opts[i].options,
min = opts[i].min,
max = opts[i].max,
default = opts[i].default,
}
end
if opts[i].type == "checkbox" then
jsonPrint(opts[i])
for k in pairs(opts[i].options) do
if options[currentNum] then currentNum += 1 end
options[currentNum] = {
type = opts[i].type,
label = opts[i].options[k].text..(opts[i].txt and " - "..opts[i].txt or ""),
name = opts[i].options[k].value,
}
end
end
if opts[i].type == "color" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label,
isRequired = opts[i].isRequired,
format = opts[i].format,
default = opts[i].default,
}
end
if opts[i].type == "slider" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label,
isRequired = opts[i].required,
min = opts[i].min,
max = opts[i].max,
default = opts[i].default,
}
end
::skip::
end
dialog = exports[OXLibExport]:inputDialog(title, options)
return dialog
elseif Config.System.Menu == "qb" then
for k, v in pairs(opts) do
currentNum += 1
if opts[k] == nil then
currentNum -= 1
else
options[currentNum] = opts[k]
end
end
dialog = exports['qb-input']:ShowInput(
{ header = title, submitText = "Accept", inputs = options }
)
return dialog
elseif Config.System.Menu == "lation" then
for i in pairs(opts) do
currentNum += 1
if opts[i] == nil then currentNum -= 1 goto skip end
if opts[i].type == "radio" then
for k in pairs(opts[i].options) do
opts[i].options[k].label = opts[i].options[k].text
end
options[currentNum] = {
type = "select",
required = opts[i].isRequired,
label = opts[i].label or opts[i].text,
name = opts[i].name,
default = opts[i].default or opts[i].options[1].value,
options = opts[i].options,
}
end
if opts[i].type == "number" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label or opts[i].text,
description = opts[i].txt and " - "..opts[i].txt or "",
required = opts[i].isRequired,
name = opts[i].name,
options = opts[i].options,
}
end
if opts[i].type == "text" then
options[currentNum] = {
type = "input",
label = opts[i].label or opts[i].text,
description = (opts[i].txt and " - "..opts[i].txt or ""),
placeholder = opts[i].default,
required = opts[i].isRequired,
}
end
if opts[i].type == "select" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label or opts[i].text,
description = opts[i].txt and " - "..opts[i].txt or "",
required = opts[i].isRequired,
name = opts[i].name,
options = opts[i].options,
min = opts[i].min,
max = opts[i].max,
default = opts[i].default,
}
end
if opts[i].type == "checkbox" then
for k in pairs(opts[i].options) do
if options[currentNum] then currentNum += 1 end
options[currentNum] = {
type = opts[i].type,
label = opts[i].options[k].text..(opts[i].txt and " - "..opts[i].txt or ""),
name = opts[i].options[k].value,
}
end
end
if opts[i].type == "color" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label,
required = opts[i].isRequired,
format = opts[i].format,
default = opts[i].default,
}
end
if opts[i].type == "slider" then
options[currentNum] = {
type = opts[i].type,
label = opts[i].label,
required = opts[i].required,
min = opts[i].min,
max = opts[i].max,
default = opts[i].default,
}
end
::skip::
end
local dialog = exports.lation_ui:input({
title = title,
submitText = "Accept",
options = options
})
return dialog
elseif Config.System.Menu == "gta" then
WarMenu.CreateMenu(tostring(opts),
title,
" ",
{
titleColor = { 222, 255, 255 },
maxOptionCountOnScreen = 15,
width = 0.25,
x = 0.7,
})
if WarMenu.IsAnyMenuOpened() then return end
WarMenu.OpenMenu(tostring(opts))
local close = true
local _comboBoxItems = {}
local _comboBoxIndex = { 1, 1 }
while true do
if WarMenu.Begin(tostring(opts)) then
for i = 1, #opts do
if opts[i].type == "radio" then
for k in pairs(opts[i].options) do
if not _comboBoxItems[i] then _comboBoxItems[i] = {} end
_comboBoxItems[i][k] = opts[i].options[k].text
end
local _, comboBoxIndex = WarMenu.ComboBox(opts[i].label, _comboBoxItems[i], _comboBoxIndex[i])
if _comboBoxIndex[i] ~= comboBoxIndex then
_comboBoxIndex[i] = comboBoxIndex
end
end
if opts[i].type == "number" then
for b = 1, opts[i].max do
if not _comboBoxItems[i] then _comboBoxItems[i] = {} end
_comboBoxItems[i][b] = b
end
local _, comboBoxIndex = WarMenu.ComboBox(opts[i].text, _comboBoxItems[i], _comboBoxIndex[i])
if _comboBoxIndex[i] ~= comboBoxIndex then
_comboBoxIndex[i] = comboBoxIndex
end
end
end
local pressed = WarMenu.Button("Pay")
if pressed then
WarMenu.CloseMenu()
close = false
local result = {}
for i = 1, #_comboBoxIndex do
result[i] = _comboBoxItems[i][_comboBoxIndex[i]]
end
return result
end
WarMenu.End()
else
return
end
if not WarMenu.IsAnyMenuOpened() and close then
if data.onExit then data.onExit() end
end
Wait(0)
end
elseif Config.System.Menu == "esx" then -- horrible input dialog, not even worth using, get OX
local results = {}
for i, opt in ipairs(opts) do
local prompt = opt.text or opt.label or "Enter value"
-- For radio/select types, append available options in the prompt.
if (opt.type == "radio" or opt.type == "select") and opt.options then
local choices = ""
for j, choice in ipairs(opt.options) do
choices = choices .. choice.text .. " (" .. tostring(choice.value) .. ")"
if j < #opt.options then choices = choices .. ", " end
end
prompt = prompt .. " [" .. choices .. "]"
elseif opt.type == "number" then
prompt = prompt .. " (number between " .. (opt.min or 0) .. " and " .. (opt.max or 100) .. ")"
end
local value = nil
ESX.UI.Menu.Open('dialog', getScript(), 'input_' .. i, {
title = prompt
}, function(data, menu)
value = data.value
menu.close()
end, function(data, menu)
menu.close()
end)
-- Wait until the player submits a value.
while value == nil do
Wait(0)
end
-- Convert to a number if needed.
if opt.type == "number" then
value = tonumber(value)
end
results[opt.name or i] = value
end
return results
end
end