feat(web/menu): menu list focus trapping

This commit is contained in:
Luke
2022-07-28 15:41:20 +02:00
committed by Luke
parent 98b9e224ef
commit 196d400c56
6 changed files with 68 additions and 33 deletions

14
node_modules/.pnpm-debug.log generated vendored Normal file
View 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"
}
}
}

View File

@@ -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
View File

@@ -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

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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>
</>
);