mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-18 07:26:02 +01:00
refactor(menu): split side and vertical scroll change handlers
Resolves an issue where changing to the next button while scrolling a horizontal list would not trigger the onChange function for the previous button
This commit is contained in:
@@ -36,28 +36,27 @@ function lib.getOpenMenu() return openMenu end
|
|||||||
|
|
||||||
RegisterNUICallback('confirmSelected', function(data, cb)
|
RegisterNUICallback('confirmSelected', function(data, cb)
|
||||||
cb(1)
|
cb(1)
|
||||||
local selected = type(data) == 'number' and data+1 or data[1]
|
local selected = {data[1] + 1, data[2] and data[2] + 1} -- data = [selected, scrollIndex]
|
||||||
local menu = registeredMenus[openMenu]
|
local menu = registeredMenus[openMenu]
|
||||||
if menu.options[selected].close ~= false then SetNuiFocus(false, false) end
|
if menu.options[selected[1]].close ~= false then SetNuiFocus(false, false) end
|
||||||
local args = menu.options[selected].args
|
local args = menu.options[selected[1]].args
|
||||||
if type(data) == 'number' then
|
registeredMenus[openMenu].cb(selected[1], selected[2], args)
|
||||||
registeredMenus[openMenu].cb(data, nil, args)
|
end)
|
||||||
else
|
|
||||||
registeredMenus[openMenu].cb(data[1], data[2], args)
|
RegisterNUICallback('changeIndex', function(data, cb)
|
||||||
end
|
cb(1)
|
||||||
|
if not registeredMenus[openMenu].onSideScroll then return end
|
||||||
|
local selected = {data[1] + 1, data[2] and data[2] + 1} -- data = [selected, scrollIndex]
|
||||||
|
local args = registeredMenus[openMenu].options[selected[1]].args
|
||||||
|
registeredMenus[openMenu].onSideScroll(selected[1], selected[2], args)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
RegisterNUICallback('changeSelected', function(data, cb)
|
RegisterNUICallback('changeSelected', function(data, cb)
|
||||||
cb(1)
|
cb(1)
|
||||||
if not registeredMenus[openMenu].onChange then return end
|
if not registeredMenus[openMenu].onSelected then return end
|
||||||
local selected = data
|
local selected = {data[1] + 1, data[2] and data[2] + 1} -- data = [selected, scrollIndex]
|
||||||
if type(selected) == 'number' then
|
local args = registeredMenus[openMenu].options[selected[1]].args
|
||||||
local args = registeredMenus[openMenu].options[selected+1].args
|
registeredMenus[openMenu].onSelected(selected[1], selected[2], args)
|
||||||
registeredMenus[openMenu].onChange(selected, nil, args)
|
|
||||||
else
|
|
||||||
local args = registeredMenus[openMenu].options[selected[1]+1].args
|
|
||||||
registeredMenus[openMenu].onChange(selected[1], selected[2], args)
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
RegisterNUICallback('closeMenu', function(data, cb)
|
RegisterNUICallback('closeMenu', function(data, cb)
|
||||||
@@ -88,10 +87,17 @@ RegisterCommand('testMenu', function()
|
|||||||
--lib.setMenuOptions('epic_menu', {label = 'Not nice'}, 1)
|
--lib.setMenuOptions('epic_menu', {label = 'Not nice'}, 1)
|
||||||
--lib.showMenu('epic_menu')
|
--lib.showMenu('epic_menu')
|
||||||
end,
|
end,
|
||||||
|
onSelected = function(selected, scrollIndex, args)
|
||||||
|
print('selected: ', selected, scrollIndex)
|
||||||
|
end,
|
||||||
|
onSideScroll = function(selected, scrollIndex, args)
|
||||||
|
print('sideScroll: ', selected, scrollIndex)
|
||||||
|
end,
|
||||||
options = {
|
options = {
|
||||||
{label = 'Extra nice option', args = 'Hello there', close = false},
|
{label = 'Extra nice option', args = 'Hello there', close = false},
|
||||||
{label = 'Giga nice option'},
|
{label = 'Giga nice option'},
|
||||||
{label = 'Omega nice option'},
|
{label = 'Omega nice option'},
|
||||||
|
{label = 'Values', values={'hello', 'there', 'general', 'kenobi'}}
|
||||||
}
|
}
|
||||||
}, function(selected, scrollIndex, args)
|
}, function(selected, scrollIndex, args)
|
||||||
print(selected, scrollIndex, args)
|
print(selected, scrollIndex, args)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import FocusTrap from "focus-trap-react";
|
|||||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||||
import { fetchNui } from "../../../utils/fetchNui";
|
import { fetchNui } from "../../../utils/fetchNui";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
export interface MenuItem {
|
export interface MenuItem {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -106,15 +107,20 @@ const ListMenu: React.FC = () => {
|
|||||||
break;
|
break;
|
||||||
case "Enter":
|
case "Enter":
|
||||||
if (!menu.items[selected]) return;
|
if (!menu.items[selected]) return;
|
||||||
fetchNui(
|
fetchNui("confirmSelected", [selected, indexStates[selected]]).catch();
|
||||||
"confirmSelected",
|
|
||||||
Array.isArray(menu.items[selected].values) ? [selected, indexStates[selected]] : selected
|
|
||||||
);
|
|
||||||
if (menu.items[selected].close === undefined || menu.items[selected].close) setVisible(false);
|
if (menu.items[selected].close === undefined || menu.items[selected].close) setVisible(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!menu.items[selected]?.values) return;
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
fetchNui("changeIndex", [selected, indexStates[selected]]).catch();
|
||||||
|
}, 100);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [indexStates]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!menu.items[selected]) return;
|
if (!menu.items[selected]) return;
|
||||||
listRefs.current[selected]?.scrollIntoView({
|
listRefs.current[selected]?.scrollIntoView({
|
||||||
@@ -124,13 +130,10 @@ const ListMenu: React.FC = () => {
|
|||||||
listRefs.current[selected]?.focus({ preventScroll: true });
|
listRefs.current[selected]?.focus({ preventScroll: true });
|
||||||
// debounces the callback to avoid spam
|
// debounces the callback to avoid spam
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
fetchNui(
|
fetchNui("changeSelected", [selected, indexStates[selected]]).catch();
|
||||||
"changeSelected",
|
}, 100);
|
||||||
Array.isArray(menu.items[selected].values) ? [selected, indexStates[selected]] : selected
|
|
||||||
);
|
|
||||||
}, 500);
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, [selected, menu, indexStates]);
|
}, [selected, menu]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
@@ -197,17 +200,11 @@ const ListMenu: React.FC = () => {
|
|||||||
<FocusTrap active={visible}>
|
<FocusTrap active={visible}>
|
||||||
<Stack direction="column" p={2} overflowY="scroll">
|
<Stack direction="column" p={2} overflowY="scroll">
|
||||||
{menu.items.map((item, index) => (
|
{menu.items.map((item, index) => (
|
||||||
<>
|
<React.Fragment key={`menu-item-${index}`}>
|
||||||
{item.label && (
|
{item.label && (
|
||||||
<ListItem
|
<ListItem index={index} item={item} scrollIndex={indexStates[index]} ref={listRefs} />
|
||||||
index={index}
|
|
||||||
item={item}
|
|
||||||
scrollIndex={indexStates[index]}
|
|
||||||
ref={listRefs}
|
|
||||||
key={`menu-item-${index}`}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
|
|||||||
Reference in New Issue
Block a user