mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-18 23:46:03 +01:00
feat(interface/menu): checkbox
feat(package/interface/menu): checkbox feat(web/menu/list): checkbox feat(web/menu/list): checkbox option for ListItem feat(web/menu/list): custom checkbox
This commit is contained in:
@@ -11,6 +11,7 @@ import React from 'react';
|
||||
|
||||
export interface MenuItem {
|
||||
label: string;
|
||||
checked?: boolean;
|
||||
values?: Array<string | { label: string; description: string }>;
|
||||
description?: string;
|
||||
icon?: IconProp;
|
||||
@@ -35,6 +36,7 @@ const ListMenu: React.FC = () => {
|
||||
const [selected, setSelected] = useState(0);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [indexStates, setIndexStates] = useState<Record<number, number>>({});
|
||||
const [checkedStates, setCheckedStates] = useState<Record<number, boolean>>({});
|
||||
const listRefs = useRef<Array<HTMLDivElement | null>>([]);
|
||||
const firstRenderRef = useRef(false);
|
||||
|
||||
@@ -59,33 +61,63 @@ const ListMenu: React.FC = () => {
|
||||
});
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
if (!Array.isArray(menu.items[selected].values)) return;
|
||||
setIndexStates({
|
||||
...indexStates,
|
||||
[selected]:
|
||||
indexStates[selected] + 1 <= menu.items[selected].values?.length! - 1
|
||||
? indexStates[selected] + 1
|
||||
: (indexStates[selected] = 0),
|
||||
});
|
||||
if (Array.isArray(menu.items[selected].values)) {
|
||||
setIndexStates({
|
||||
...indexStates,
|
||||
[selected]:
|
||||
indexStates[selected] + 1 <= menu.items[selected].values?.length! - 1
|
||||
? indexStates[selected] + 1
|
||||
: 0
|
||||
});
|
||||
} else if (menu.items[selected].checked !== undefined) {
|
||||
setCheckedStates({
|
||||
...checkedStates,
|
||||
[selected]: !checkedStates[selected]
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
if (!Array.isArray(menu.items[selected].values)) return;
|
||||
setIndexStates({
|
||||
...indexStates,
|
||||
[selected]:
|
||||
indexStates[selected] - 1 >= 0
|
||||
? indexStates[selected] - 1
|
||||
: (indexStates[selected] = menu.items[selected].values?.length! - 1),
|
||||
});
|
||||
if (Array.isArray(menu.items[selected].values)) {
|
||||
setIndexStates({
|
||||
...indexStates,
|
||||
[selected]:
|
||||
indexStates[selected] - 1 >= 0
|
||||
? indexStates[selected] - 1
|
||||
: menu.items[selected].values?.length! - 1
|
||||
});
|
||||
} else if (menu.items[selected].checked !== undefined) {
|
||||
setCheckedStates({
|
||||
...checkedStates,
|
||||
[selected]: !checkedStates[selected]
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
if (!menu.items[selected]) return;
|
||||
if (menu.items[selected].checked !== undefined) {
|
||||
setCheckedStates({
|
||||
...checkedStates,
|
||||
[selected]: !checkedStates[selected]
|
||||
});
|
||||
}
|
||||
fetchNui('confirmSelected', [selected, indexStates[selected]]).catch();
|
||||
if (menu.items[selected].close === undefined || menu.items[selected].close) setVisible(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (firstRenderRef.current) {
|
||||
firstRenderRef.current = false;
|
||||
return;
|
||||
}
|
||||
if (menu.items[selected]?.checked === undefined) return;
|
||||
const timer = setTimeout(() => {
|
||||
fetchNui('changeChecked', [selected, indexStates[selected], checkedStates[selected]]).catch();
|
||||
}, 100);
|
||||
return () => clearTimeout(timer);
|
||||
}, [checkedStates])
|
||||
|
||||
useEffect(() => {
|
||||
if (firstRenderRef.current) {
|
||||
firstRenderRef.current = false;
|
||||
@@ -93,7 +125,7 @@ const ListMenu: React.FC = () => {
|
||||
}
|
||||
if (!menu.items[selected]?.values) return;
|
||||
const timer = setTimeout(() => {
|
||||
fetchNui('changeIndex', [selected, indexStates[selected]]).catch();
|
||||
fetchNui('changeIndex', [selected, indexStates[selected], checkedStates[selected]]).catch();
|
||||
}, 100);
|
||||
return () => clearTimeout(timer);
|
||||
}, [indexStates]);
|
||||
@@ -107,7 +139,7 @@ const ListMenu: React.FC = () => {
|
||||
listRefs.current[selected]?.focus({ preventScroll: true });
|
||||
// debounces the callback to avoid spam
|
||||
const timer = setTimeout(() => {
|
||||
fetchNui('changeSelected', [selected, indexStates[selected]]).catch();
|
||||
fetchNui('changeSelected', [selected, indexStates[selected], checkedStates[selected]]).catch();
|
||||
}, 100);
|
||||
return () => clearTimeout(timer);
|
||||
}, [selected, menu]);
|
||||
@@ -135,11 +167,14 @@ const ListMenu: React.FC = () => {
|
||||
listRefs.current = [];
|
||||
setMenu(data);
|
||||
setVisible(true);
|
||||
let arrayIndexes: { [key: number]: number } = {};
|
||||
const arrayIndexes: { [key: number]: number } = {};
|
||||
const checkedIndexes: { [key: number]: boolean } = {};
|
||||
for (let i = 0; i < data.items.length; i++) {
|
||||
if (Array.isArray(data.items[i].values)) arrayIndexes[i] = (data.items[i].defaultIndex || 1) - 1;
|
||||
else if (data.items[i].checked !== undefined) checkedIndexes[i] = data.items[i].checked || false;
|
||||
}
|
||||
setIndexStates(arrayIndexes);
|
||||
setCheckedStates(checkedIndexes);
|
||||
listRefs.current[data.startItemIndex]?.focus();
|
||||
});
|
||||
|
||||
@@ -189,7 +224,7 @@ const ListMenu: React.FC = () => {
|
||||
{menu.items.map((item, index) => (
|
||||
<React.Fragment key={`menu-item-${index}`}>
|
||||
{item.label && (
|
||||
<ListItem index={index} item={item} scrollIndex={indexStates[index]} ref={listRefs} />
|
||||
<ListItem index={index} item={item} scrollIndex={indexStates[index]} checked={checkedStates[index]} ref={listRefs} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user