diff --git a/resource/interface/client/menu.lua b/resource/interface/client/menu.lua new file mode 100644 index 0000000..14a8332 --- /dev/null +++ b/resource/interface/client/menu.lua @@ -0,0 +1,56 @@ +local registeredMenus = {} +local openMenu = nil + +function lib.registerMenu(data) + if not registeredMenus[data.id] then registeredMenus[data.id] = data end +end + +function lib.showMenu(id) + if not registeredMenus[id] then return error('No menu of such id found.') end + local data = registeredMenus[id] + openMenu = id + SetNuiFocus(true, true) + SendNUIMessage({ + action = 'setMenu', + data = { + position = data.position, + title = data.title, + items = data.options + } + }) +end + +function lib.getOpenMenu() return openMenu end + +RegisterNUICallback('confirmSelected', function(data, cb) + cb(1) + -- print(data) +end) + +RegisterNUICallback('changeSelected', function(data, cb) + cb(1) + if registeredMenus[openMenu].onChange then + local selected = data + if type(selected) == 'number' then + registeredMenus[openMenu].onChange(selected) + else + registeredMenus[openMenu].onChange(selected[1], selected[2]) + end + end +end) + +RegisterCommand('testMenu', function() + lib.registerMenu({ + id = 'epic_menu', + title = 'Nice menu', + position = 'top-right', + onChange = function(selected, scrollIndex) + print(selected, scrollIndex) + end, + options = { + {label = 'Nice option'}, + {label = 'Nice header', values = {'Option1', 'option2', 'option3'}} + } + }) + lib.showMenu('epic_menu') +end) \ No newline at end of file diff --git a/web/src/features/menu/list/ListItem.tsx b/web/src/features/menu/list/ListItem.tsx index a566b12..07d4dc9 100644 --- a/web/src/features/menu/list/ListItem.tsx +++ b/web/src/features/menu/list/ListItem.tsx @@ -35,19 +35,19 @@ const ListItem = forwardRef, Props>(({ item, index, style={{ marginRight: 20, marginLeft: 5 }} /> )} - {Array.isArray(item.value) ? ( + {Array.isArray(item.values) ? ( {item.label} - {item.value[scrollIndex]} + {item.values[scrollIndex]} - {scrollIndex + 1}/{item.value.length} + {scrollIndex + 1}/{item.values.length} diff --git a/web/src/features/menu/list/index.tsx b/web/src/features/menu/list/index.tsx index 775d7c0..7816a74 100644 --- a/web/src/features/menu/list/index.tsx +++ b/web/src/features/menu/list/index.tsx @@ -10,7 +10,7 @@ import { fetchNui } from "../../../utils/fetchNui"; export interface MenuItem { label: string; - value: string | string[]; + values?: string[]; icon?: IconProp; } @@ -27,15 +27,15 @@ debugData([ // position: "bottom-left", title: "Vehicle garage", items: [ - { label: "Option 1", value: "option1", icon: "heart" }, - { label: "Option 2", value: "option2", icon: "basket-shopping" }, - { label: "Vehicle class", value: ["Nice", "Super nice", "Extra nice"], icon: "tag" }, - { label: "Option 1", value: "option1" }, - { label: "Option 2", value: "option2" }, - { label: "Vehicle class", value: ["Nice", "Super nice", "Extra nice"] }, - { label: "Option 1", value: "option1" }, - { label: "Option 2", value: "option2" }, - { label: "Vehicle class", value: ["Nice", "Super nice", "Extra nice"] }, + { label: "Option 1", icon: "heart" }, + { label: "Option 2", icon: "basket-shopping" }, + { label: "Vehicle class", values: ["Nice", "Super nice", "Extra nice"], icon: "tag" }, + { label: "Option 1" }, + { label: "Option 2" }, + { label: "Vehicle class", values: ["Nice", "Super nice", "Extra nice"] }, + { label: "Option 1" }, + { label: "Option 2" }, + { label: "Vehicle class", values: ["Nice", "Super nice", "Extra nice"] }, ], }, }, @@ -67,28 +67,27 @@ const ListMenu: React.FC = () => { }); break; case "ArrowRight": - if (!Array.isArray(menu.items[selected].value)) return; + if (!Array.isArray(menu.items[selected].values)) return; setIndexStates({ ...indexStates, [selected]: - indexStates[selected] + 1 <= menu.items[selected].value.length - 1 + indexStates[selected] + 1 <= menu.items[selected].values?.length! - 1 ? indexStates[selected] + 1 : indexStates[selected], }); break; case "ArrowLeft": - if (!Array.isArray(menu.items[selected].value)) return; + if (!Array.isArray(menu.items[selected].values)) return; setIndexStates({ ...indexStates, [selected]: indexStates[selected] - 1 >= 0 ? indexStates[selected] - 1 : indexStates[selected], }); break; case "Enter": + if (!menu.items[selected].values) return; fetchNui( "confirmSelected", - Array.isArray(menu.items[selected].value) - ? menu.items[selected].value[indexStates[selected]] - : menu.items[selected].value + Array.isArray(menu.items[selected].values) ? [selected, indexStates[selected]] : selected ); setVisible(false); break; @@ -96,15 +95,13 @@ const ListMenu: React.FC = () => { }; useEffect(() => { - if (!menu.items[selected]?.value) return; + if (!menu.items[selected]) return; listRefs.current[selected]?.focus(); // debounces the callback to avoid spam const timer = setTimeout(() => { fetchNui( "changeSelected", - Array.isArray(menu.items[selected].value) - ? menu.items[selected].value[indexStates[selected]] - : menu.items[selected].value + Array.isArray(menu.items[selected].values) ? [selected, indexStates[selected]] : selected ); }, 500); return () => clearTimeout(timer); @@ -116,7 +113,7 @@ const ListMenu: React.FC = () => { setVisible(true); let arrayIndexes: { [key: number]: number } = {}; for (let i = 0; i < data.items.length; i++) { - if (Array.isArray(data.items[i].value)) arrayIndexes[i] = 0; + if (Array.isArray(data.items[i].values)) arrayIndexes[i] = 0; } setIndexStates(arrayIndexes); listRefs.current[0]?.focus();