diff --git a/picture-gallery-client/.eslintrc.json b/picture-gallery-client/.eslintrc.json new file mode 100644 index 0000000..ef32c74 --- /dev/null +++ b/picture-gallery-client/.eslintrc.json @@ -0,0 +1,51 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "plugin:react/recommended", + "airbnb", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "react", + "@typescript-eslint" + ], + "ignorePatterns": [ + "**/*.css" + ], + "rules": { + "import/extensions": [ + "error", + "ignorePackages", + { + "ts": "never", + "tsx": "never" + } + ], + "react/jsx-filename-extension": [ + 1, + { + "extensions": [ + ".jsx", + ".tsx" + ] + } + ] + }, + "settings": { + "import/resolver": { + "typescript": { + } + } + } +} diff --git a/picture-gallery-client/.prettierignore b/picture-gallery-client/.prettierignore new file mode 100644 index 0000000..b7dab5e --- /dev/null +++ b/picture-gallery-client/.prettierignore @@ -0,0 +1,2 @@ +node_modules +build \ No newline at end of file diff --git a/picture-gallery-client/package-lock.json b/picture-gallery-client/package-lock.json index cae6e51..b0fbcb0 100644 --- a/picture-gallery-client/package-lock.json +++ b/picture-gallery-client/package-lock.json @@ -27,6 +27,20 @@ "react-scripts": "5.0.0", "typescript": "^4.6.3", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.18.0", + "@typescript-eslint/parser": "^5.18.0", + "eslint": "^8.13.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^8.5.0", + "eslint-import-resolver-typescript": "^2.7.1", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.29.4", + "eslint-plugin-react-hooks": "^4.4.0", + "prettier": "^2.6.2" } }, "node_modules/@ampproject/remapping": { @@ -7036,9 +7050,9 @@ } }, "node_modules/eslint": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", - "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", + "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", "dependencies": { "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", @@ -7086,6 +7100,67 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + }, + "engines": { + "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-config-react-app": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.0.tgz", @@ -7130,6 +7205,26 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, "node_modules/eslint-module-utils": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", @@ -7326,6 +7421,27 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, + "node_modules/eslint-plugin-prettier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", + "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.29.4", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", @@ -7809,6 +7925,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -13375,6 +13497,33 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -21559,9 +21708,9 @@ } }, "eslint": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.12.0.tgz", - "integrity": "sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz", + "integrity": "sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ==", "requires": { "@eslint/eslintrc": "^1.2.1", "@humanwhocodes/config-array": "^0.9.2", @@ -21676,6 +21825,44 @@ } } }, + "eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + } + }, + "eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, "eslint-config-react-app": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.0.tgz", @@ -21716,6 +21903,19 @@ } } }, + "eslint-import-resolver-typescript": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz", + "integrity": "sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "glob": "^7.2.0", + "is-glob": "^4.0.3", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + } + }, "eslint-module-utils": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", @@ -21857,6 +22057,15 @@ "minimatch": "^3.0.4" } }, + "eslint-plugin-prettier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", + "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { "version": "7.29.4", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", @@ -22117,6 +22326,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -25977,6 +26192,21 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, + "prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/picture-gallery-client/package.json b/picture-gallery-client/package.json index a8f4601..8b9b17c 100644 --- a/picture-gallery-client/package.json +++ b/picture-gallery-client/package.json @@ -28,7 +28,10 @@ "start-client": "react-scripts start", "build-client": "react-scripts build", "test-client": "react-scripts test", - "eject-client": "react-scripts eject" + "eject-client": "react-scripts eject", + "lint": "eslint src/*", + "lint:fix": "eslint --fix src/*", + "format": "prettier --write \"**/*.+(ts|tsx)\"" }, "eslintConfig": { "extends": [ @@ -47,5 +50,19 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.18.0", + "@typescript-eslint/parser": "^5.18.0", + "eslint": "^8.13.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^8.5.0", + "eslint-import-resolver-typescript": "^2.7.1", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.29.4", + "eslint-plugin-react-hooks": "^4.4.0", + "prettier": "^2.6.2" } } diff --git a/picture-gallery-client/src/ImageGallery/ImageGallery.tsx b/picture-gallery-client/src/ImageGallery/ImageGallery.tsx index 3228a7e..e6a4bd6 100644 --- a/picture-gallery-client/src/ImageGallery/ImageGallery.tsx +++ b/picture-gallery-client/src/ImageGallery/ImageGallery.tsx @@ -1,17 +1,22 @@ -import PhotoAlbum, {Photo} from "react-photo-album"; +import PhotoAlbum, { Photo } from "react-photo-album"; -function ImageGallery({images}: { images: Photo[] }) { - // For all kind of settings see: - // https://react-photo-album.com/examples/playground - // https://codesandbox.io/s/github/igordanchenko/react-photo-album/tree/main/examples/playground +function ImageGallery({ images }: { images: Photo[] }) { + // For all kind of settings see: + // https://react-photo-album.com/examples/playground + // https://codesandbox.io/s/github/igordanchenko/react-photo-album/tree/main/examples/playground - return ( - <> - {images.length === 0 - ?

No images available. You may want to add images in your root directory.

- : } - - ); + return ( + <> + {images.length === 0 ? ( +

+ No images available. You may want to add images in your root + directory. +

+ ) : ( + + )} + + ); } export default ImageGallery; diff --git a/picture-gallery-client/src/ImageGallery/ImageGalleryAppBar.tsx b/picture-gallery-client/src/ImageGallery/ImageGalleryAppBar.tsx index ff0337f..af36616 100644 --- a/picture-gallery-client/src/ImageGallery/ImageGalleryAppBar.tsx +++ b/picture-gallery-client/src/ImageGallery/ImageGalleryAppBar.tsx @@ -4,25 +4,33 @@ import MenuIcon from "@mui/icons-material/Menu"; import Typography from "@mui/material/Typography"; import AppBar from "../MuiLayout/AppBar"; -function ImageGalleryAppBar({open, drawerWidth, onDrawerOpenClick}: { open: boolean, drawerWidth: number, onDrawerOpenClick: () => void }) { - return ( - - - - - - - {process.env.TITLE ?? "Image Gallery"} - - - - ) +function ImageGalleryAppBar({ + open, + drawerWidth, + onDrawerOpenClick, +}: { + open: boolean; + drawerWidth: number; + onDrawerOpenClick: () => void; +}) { + return ( + + + + + + + {process.env.TITLE ?? "Image Gallery"} + + + + ); } -export default ImageGalleryAppBar; \ No newline at end of file +export default ImageGalleryAppBar; diff --git a/picture-gallery-client/src/ImageGallery/ImageGalleryDrawer.tsx b/picture-gallery-client/src/ImageGallery/ImageGalleryDrawer.tsx index 7777d38..7f1e488 100644 --- a/picture-gallery-client/src/ImageGallery/ImageGalleryDrawer.tsx +++ b/picture-gallery-client/src/ImageGallery/ImageGalleryDrawer.tsx @@ -3,100 +3,150 @@ import DrawerHeader from "../MuiLayout/DrawerHeader"; import IconButton from "@mui/material/IconButton"; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import ChevronRightIcon from "@mui/icons-material/ChevronRight"; -import FolderIcon from '@mui/icons-material/Folder'; -import FolderOpenIcon from '@mui/icons-material/FolderOpen'; +import FolderIcon from "@mui/icons-material/Folder"; +import FolderOpenIcon from "@mui/icons-material/FolderOpen"; import Divider from "@mui/material/Divider"; -import {useTheme} from "@mui/material/styles"; -import {TreeItem, TreeView} from '@mui/lab'; -import {Folders} from "./models"; -import {useLocation, useNavigate} from "react-router-dom"; -import {useState} from "react"; +import { useTheme } from "@mui/material/styles"; +import { TreeItem, TreeView } from "@mui/lab"; +import { Folders } from "./models"; +import { useLocation, useNavigate } from "react-router-dom"; +import { useState } from "react"; function getDefaultExpanded(pathname: string): string[] { - const pathParts = [] - let curPathName = pathname.substring(0, pathname.lastIndexOf("/")) - while (curPathName.length > 0) { - pathParts.push(curPathName) - curPathName = curPathName.substring(0, curPathName.lastIndexOf("/")) - } - return pathParts + const pathParts = []; + let curPathName = pathname.substring(0, pathname.lastIndexOf("/")); + while (curPathName.length > 0) { + pathParts.push(curPathName); + curPathName = curPathName.substring(0, curPathName.lastIndexOf("/")); + } + return pathParts; } -function generateTreeViewChildren(folders: Folders[], navigateAndToggleExpand: (path: string, navigationAllowed: boolean) => void) { - return (<> - {folders.map(f => { - const label = f.numberOfFiles === 0 ? f.name : `${f.name} - (${f.numberOfFiles})`; - const containsImages = f.numberOfFiles > 0; - if (f.children.length === 0) { - return ( navigateAndToggleExpand(f.fullPath, containsImages)}/>) - } - return ( - navigateAndToggleExpand(f.fullPath, containsImages)}> - {generateTreeViewChildren(f.children, navigateAndToggleExpand)} - - ) - })} - ) -} - -function GenerateTreeView({root}: { root: Folders }) { - const location = useLocation(); - const navigate = useNavigate(); - const [expanded, setExpanded] = useState(getDefaultExpanded(location.pathname)) - - const toggleExpanded = (path: string) => { - if (expanded.includes(path)) { - setExpanded(expanded.filter(p => p !== path)) - } else { - setExpanded([path, ...expanded]) +function generateTreeViewChildren( + folders: Folders[], + navigateAndToggleExpand: (path: string, navigationAllowed: boolean) => void +) { + return ( + <> + {folders.map((f) => { + const label = + f.numberOfFiles === 0 ? f.name : `${f.name} - (${f.numberOfFiles})`; + const containsImages = f.numberOfFiles > 0; + if (f.children.length === 0) { + return ( + + navigateAndToggleExpand(f.fullPath, containsImages) + } + /> + ); } - } - - const navigateAndToggleExpand = (path: string, navigationAllowed: boolean) => { - if (!navigationAllowed || location.pathname === path) { - toggleExpanded(path); - return; - } - toggleExpanded(path); - navigate(path) - } - - return ( - } - defaultExpandIcon={} - expanded={expanded} - > - navigate(root.fullPath)}/> - {root.children.length > 0 ? generateTreeViewChildren(root.children, navigateAndToggleExpand) : <>} - - ) + return ( + navigateAndToggleExpand(f.fullPath, containsImages)} + > + {generateTreeViewChildren(f.children, navigateAndToggleExpand)} + + ); + })} + + ); } -function ImageGalleryDrawer({open, drawerWidth, folder, onDrawerCloseClick}: { open: boolean, drawerWidth: number, folder: Folders, onDrawerCloseClick: () => void }) { - const theme = useTheme(); - return (( + getDefaultExpanded(location.pathname) + ); + + const toggleExpanded = (path: string) => { + if (expanded.includes(path)) { + setExpanded(expanded.filter((p) => p !== path)); + } else { + setExpanded([path, ...expanded]); + } + }; + + const navigateAndToggleExpand = ( + path: string, + navigationAllowed: boolean + ) => { + if (!navigationAllowed || location.pathname === path) { + toggleExpanded(path); + return; + } + toggleExpanded(path); + navigate(path); + }; + + return ( + } + defaultExpandIcon={} + expanded={expanded} > - - - {theme.direction === 'ltr' ? : } - - - - - ) + navigate(root.fullPath)} + /> + {root.children.length > 0 ? ( + generateTreeViewChildren(root.children, navigateAndToggleExpand) + ) : ( + <> + )} + + ); } -export default ImageGalleryDrawer; \ No newline at end of file +function ImageGalleryDrawer({ + open, + drawerWidth, + folder, + onDrawerCloseClick, +}: { + open: boolean; + drawerWidth: number; + folder: Folders; + onDrawerCloseClick: () => void; +}) { + const theme = useTheme(); + return ( + + + + {theme.direction === "ltr" ? ( + + ) : ( + + )} + + + + + + ); +} + +export default ImageGalleryDrawer; diff --git a/picture-gallery-client/src/ImageGallery/models.ts b/picture-gallery-client/src/ImageGallery/models.ts index a280f54..e2206cd 100644 --- a/picture-gallery-client/src/ImageGallery/models.ts +++ b/picture-gallery-client/src/ImageGallery/models.ts @@ -1,6 +1,6 @@ export interface Folders { - name: string - fullPath: string - numberOfFiles: number - children: Folders[] + name: string; + fullPath: string; + numberOfFiles: number; + children: Folders[]; } diff --git a/picture-gallery-client/src/ImageGalleryLayout.tsx b/picture-gallery-client/src/ImageGalleryLayout.tsx index d132420..e616e08 100644 --- a/picture-gallery-client/src/ImageGalleryLayout.tsx +++ b/picture-gallery-client/src/ImageGalleryLayout.tsx @@ -1,9 +1,9 @@ -import {useEffect, useState} from "react"; -import {Photo} from "react-photo-album"; -import Box from '@mui/material/Box'; -import CssBaseline from '@mui/material/CssBaseline'; -import {Folders} from "./ImageGallery/models"; -import {useLocation} from "react-router-dom"; +import React, { useEffect, useState } from "react"; +import { Photo } from "react-photo-album"; +import Box from "@mui/material/Box"; +import CssBaseline from "@mui/material/CssBaseline"; +import { useLocation } from "react-router-dom"; +import { Folders } from "./ImageGallery/models"; import ImageGalleryAppBar from "./ImageGallery/ImageGalleryAppBar"; import DrawerHeader from "./MuiLayout/DrawerHeader"; import ImageGalleryDrawer from "./ImageGallery/ImageGalleryDrawer"; @@ -13,43 +13,56 @@ import Main from "./MuiLayout/Main"; const drawerWidth = 240; function ImageGalleryLayout() { - const [open, setOpen] = useState(true); - const [folders, setFolders] = useState({name: "Home", fullPath: "/", numberOfFiles: 0, children: []}); - const location = useLocation(); + const [open, setOpen] = useState(true); + const [folders, setFolders] = useState({ + name: "Home", + fullPath: "/", + numberOfFiles: 0, + children: [], + }); + const location = useLocation(); - const handleDrawerOpen = () => { - setOpen(true); - }; + const handleDrawerOpen = () => { + setOpen(true); + }; - const handleDrawerClose = () => { - setOpen(false); - }; - const [images, setImages] = useState([]); + const handleDrawerClose = () => { + setOpen(false); + }; + const [images, setImages] = useState([]); - useEffect(() => { - fetch(`/api/${location.pathname}`) - .then((res) => res.json()) - .then((data) => setImages(data.images)); - }, [location.pathname]); + useEffect(() => { + fetch(`/api/${location.pathname}`) + .then((res) => res.json()) + .then((data) => setImages(data.images)); + }, [location.pathname]); - useEffect(() => { - fetch("/directories") - .then((res) => res.json()) - .then((data) => setFolders(data)); - }, []); + useEffect(() => { + fetch("/directories") + .then((res) => res.json()) + .then((data) => setFolders(data)); + }, []); - return ( - - - - -
- - -
- -
- ); + return ( + + + + +
+ + +
+
+ ); } export default ImageGalleryLayout; diff --git a/picture-gallery-client/src/MuiLayout/AppBar.tsx b/picture-gallery-client/src/MuiLayout/AppBar.tsx index d5df586..605cec8 100644 --- a/picture-gallery-client/src/MuiLayout/AppBar.tsx +++ b/picture-gallery-client/src/MuiLayout/AppBar.tsx @@ -1,24 +1,24 @@ -import {styled} from "@mui/material/styles"; +import { styled } from "@mui/material/styles"; import MuiAppBar from "@mui/material/AppBar"; const AppBar = styled(MuiAppBar, { - shouldForwardProp: (prop) => prop !== 'open', + shouldForwardProp: (prop) => prop !== "open", })<{ - open?: boolean; - drawerwidth: number; -}>(({theme, open, drawerwidth}) => ({ - transition: theme.transitions.create(['margin', 'width'], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - ...(open && { - width: `calc(100% - ${drawerwidth}px)`, - marginLeft: `${drawerwidth}px`, - transition: theme.transitions.create(['margin', 'width'], { - easing: theme.transitions.easing.easeOut, - duration: theme.transitions.duration.enteringScreen, - }), + open?: boolean; + drawerwidth: number; +}>(({ theme, open, drawerwidth }) => ({ + transition: theme.transitions.create(["margin", "width"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + ...(open && { + width: `calc(100% - ${drawerwidth}px)`, + marginLeft: `${drawerwidth}px`, + transition: theme.transitions.create(["margin", "width"], { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, }), + }), })); -export default AppBar; \ No newline at end of file +export default AppBar; diff --git a/picture-gallery-client/src/MuiLayout/DrawerHeader.tsx b/picture-gallery-client/src/MuiLayout/DrawerHeader.tsx index b3ffc0b..12b437b 100644 --- a/picture-gallery-client/src/MuiLayout/DrawerHeader.tsx +++ b/picture-gallery-client/src/MuiLayout/DrawerHeader.tsx @@ -1,12 +1,12 @@ -import {styled} from "@mui/material/styles"; +import { styled } from "@mui/material/styles"; -const DrawerHeader = styled('div')(({theme}) => ({ - display: 'flex', - alignItems: 'center', - padding: theme.spacing(0, 1), - // necessary for content to be below app bar - ...theme.mixins.toolbar, - justifyContent: 'flex-end', +const DrawerHeader = styled("div")(({ theme }) => ({ + display: "flex", + alignItems: "center", + padding: theme.spacing(0, 1), + // necessary for content to be below app bar + ...theme.mixins.toolbar, + justifyContent: "flex-end", })); -export default DrawerHeader; \ No newline at end of file +export default DrawerHeader; diff --git a/picture-gallery-client/src/MuiLayout/Main.tsx b/picture-gallery-client/src/MuiLayout/Main.tsx index 6f8da51..4a627cb 100644 --- a/picture-gallery-client/src/MuiLayout/Main.tsx +++ b/picture-gallery-client/src/MuiLayout/Main.tsx @@ -1,25 +1,23 @@ -import {styled} from "@mui/material/styles"; +import { styled } from "@mui/material/styles"; -const Main = styled('main', {shouldForwardProp: (prop) => prop !== 'open'})<{ - open?: boolean; - drawerwidth: number; -}>( - ({theme, open, drawerwidth}) => ({ - flexGrow: 1, - padding: theme.spacing(3), - transition: theme.transitions.create('margin', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - marginLeft: `-${drawerwidth}px`, - ...(open && { - transition: theme.transitions.create('margin', { - easing: theme.transitions.easing.easeOut, - duration: theme.transitions.duration.enteringScreen, - }), - marginLeft: 0, - }), +const Main = styled("main", { shouldForwardProp: (prop) => prop !== "open" })<{ + open?: boolean; + drawerwidth: number; +}>(({ theme, open, drawerwidth }) => ({ + flexGrow: 1, + padding: theme.spacing(3), + transition: theme.transitions.create("margin", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + marginLeft: `-${drawerwidth}px`, + ...(open && { + transition: theme.transitions.create("margin", { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, }), -); + marginLeft: 0, + }), +})); -export default Main; \ No newline at end of file +export default Main; diff --git a/picture-gallery-client/src/index.tsx b/picture-gallery-client/src/index.tsx index ef4a76c..2d645eb 100644 --- a/picture-gallery-client/src/index.tsx +++ b/picture-gallery-client/src/index.tsx @@ -1,16 +1,18 @@ -import React from 'react'; +import React from "react"; import ReactDOM from "react-dom/client"; -import './index.css'; -import reportWebVitals from './reportWebVitals'; +import "./index.css"; +import { BrowserRouter } from "react-router-dom"; +import reportWebVitals from "./reportWebVitals"; import ImageGalleryLayout from "./ImageGalleryLayout"; -import {BrowserRouter} from "react-router-dom"; -const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) +const root = ReactDOM.createRoot( + document.getElementById("root") as HTMLElement +); root.render( - - - -) + + + +); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) diff --git a/picture-gallery-client/src/react-app-env.d.ts b/picture-gallery-client/src/react-app-env.d.ts index ece12df..6431bc5 100644 --- a/picture-gallery-client/src/react-app-env.d.ts +++ b/picture-gallery-client/src/react-app-env.d.ts @@ -1 +1 @@ -/// +/// diff --git a/picture-gallery-client/src/reportWebVitals.ts b/picture-gallery-client/src/reportWebVitals.ts index 49a2a16..5fa3583 100644 --- a/picture-gallery-client/src/reportWebVitals.ts +++ b/picture-gallery-client/src/reportWebVitals.ts @@ -1,8 +1,8 @@ -import { ReportHandler } from 'web-vitals'; +import { ReportHandler } from "web-vitals"; const reportWebVitals = (onPerfEntry?: ReportHandler) => { if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { getCLS(onPerfEntry); getFID(onPerfEntry); getFCP(onPerfEntry); diff --git a/picture-gallery-client/src/setupTests.ts b/picture-gallery-client/src/setupTests.ts index 8f2609b..1dd407a 100644 --- a/picture-gallery-client/src/setupTests.ts +++ b/picture-gallery-client/src/setupTests.ts @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import "@testing-library/jest-dom";