mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-16 22:46:03 +01:00
feat(web/menu): menu list focus trapping
This commit is contained in:
14
node_modules/.pnpm-debug.log
generated
vendored
Normal file
14
node_modules/.pnpm-debug.log
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"0 debug pnpm:scope": {
|
||||
"selected": 1
|
||||
},
|
||||
"1 error pnpm": {
|
||||
"code": "ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND",
|
||||
"err": {
|
||||
"name": "pnpm",
|
||||
"message": "No package.json (or package.yaml, or package.json5) was found in \"C:\\GTA\\esx-server\\txData\\CFXDefault_6E51DF.base\\resources\\ox_lib\".",
|
||||
"code": "ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND",
|
||||
"stack": "pnpm: No package.json (or package.yaml, or package.json5) was found in \"C:\\GTA\\esx-server\\txData\\CFXDefault_6E51DF.base\\resources\\ox_lib\".\n at readProjectManifest (C:\\Users\\Luka-PC\\AppData\\Local\\pnpm\\global\\5\\.pnpm\\pnpm@7.5.0\\node_modules\\pnpm\\dist\\pnpm.cjs:20853:13)\n at async Object.readProjectManifestOnly (C:\\Users\\Luka-PC\\AppData\\Local\\pnpm\\global\\5\\.pnpm\\pnpm@7.5.0\\node_modules\\pnpm\\dist\\pnpm.cjs:20857:28)\n at async readProjectManifestOnly (C:\\Users\\Luka-PC\\AppData\\Local\\pnpm\\global\\5\\.pnpm\\pnpm@7.5.0\\node_modules\\pnpm\\dist\\pnpm.cjs:44022:24)\n at async Object.handler (C:\\Users\\Luka-PC\\AppData\\Local\\pnpm\\global\\5\\.pnpm\\pnpm@7.5.0\\node_modules\\pnpm\\dist\\pnpm.cjs:184457:24)\n at async C:\\Users\\Luka-PC\\AppData\\Local\\pnpm\\global\\5\\.pnpm\\pnpm@7.5.0\\node_modules\\pnpm\\dist\\pnpm.cjs:192593:21\n at async run (C:\\Users\\Luka-PC\\AppData\\Local\\pnpm\\global\\5\\.pnpm\\pnpm@7.5.0\\node_modules\\pnpm\\dist\\pnpm.cjs:192567:34)\n at async runPnpm (C:\\Users\\Luka-PC\\AppData\\Local\\pnpm\\global\\5\\.pnpm\\pnpm@7.5.0\\node_modules\\pnpm\\dist\\pnpm.cjs:192785:5)\n at async C:\\Users\\Luka-PC\\AppData\\Local\\pnpm\\global\\5\\.pnpm\\pnpm@7.5.0\\node_modules\\pnpm\\dist\\pnpm.cjs:192777:7"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
"@types/react": "^17.0.43",
|
||||
"@types/react-dom": "^17.0.14",
|
||||
"@vitejs/plugin-react": "^1.3.2",
|
||||
"focus-trap-react": "^9.0.2",
|
||||
"framer-motion": "^6.2.8",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
@@ -60,6 +61,7 @@
|
||||
"autoprefixer": "^10.0.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"postcss": "^8.1.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"rimraf": "^3.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
34
web/pnpm-lock.yaml
generated
34
web/pnpm-lock.yaml
generated
@@ -23,8 +23,10 @@ specifiers:
|
||||
'@vitejs/plugin-react': ^1.3.2
|
||||
autoprefixer: ^10.0.2
|
||||
cross-env: ^7.0.3
|
||||
focus-trap-react: ^9.0.2
|
||||
framer-motion: ^6.2.8
|
||||
postcss: ^8.1.0
|
||||
prop-types: ^15.8.1
|
||||
react: ^17.0.2
|
||||
react-dom: ^17.0.2
|
||||
react-markdown: ^8.0.1
|
||||
@@ -50,6 +52,7 @@ dependencies:
|
||||
'@types/react': 17.0.43
|
||||
'@types/react-dom': 17.0.14
|
||||
'@vitejs/plugin-react': 1.3.2
|
||||
focus-trap-react: 9.0.2_oxfzelaz5ynxsop2v2nu2h2m64
|
||||
framer-motion: 6.2.8_sfoxds7t5ydpegc3knd667wn6m
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2_react@17.0.2
|
||||
@@ -66,6 +69,7 @@ devDependencies:
|
||||
autoprefixer: 10.4.4_postcss@8.4.12
|
||||
cross-env: 7.0.3
|
||||
postcss: 8.4.12
|
||||
prop-types: 15.8.1
|
||||
rimraf: 3.0.2
|
||||
|
||||
packages:
|
||||
@@ -2333,6 +2337,26 @@ packages:
|
||||
tslib: 2.3.1
|
||||
dev: false
|
||||
|
||||
/focus-trap-react/9.0.2_oxfzelaz5ynxsop2v2nu2h2m64:
|
||||
resolution: {integrity: sha512-ZwhO5by6KG5r3dy48Lk00A1/0zNYw1Z3RZTN6O6kgAPsWFcwTFszOcQ1dLSfM8pIxpS/ttc7wTttJowjVT3jpg==}
|
||||
peerDependencies:
|
||||
prop-types: ^15.8.1
|
||||
react: '>=16.3.0'
|
||||
react-dom: '>=16.3.0'
|
||||
dependencies:
|
||||
focus-trap: 6.9.4
|
||||
prop-types: 15.8.1
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2_react@17.0.2
|
||||
tabbable: 5.3.3
|
||||
dev: false
|
||||
|
||||
/focus-trap/6.9.4:
|
||||
resolution: {integrity: sha512-v2NTsZe2FF59Y+sDykKY+XjqZ0cPfhq/hikWVL88BqLivnNiEffAsac6rP6H45ff9wG9LL5ToiDqrLEP9GX9mw==}
|
||||
dependencies:
|
||||
tabbable: 5.3.3
|
||||
dev: false
|
||||
|
||||
/fraction.js/4.2.0:
|
||||
resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
|
||||
dev: true
|
||||
@@ -2546,7 +2570,6 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
js-tokens: 4.0.0
|
||||
dev: false
|
||||
|
||||
/lz-string/1.4.4:
|
||||
resolution: {integrity: sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=}
|
||||
@@ -2818,9 +2841,8 @@ packages:
|
||||
dev: true
|
||||
|
||||
/object-assign/4.1.1:
|
||||
resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=}
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/once/1.4.0:
|
||||
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
|
||||
@@ -2927,7 +2949,6 @@ packages:
|
||||
loose-envify: 1.4.0
|
||||
object-assign: 4.1.1
|
||||
react-is: 16.13.1
|
||||
dev: false
|
||||
|
||||
/property-information/6.1.1:
|
||||
resolution: {integrity: sha512-hrzC564QIl0r0vy4l6MvRLhafmUowhO/O3KgVSoXIbbA2Sz4j8HGpJc6T2cubRVwMwpdiG/vKGfhT4IixmKN9w==}
|
||||
@@ -2975,7 +2996,6 @@ packages:
|
||||
|
||||
/react-is/16.13.1:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
dev: false
|
||||
|
||||
/react-is/17.0.2:
|
||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||
@@ -3230,6 +3250,10 @@ packages:
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: false
|
||||
|
||||
/tabbable/5.3.3:
|
||||
resolution: {integrity: sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==}
|
||||
dev: false
|
||||
|
||||
/tiny-invariant/1.2.0:
|
||||
resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==}
|
||||
dev: false
|
||||
|
||||
@@ -22,17 +22,17 @@ interface DialogProps {
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
debugData<DialogProps>([
|
||||
{
|
||||
action: "sendAlert",
|
||||
data: {
|
||||
header: "Hello there",
|
||||
content: "General kenobi \n Markdown works",
|
||||
centered: true,
|
||||
cancel: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
// debugData<DialogProps>([
|
||||
// {
|
||||
// action: "sendAlert",
|
||||
// data: {
|
||||
// header: "Hello there",
|
||||
// content: "General kenobi \n Markdown works",
|
||||
// centered: true,
|
||||
// cancel: true,
|
||||
// },
|
||||
// },
|
||||
// ]);
|
||||
|
||||
const AlertDialog: React.FC = () => {
|
||||
const { locale } = useLocales();
|
||||
@@ -77,10 +77,7 @@ const AlertDialog: React.FC = () => {
|
||||
{locale.ui.cancel}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
colorScheme={dialogData.cancel ? "blue" : undefined}
|
||||
onClick={() => closeAlert("confirm")}
|
||||
>
|
||||
<Button colorScheme={dialogData.cancel ? "blue" : undefined} onClick={() => closeAlert("confirm")}>
|
||||
{locale.ui.confirm}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@@ -35,7 +35,7 @@ const ListItem = forwardRef<Array<HTMLDivElement | null>, Props>(({ item, index
|
||||
<Text color="#909296" textTransform="uppercase" fontSize={12} verticalAlign="middle">
|
||||
{item.label}
|
||||
</Text>
|
||||
<Text>Nice cool</Text>
|
||||
<Text>hello</Text>
|
||||
</Stack>
|
||||
<Spacer />
|
||||
<FontAwesomeIcon icon="arrows-left-right" fontSize={20} color="#909296" />
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useNuiEvent } from "../../../hooks/useNuiEvent";
|
||||
import { debugData } from "../../../utils/debugData";
|
||||
import ListItem from "./ListItem";
|
||||
import Header from "./Header";
|
||||
import FocusTrap from "focus-trap-react";
|
||||
|
||||
interface MenuSettings {
|
||||
position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
||||
@@ -42,7 +43,7 @@ const ListMenu: React.FC = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const listRefs = useRef<Array<HTMLDivElement | null>>([]);
|
||||
|
||||
const moveMenu = (e: KeyboardEvent) => {
|
||||
const moveMenu = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
switch (e.code) {
|
||||
case "ArrowDown":
|
||||
setSelected((selected) => {
|
||||
@@ -63,12 +64,6 @@ const ListMenu: React.FC = () => {
|
||||
listRefs.current[selected]?.focus();
|
||||
}, [selected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!visible) return document.removeEventListener("keyup", moveMenu);
|
||||
|
||||
document.addEventListener("keyup", moveMenu);
|
||||
}, [visible]);
|
||||
|
||||
useNuiEvent("setMenu", (data: MenuSettings) => {
|
||||
if (!data.position) data.position = "top-left";
|
||||
setMenu(data);
|
||||
@@ -86,6 +81,7 @@ const ListMenu: React.FC = () => {
|
||||
bg="#141517"
|
||||
position="absolute"
|
||||
fontFamily="Nunito"
|
||||
onKeyDown={(e) => moveMenu(e)}
|
||||
mt={menu.position === "top-left" || menu.position === "top-right" ? 5 : 0}
|
||||
ml={menu.position === "top-left" || menu.position === "bottom-left" ? 5 : 0}
|
||||
mr={menu.position === "top-right" || menu.position === "bottom-right" ? 5 : 0}
|
||||
@@ -95,11 +91,13 @@ const ListMenu: React.FC = () => {
|
||||
bottom={menu.position === "bottom-left" || menu.position === "bottom-right" ? 1 : undefined}
|
||||
>
|
||||
<Header title={menu.title} />
|
||||
<Stack direction="column" p={2} overflowY="scroll">
|
||||
{menu.items.map((item, index) => (
|
||||
<ListItem index={index} item={item} ref={listRefs} key={`menu-item-${index}`} />
|
||||
))}
|
||||
</Stack>
|
||||
<FocusTrap active={visible}>
|
||||
<Stack direction="column" p={2} overflowY="scroll">
|
||||
{menu.items.map((item, index) => (
|
||||
<ListItem index={index} item={item} ref={listRefs} key={`menu-item-${index}`} />
|
||||
))}
|
||||
</Stack>
|
||||
</FocusTrap>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user