Skip to content

Commit

Permalink
fix: fixed all existing bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
ardelan869 committed Oct 15, 2024
1 parent 6523750 commit 84310ca
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 74 deletions.
51 changes: 39 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,53 @@ FiveM native Rage Menu, built with React. [Documentation](https://docs.ardelanya
```lua
--- you can create a menu once and reopen it at any time
--- state will be cached and reused
local menu = Menu:Create('Example', 'Example Subtitle')
local menu = Menu:Create('Example', 'Example Subtitle', nil, nil, '')
-- 520 is a custom width, the default is
local submenu = Menu:Create('Submenu', 'Submenu Subtitle', 520)
submenu:AddButton('Submenu Button'):OnClick(function()
print('Submenu Button Clicked')
end)

menu:AddButton('Button', 'Button Right Label', 'Button Description'):OnClick(function()
print('Button Clicked')
end)

menu:AddSubmenu(submenu, 'Submenu Label', 'Right Label', 'Submenu Description')

local exampleButton = menu:AddButton('Example Button', 'Example Description', {
left = 'shop_ammo_icon'
})
menu:AddSeparator('Separator')

exampleButton:OnClick(function(component)
print(component.label .. ' Clicked!')
local checkbox = menu:AddCheckbox('Checkbox', 'Checkbox Description', {
right = 'card_suit_hearts'
}, true)

checkbox:OnCheck(function(checked)
print('Checkbox Checked', checked)
end)

local exampleSlider = menu:AddSlider('Example Slider', 'Example Description', nil, 100, 0, 10, 50)
menu:AddButton('Disable Checkbox'):OnClick(function()
checkbox:Disable(not checkbox.disabled)
print('Checkbox Disabled', checkbox.disabled)
end)

local removeSliderHandler = exampleSlider:OnChange(function(current)
print('current slider progress', current)
menu:AddButton('Toggle Checkbox Visibility'):OnClick(function()
checkbox:ToggleVisiblity(not checkbox.visible)
print('Checkbox Visibility', checkbox.visible)
end)

exampleSlider:OnClick(function()
removeSliderHandler() -- this function removes the OnChange handler, that's above
menu:AddList('List', 'List Description', {
right = 'card_suit_hearts'
}, {
'List Item 1',
'List Item 2',
'List Item 3'
}, 1):OnChange(function(current, currentValue)
print('List Changed', current, currentValue)
end)

print('Removed slider `OnChange` handler')
menu:AddSlider('Slider', 'Slider Description', {
right = 'card_suit_hearts'
}, 100, 0, 10, 50):OnChange(function(current)
print('Slider Changed', current)
end)

RegisterCommand('example', function()
Expand Down
2 changes: 1 addition & 1 deletion client/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ end);
exports('SetNuiFocus', SetNuiFocus);
exports('SetNuiFocusKeepInput', SetNuiFocusKeepInput);


for _, callback in next, {
'OnSelect',
'OnChange',
Expand Down Expand Up @@ -57,6 +56,7 @@ end

AddEventHandler('onResourceStop', function(resource)
if LAST_RESOURCE == resource then
SendNUIMessage({ action = 'SetMenu' });
SendNUIMessage({ action = 'SetItems' });
end
end);
38 changes: 18 additions & 20 deletions import.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,7 @@ local function resetNUI()

SendNUIMessage({ action = 'SetMenu' });

SendNUIMessage({
action = 'SetItems',
data = {}
});
SendNUIMessage({ action = 'SetItems' });
end

---@param menu Menu
Expand All @@ -74,16 +71,13 @@ function Menu:Open(menu)
return;
end

if self.current ~= nil then
local oldMenu = self:GetById(self.current);

if oldMenu then
oldMenu:Close();

self.opened[#self.opened + 1] = self.current;
for index, menuId in next, self.opened do
if menuId == menu.id then
table.remove(self.opened, index);
end
end

self.opened[#self.opened + 1] = menu.id;
self.current = menu.id;

SetNuiFocus(true, false);
Expand All @@ -101,18 +95,22 @@ function Menu:Open(menu)
end

function Menu:Close()
if not self.current then
return;
end

if #self.opened == 0 then
self.current = nil;
return self:CloseAll();
end

return resetNUI();
for index, menuId in next, self.opened do
if menuId == self.current then
table.remove(self.opened, index);
end
end

local lastOpened = self:GetById(self.opened[#self.opened]);

if not lastOpened then
return self:CloseAll();
end

if lastOpened then
self:Open(lastOpened);
end
Expand Down Expand Up @@ -337,9 +335,11 @@ function Menu:Create(menuTitle, menuSubtitle, menuWidth, maxVisibleItems, banner
id = self.id,
type = self.type,
label = self.label,
rightLabel = self.rightLabel,
description = self.description,
badges = self.badges,
disabled = self.disabled,
visible = self.visible,
values = self.values,
checked = self.checked,
current = self.current,
Expand Down Expand Up @@ -367,9 +367,7 @@ function Menu:Create(menuTitle, menuSubtitle, menuWidth, maxVisibleItems, banner
end

function menu:AddSubmenu(submenu, label, rightLabel, description, badges, disabled)
local button = self:AddButton(label, rightLabel, description, badges or {
right = 'arrow_right'
}, disabled);
local button = self:AddButton(label, rightLabel, description, badges, disabled);

button:OnClick(function()
local subMenu = Menu:GetById(type(submenu) == 'string' and submenu or submenu.id);
Expand Down
2 changes: 2 additions & 0 deletions meta.lua
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@
---@field id string
---@field type MenuType
---@field label string
---@field rightLabel? string
---@field description? string
---@field badges? { left?: BadgeName; right?: BadgeName }
---@field disabled? boolean
---@field visible boolean
---@field values? string[]
---@field checked? boolean
---@field current? number
Expand Down
62 changes: 33 additions & 29 deletions web/src/components/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import Item, { type ItemProps } from '@/components/item';
import SubTitle from '@/components/sub-title';
import Description from '@/components/description';

import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useKeyDown } from '@/lib/keys';
import { useNuiEvent } from '@/lib/hooks';
import { cn, debugData, fetchNui, findLastIndex } from '@/lib';
import { cn, debugData, fetchNui } from '@/lib';

interface MenuProps {
id: string;
Expand Down Expand Up @@ -58,39 +58,39 @@ export default function Menu() {
setItems([...items]);
});

function ArrowUp() {
let index = Math.max(-1, selected - 1);

if (items[index]?.type === 'separator' || items[index]?.disabled) index--;
const findNextValidIndex = useCallback(
(direction: 'up' | 'down') => {
const step = direction === 'up' ? -1 : 1;
let index = selected;

do {
index += step;
if (index < 0) index = items.length - 1;
if (index >= items.length) index = 0;

if (
items[index].type !== 'separator' &&
!items[index].disabled &&
items[index].visible !== false
) {
return index;
}
} while (index !== selected);

if (index < 0)
index = findLastIndex(
items,
(c) => !c.disabled && c.visible !== false && c.type !== 'separator'
);
return -1;
},
[items, selected]
);

setSelected(index);
function ArrowUp() {
const newIndex = findNextValidIndex('up');
setSelected(newIndex);
}
useKeyDown('ArrowUp', ArrowUp);

function ArrowDown() {
let index = Math.min(items.length, selected + 1);

if (
items[index]?.type === 'separator' ||
items[index]?.disabled ||
items[index]?.visible === false
)
index += 1;

if (index === items.length)
index = items.findIndex(
(c) => !c.disabled && c.visible !== false && c.type !== 'separator'
);

if (index < 0) return;

setSelected(index);
const newIndex = findNextValidIndex('down');
setSelected(newIndex);
}
useKeyDown('ArrowDown', ArrowDown);

Expand Down Expand Up @@ -201,6 +201,10 @@ export default function Menu() {
});
}, [selected, menu, items]);

useEffect(() => {
setSelected(0);
}, [menu]);

useEffect(() => {
debugData([
{
Expand Down
12 changes: 0 additions & 12 deletions web/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,3 @@ export function debugData<P>(events: DebugEvent<P>[], timer = 1000) {
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

export function findLastIndex<T>(
array: T[],
predicate: (value: T, index: number, array: T[]) => boolean
): number {
for (let i = array.length - 1; i >= 0; i--) {
if (predicate(array[i], i, array)) {
return i;
}
}
return -1;
}

0 comments on commit 84310ca

Please sign in to comment.