From 13e36b4268050e50934cd95703e496a362148b5b Mon Sep 17 00:00:00 2001 From: Azat Ismagilov Date: Thu, 22 Aug 2024 00:34:43 +0100 Subject: [PATCH] Add prettier --- .eslintrc.json | 74 +--- .github/workflows/pages.yml | 9 +- .github/workflows/release.yml | 8 +- .prettierrc.json | 3 + README.md | 65 +-- data/consts.js | 50 +-- dataACPC/consts.js | 5 +- dataAE/consts.js | 5 +- dataAP/consts.js | 5 +- dataAW/consts.js | 5 +- dataEU/consts.js | 5 +- dataLAC/consts.js | 11 +- dataNAC/consts.js | 38 +- dataNERC/consts.js | 39 +- index.html | 27 +- package-lock.json | 175 ++++++++ package.json | 5 +- src/App.jsx | 58 ++- src/Util/DataLoader.js | 45 +- src/Util/PhotoInfoHelper.js | 196 +++++---- src/Util/PhotoLoader.js | 280 +++++++----- src/Util/PhotoService.js | 79 ++-- src/Util/UniqueList.js | 6 +- src/components/AppContext.jsx | 395 ++++++++--------- src/components/Body/Body.jsx | 233 +++++----- src/components/Body/Control.jsx | 36 +- src/components/Body/FaceDiv.jsx | 35 +- .../Body/ImageWithFaceSelection.jsx | 175 ++++---- src/components/Body/Lightbox.jsx | 54 ++- src/components/Body/MyModal.jsx | 49 ++- src/components/Body/PhotoGrid.jsx | 32 +- src/components/Body/PhotoGridByYear.jsx | 76 ++-- .../Body/PhotoInfo/PhotoInfoContext.jsx | 118 +++--- .../Body/PhotoInfo/PhotoInfoDetails.jsx | 400 +++++++++--------- .../Body/PhotoInfo/PhotoInfoPanel.jsx | 277 ++++++------ src/components/Body/Slideshow.jsx | 34 +- src/components/Header/Filters.jsx | 110 +++-- src/components/Header/Header.jsx | 48 ++- src/components/Header/MobileYearWrapper.jsx | 37 +- src/components/Header/Search.jsx | 62 +-- src/components/Header/Selector.jsx | 91 ++-- src/components/Logo.jsx | 28 +- src/components/Sidebar.jsx | 10 +- src/components/TableOfContents.jsx | 65 +-- src/consts.js | 13 +- src/index.jsx | 18 +- src/styles/App.css | 7 +- src/styles/Body.css | 276 ++++++------ src/styles/DropdownMenu.css | 38 +- src/styles/FaceDiv.css | 4 +- src/styles/PhotoInfo.css | 47 +- src/styles/Search.css | 51 +-- src/styles/Sidebar.css | 4 +- src/styles/TableOfContents.css | 94 ++-- src/styles/theme-variables.css | 16 +- src/theme.js | 120 +++--- vite.config.mjs | 41 +- 57 files changed, 2332 insertions(+), 1955 deletions(-) create mode 100644 .prettierrc.json diff --git a/.eslintrc.json b/.eslintrc.json index f58e6d3..07ea79a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,5 @@ { - "ignorePatterns": [ - "build/**" - ], + "ignorePatterns": ["build/**"], "env": { "node": true, "browser": true, @@ -10,7 +8,8 @@ "extends": [ "eslint:recommended", "plugin:react/recommended", - "plugin:react/jsx-runtime" + "plugin:react/jsx-runtime", + "plugin:prettier/recommended" ], "settings": { "react": { @@ -24,77 +23,28 @@ "ecmaVersion": 2022, "sourceType": "module" }, - "plugins": [ - "react", - "simple-import-sort" - ], + "plugins": ["react", "simple-import-sort"], "rules": { - "indent": [ - "error", - 4 - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "double" - ], - "semi": [ - "error", - "always" - ], - "eol-last": [ - "error", - "always" - ], - "no-case-declarations": "off", - "object-curly-spacing": [ - "error", - "always" - ], - "no-unused-vars": [ - "warn" - ], - "react/prop-types": [ - "off" - ], + "react/prop-types": ["off"], "simple-import-sort/imports": [ "error", { "groups": [ // Packages `react` related packages come first. - [ - "^react", - "^@?\\w" - ], + ["^react", "^@?\\w"], // Internal packages. - [ - "^(@|components)(/.*|$)" - ], + ["^(@|components)(/.*|$)"], // Side effect imports. - [ - "^\\u0000" - ], + ["^\\u0000"], // Parent imports. Put `..` last. - [ - "^\\.\\.(?!/?$)", - "^\\.\\./?$" - ], + ["^\\.\\.(?!/?$)", "^\\.\\./?$"], // Other relative imports. Put same-folder imports and `.` last. - [ - "^\\./(?=.*/)(?!/?$)", - "^\\.(?!/?$)", - "^\\./?$" - ], + ["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"], // Style imports. - [ - "^.+\\.?(css)$" - ] + ["^.+\\.?(css)$"] ] } ], "simple-import-sort/exports": "error" } -} \ No newline at end of file +} diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 1f0c2ae..d0ef66b 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -3,7 +3,7 @@ name: Publish GitHub Pages on: push: branches: - - 'main' + - "main" jobs: build: @@ -41,13 +41,12 @@ jobs: data_folder: dataACPC folder: acpc - steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '16' - cache: 'npm' + node-version: "16" + cache: "npm" - name: Install dependencies run: npm ci --legacy-peer-deps - name: Build @@ -89,7 +88,7 @@ jobs: name: github-pages url: ${{ steps.deployment.outputs.page_url }} permissions: write-all - steps: + steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 859e7e8..7c33842 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,7 @@ name: Publish release on: push: branches: - - 'main' + - "main" jobs: build: @@ -33,13 +33,13 @@ jobs: data_folder: dataEU - public_url: galleryACPC # acpc data_folder: dataACPC - + steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '16' - cache: 'npm' + node-version: "16" + cache: "npm" - name: Install dependencies run: npm ci --legacy-peer-deps - name: Build diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..75fa134 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "tabWidth": 2 +} diff --git a/README.md b/README.md index 242f208..a2e6c0d 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,15 @@ Created to substitute [news.icpc.global/gallery](https://news.icpc.global/galler [Figma mockups](https://www.figma.com/file/MvNh0jm8dj0LXOh9vsVUbK/ICPC-Live?node-id=0%3A1) Important features to keep: -* no-backend flickr-based solution -* photos are classified based on public flickr tags: year, event, team -* individuals are tagged as "Firstname Lastname (location)" -* show metadata information: photographer, year, event, team, tagged individuals, highlight tag locations -* search by substring over all tags -* easy image download -* adaptive interface for larger screens -* mobile version + +- no-backend flickr-based solution +- photos are classified based on public flickr tags: year, event, team +- individuals are tagged as "Firstname Lastname (location)" +- show metadata information: photographer, year, event, team, tagged individuals, highlight tag locations +- search by substring over all tags +- easy image download +- adaptive interface for larger screens +- mobile version # User guide @@ -41,42 +42,45 @@ To search by a keyword just type it into the Search field. For example, `Bill`, # Development The gallery is built on top of the flickr api backend. All metadata is stored in photo exif tags. Tag types are: -* year, for example `album$2001` -* event, for example `event$Team Photos` -* team, for example `team$Aalto University` -* person, for example `Bill Poucher(1761524d3f579a4d)`. The data in parenthesis denotes face location within the image in Picasa standard. + +- year, for example `album$2001` +- event, for example `event$Team Photos` +- team, for example `team$Aalto University` +- person, for example `Bill Poucher(1761524d3f579a4d)`. The data in parenthesis denotes face location within the image in Picasa standard. Visit [tutorial](https://docs.google.com/document/d/1yBeLEYyGG2FpZGjBmAHP37gJAnKGtA56hAf1XjW8d18/edit#heading=h.iv1qq69p60ub) for more info. ## Picasa Standard Face Tag -The 16 characters enclosed in rect64(…) is a 64-bit hexadecimal number which can be broken up into four 16-bit numbers used to identify the position of the rectangle used to mark the face. -If you divide each of the four 16-bit numbers by the maximum unsigned 16-bit number (65535), you’ll get four numbers between 0 and 1 which give the relative coordinates for the face rectangle in the order: left, top, right, bottom. +The 16 characters enclosed in rect64(…) is a 64-bit hexadecimal number which can be broken up into four 16-bit numbers used to identify the position of the rectangle used to mark the face. + +If you divide each of the four 16-bit numbers by the maximum unsigned 16-bit number (65535), you’ll get four numbers between 0 and 1 which give the relative coordinates for the face rectangle in the order: left, top, right, bottom. -To calculate the absolute coordinates, multiply the left and right relative coordinates by the width of the image and multiply the top and bottom relative coordinates by the height of the image. This way the faces will always be identified even when the image is re-sized. +To calculate the absolute coordinates, multiply the left and right relative coordinates by the width of the image and multiply the top and bottom relative coordinates by the height of the image. This way the faces will always be identified even when the image is re-sized. ## Flickr Tag Processing Even though flickr stores tags in their initial form, it is important to remember the following -* by default, manually entered tags are split into separate words. Enclose the tag in quotes `"team$Aalto University"` to keep tag intact -* flickr eliminates all spaces and non-letter sybmols from tags for the filtering purposes -* make sure to keep correct encoding on tags, specifically in people names, to consistently process diacritic symbols -* make sure to enclose all university names with punctuation in quotes + +- by default, manually entered tags are split into separate words. Enclose the tag in quotes `"team$Aalto University"` to keep tag intact +- flickr eliminates all spaces and non-letter sybmols from tags for the filtering purposes +- make sure to keep correct encoding on tags, specifically in people names, to consistently process diacritic symbols +- make sure to enclose all university names with punctuation in quotes ## Configuration -[`src/const.js`](https://github.com/icpc/gallery/blob/main/data/consts.js) contais access detail and general year setup. +[`src/const.js`](https://github.com/icpc/gallery/blob/main/data/consts.js) contais access detail and general year setup. This repository supports multiple galleries, each with its own dedicated [`/data/`](https://github.com/icpc/gallery/tree/main/data) folder. -Each year is represented by three files in this folder: ```.event```, ```.people``` and ```.team``` with one item per line. +Each year is represented by three files in this folder: `.event`, `.people` and `.team` with one item per line. ## Production Deployment Guide 1. After every commit Action [Publish Release](https://github.com/icpc/gallery/actions) will run automatically. 1. Open the Action page for the corresponding run. Wait for it to finish -1. Download the created ```all_raw``` on the bottom of the page -1. Upload the acquired ```all_raw.zip``` archive into Bluehost -> Advanced -> File Manager -> under ```home/public_html/newsicpc/``` folder -1. Select ```all_raw.zip``` in the bluehost filemanager webinterface and press the ```Extract``` button +1. Download the created `all_raw` on the bottom of the page +1. Upload the acquired `all_raw.zip` archive into Bluehost -> Advanced -> File Manager -> under `home/public_html/newsicpc/` folder +1. Select `all_raw.zip` in the bluehost filemanager webinterface and press the `Extract` button 1. All new galleries are deployed, good job! ## Development Deployment Guide @@ -102,10 +106,9 @@ npm run build -- --base=/%PUBLIC_URL% ## Future Work: -* easy interface to suggest a photo tag change -* (?) support universities changing names -* rename events for better consistence (correct flickr tags) -* release badge recognition tool -* release tag embedding tool -* support a face recognition tool - +- easy interface to suggest a photo tag change +- (?) support universities changing names +- rename events for better consistence (correct flickr tags) +- release badge recognition tool +- release tag embedding tool +- support a face recognition tool diff --git a/data/consts.js b/data/consts.js index 6ca6192..a5c730c 100644 --- a/data/consts.js +++ b/data/consts.js @@ -1,34 +1,34 @@ export const places = [ - ["2023", "Luxor, Egypt, 47th", "World Finals"], - ["2022", "Luxor, Egypt, 46th", "World Finals"], - ["2021", "Dhaka, Bangladesh", "World Finals"], - ["2020", "Moscow, Russia", "World Finals"], - ["2019", "Porto, Portugal", "World Finals"], - ["2018", "Beijing, China", "World Finals"], - ["2017", "Rapid City, SD, USA", "World Finals"], - ["2016", "Phuket, Thailand", "World Finals"], - ["2015", "Marrakesh, Morocco", "World Finals"], - ["2014", "Ekaterinburg, Russia", "World Finals"], - ["2013", "Saint Petersburg, Russia", "World Finals"], - ["2012", "Warsaw, Poland", "World Finals"], - ["2011", "Orlando FL, USA", "World Finals"], - ["2010", "Harbin, China", "World Finals"], - ["2009", "Stockholm, Sweden", "World Finals"], - ["2008", "Banff, Canada", "World Finals"], - ["2007", "Tokyo, Japan", "World Finals"], - ["2006", "San Antonio, TX, USA", "World Finals"], - ["2005", "Shanghai, China", "World Finals"], - ["2004", "Prague, Czech Republic", "World Finals"], - ["2003", "Beverly Hills, CA, USA", "World Finals"], - ["2002", "Honolulu, Hawaii, USA", "World Finals"], - ["2001", "Vancouver, Canada", "World Finals"] + ["2023", "Luxor, Egypt, 47th", "World Finals"], + ["2022", "Luxor, Egypt, 46th", "World Finals"], + ["2021", "Dhaka, Bangladesh", "World Finals"], + ["2020", "Moscow, Russia", "World Finals"], + ["2019", "Porto, Portugal", "World Finals"], + ["2018", "Beijing, China", "World Finals"], + ["2017", "Rapid City, SD, USA", "World Finals"], + ["2016", "Phuket, Thailand", "World Finals"], + ["2015", "Marrakesh, Morocco", "World Finals"], + ["2014", "Ekaterinburg, Russia", "World Finals"], + ["2013", "Saint Petersburg, Russia", "World Finals"], + ["2012", "Warsaw, Poland", "World Finals"], + ["2011", "Orlando FL, USA", "World Finals"], + ["2010", "Harbin, China", "World Finals"], + ["2009", "Stockholm, Sweden", "World Finals"], + ["2008", "Banff, Canada", "World Finals"], + ["2007", "Tokyo, Japan", "World Finals"], + ["2006", "San Antonio, TX, USA", "World Finals"], + ["2005", "Shanghai, China", "World Finals"], + ["2004", "Prague, Czech Republic", "World Finals"], + ["2003", "Beverly Hills, CA, USA", "World Finals"], + ["2002", "Honolulu, Hawaii, USA", "World Finals"], + ["2001", "Vancouver, Canada", "World Finals"], ]; - export const api_key = "d30033b3d833adaace90f8487da70bba"; export const user_id = "141939107@N06"; export const title = "ICPC Gallery"; -export const description = "The International Collegiate Programming Contest World Finals Photo Gallery."; +export const description = + "The International Collegiate Programming Contest World Finals Photo Gallery."; export const FLICKR_IMAGE_PREFIX = "https://www.flickr.com/photos/icpcnews/"; export const SUGGESTIONS_EMAIL = "lidia+gallery@icpc.global"; diff --git a/dataACPC/consts.js b/dataACPC/consts.js index f8b5fe1..c3f756a 100644 --- a/dataACPC/consts.js +++ b/dataACPC/consts.js @@ -1,9 +1,8 @@ export const places = [ - ["RC 2023-2024", "Regional Contests", ""], - ["2023", "", ""] + ["RC 2023-2024", "Regional Contests", ""], + ["2023", "", ""], ]; - export const api_key = "d30033b3d833adaace90f8487da70bba"; export const user_id = "141939107@N06"; export const title = "ACPC Gallery"; diff --git a/dataAE/consts.js b/dataAE/consts.js index 166d002..c61f674 100644 --- a/dataAE/consts.js +++ b/dataAE/consts.js @@ -1,9 +1,8 @@ export const places = [ - ["RC 2023-2024", "Regional Contests", ""], - ["2023", "", ""] + ["RC 2023-2024", "Regional Contests", ""], + ["2023", "", ""], ]; - export const api_key = "d30033b3d833adaace90f8487da70bba"; export const user_id = "141939107@N06"; export const title = "Asia East Gallery"; diff --git a/dataAP/consts.js b/dataAP/consts.js index 5b3eef1..5ed0755 100644 --- a/dataAP/consts.js +++ b/dataAP/consts.js @@ -1,9 +1,8 @@ export const places = [ - ["2024", "", ""], - ["RC 2023-2024", "Regional Contests", ""] + ["2024", "", ""], + ["RC 2023-2024", "Regional Contests", ""], ]; - export const api_key = "d30033b3d833adaace90f8487da70bba"; export const user_id = "141939107@N06"; export const title = "Asia Pacific Gallery"; diff --git a/dataAW/consts.js b/dataAW/consts.js index ad119b6..66a6422 100644 --- a/dataAW/consts.js +++ b/dataAW/consts.js @@ -1,9 +1,8 @@ export const places = [ - ["RC 2023-2024", "Regional Contests", ""], - ["2023", "", ""] + ["RC 2023-2024", "Regional Contests", ""], + ["2023", "", ""], ]; - export const api_key = "d30033b3d833adaace90f8487da70bba"; export const user_id = "141939107@N06"; export const title = "Asia West Gallery"; diff --git a/dataEU/consts.js b/dataEU/consts.js index 742bb17..465a93c 100644 --- a/dataEU/consts.js +++ b/dataEU/consts.js @@ -1,9 +1,8 @@ export const places = [ - ["2024", "EUC, Prague", ""], - ["RC 2023-2024", "Regional Contests", ""] + ["2024", "EUC, Prague", ""], + ["RC 2023-2024", "Regional Contests", ""], ]; - export const api_key = "d30033b3d833adaace90f8487da70bba"; export const user_id = "141939107@N06"; export const title = "Europe Gallery"; diff --git a/dataLAC/consts.js b/dataLAC/consts.js index 7d09f33..92a7cc9 100644 --- a/dataLAC/consts.js +++ b/dataLAC/consts.js @@ -1,10 +1,7 @@ - -export const places = - [ - ["RC 2023-2024", "Regional Contests", ""], - ["2023", "", ""] - ]; - +export const places = [ + ["RC 2023-2024", "Regional Contests", ""], + ["2023", "", ""], +]; export const api_key = "d30033b3d833adaace90f8487da70bba"; export const user_id = "141939107@N06"; diff --git a/dataNAC/consts.js b/dataNAC/consts.js index e2e9794..fdfe4c0 100644 --- a/dataNAC/consts.js +++ b/dataNAC/consts.js @@ -1,26 +1,24 @@ - export const places = [ - ["2024", "NAC, Orlando, FL", ""], - ["RC 2023-2024", "Regional Contests", ""], - ["2023", "NAC, Orlando, FL", ""], - ["RC 2022-2023", "Regional Contests", ""], - ["2022", "NAC, Orlando, FL", ""], - ["RC 2021-2022", "Regional Contests", ""], - ["2021", "NAC, Online", ""], - ["2020", "NAC, Atlanta, GA", ""], - ["RC 2019-2020", "Contests", ""], - ["RC 2018-2019", "Contests", ""], - ["RC 2016-2017", "Contests", ""], - ["RC 2015-2016", "Contests", ""], - ["RC 2014-2015", "Contests", ""], - ["2014", "NAIPC", ""], - ["RC 2013-2014", "Contests", ""], - ["2013", "UCIPC", ""], - ["RC 2012-2013", "Contests", ""], - ["RC 2011-2012", "Contests", ""] + ["2024", "NAC, Orlando, FL", ""], + ["RC 2023-2024", "Regional Contests", ""], + ["2023", "NAC, Orlando, FL", ""], + ["RC 2022-2023", "Regional Contests", ""], + ["2022", "NAC, Orlando, FL", ""], + ["RC 2021-2022", "Regional Contests", ""], + ["2021", "NAC, Online", ""], + ["2020", "NAC, Atlanta, GA", ""], + ["RC 2019-2020", "Contests", ""], + ["RC 2018-2019", "Contests", ""], + ["RC 2016-2017", "Contests", ""], + ["RC 2015-2016", "Contests", ""], + ["RC 2014-2015", "Contests", ""], + ["2014", "NAIPC", ""], + ["RC 2013-2014", "Contests", ""], + ["2013", "UCIPC", ""], + ["RC 2012-2013", "Contests", ""], + ["RC 2011-2012", "Contests", ""], ]; - export const api_key = "d30033b3d833adaace90f8487da70bba"; export const user_id = "141939107@N06"; export const title = "NAC Gallery"; diff --git a/dataNERC/consts.js b/dataNERC/consts.js index e7f6946..526fca4 100644 --- a/dataNERC/consts.js +++ b/dataNERC/consts.js @@ -1,23 +1,23 @@ export const places = [ - ["2023", "SPb, Novosibirsk, Astana, Kutaisi", ""], - ["RC 2023-2024", "Regional Contests", ""], - ["2022", "SPb, Tinkoff Arena", ""], - ["RC 2022-2023", "Regional Contests", ""], - ["2021", "SPb, Tinkoff Arena", ""], - ["2020", "SPb, My history park", ""], - ["2019", "SPb, My history park", ""], - ["2018", "SPb, ITMO University", ""], - ["2017", "SPb, ITMO University", ""], - ["2016", "SPb, ITMO University", ""], - ["2015", "SPb, ITMO University", ""], - ["2014", "SPb, ITMO University", ""], - ["2013", "SPb, ITMO University", ""], - ["2012", "SPb, ITMO University", ""], - ["2011", "SPb, ITMO University", ""], - ["2004", "SPb, Palace of Youth Creativity", ""], - ["2003", "SPb, Palace of Youth Creativity", ""], - ["2001", "SPb, Palace of Youth Creativity", ""], - ["2000", "SPb, Palace of Youth Creativity", ""] + ["2023", "SPb, Novosibirsk, Astana, Kutaisi", ""], + ["RC 2023-2024", "Regional Contests", ""], + ["2022", "SPb, Tinkoff Arena", ""], + ["RC 2022-2023", "Regional Contests", ""], + ["2021", "SPb, Tinkoff Arena", ""], + ["2020", "SPb, My history park", ""], + ["2019", "SPb, My history park", ""], + ["2018", "SPb, ITMO University", ""], + ["2017", "SPb, ITMO University", ""], + ["2016", "SPb, ITMO University", ""], + ["2015", "SPb, ITMO University", ""], + ["2014", "SPb, ITMO University", ""], + ["2013", "SPb, ITMO University", ""], + ["2012", "SPb, ITMO University", ""], + ["2011", "SPb, ITMO University", ""], + ["2004", "SPb, Palace of Youth Creativity", ""], + ["2003", "SPb, Palace of Youth Creativity", ""], + ["2001", "SPb, Palace of Youth Creativity", ""], + ["2000", "SPb, Palace of Youth Creativity", ""], ]; export const api_key = "d30033b3d833adaace90f8487da70bba"; @@ -34,7 +34,6 @@ export const TAG_ALBUM = "albumNERC"; export const TAG_PERSON = "person"; export const TAG_PHOTOGRAPHER = "photographer"; - export const SVG_WIDTH = 60; export const SVG_HEIGHT = 60; diff --git a/index.html b/index.html index ff6666f..552cc43 100644 --- a/index.html +++ b/index.html @@ -1,26 +1,29 @@ - + - - + - + - + - +
- - + diff --git a/package-lock.json b/package-lock.json index f457a5e..faf703d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,8 @@ "@vitejs/plugin-react": "^4.2.1", "babel-plugin-styled-components": "^2.0.7", "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-simple-import-sort": "^10.0.0", "vite": "^5.0.11" @@ -1437,6 +1439,18 @@ "node": ">= 8" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@popperjs/core": { "version": "2.11.6", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", @@ -2521,6 +2535,48 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.33.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", @@ -2797,6 +2853,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4091,6 +4153,34 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "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/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4755,6 +4845,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/terser": { "version": "5.19.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.1.tgz", @@ -6024,6 +6136,12 @@ "fastq": "^1.6.0" } }, + "@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true + }, "@popperjs/core": { "version": "2.11.6", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", @@ -6920,6 +7038,23 @@ } } }, + "eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + } + }, "eslint-plugin-react": { "version": "7.33.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", @@ -7036,6 +7171,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -7938,6 +8079,22 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "peer": 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" + } + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -8406,6 +8563,24 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "synckit": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "dev": true, + "requires": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + } + } + }, "terser": { "version": "5.19.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.1.tgz", diff --git a/package.json b/package.json index 8188418..145c98a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "start": "vite --profile", "build": "vite build", "preview": "vite preview", - "lint": "eslint src --ext .js,.jsx,.ts,.tsx" + "lint": "eslint src --ext .js,.jsx,.ts,.tsx", + "format": "prettier . --write" }, "eslintConfig": { "extends": [ @@ -49,6 +50,8 @@ "@vitejs/plugin-react": "^4.2.1", "babel-plugin-styled-components": "^2.0.7", "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-simple-import-sort": "^10.0.0", "vite": "^5.0.11" diff --git a/src/App.jsx b/src/App.jsx index cd6de00..5fc92cd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -14,31 +14,43 @@ import theme from "./theme"; import "./styles/App.css"; - function App() { - const { desktop } = useAppContext(); + const { desktop } = useAppContext(); - return ( - - - {title} - - - - - - {desktop && - - - - } - -
- - - - - ); + return ( + + + {title} + + + + + + {desktop && ( + + + + + )} + +
+ + + + + ); } export default App; diff --git a/src/Util/DataLoader.js b/src/Util/DataLoader.js index f45237a..1db64f1 100644 --- a/src/Util/DataLoader.js +++ b/src/Util/DataLoader.js @@ -3,27 +3,42 @@ import { getRawEventData, getRawPeopleData, getRawTeamData } from "../consts"; import UniqueList from "./UniqueList"; async function getEventData(year) { - if (!year) { - return []; - } - const response = await getRawEventData(year); - return UniqueList(response.split("\n").map(i => i.trim()).filter(i => i != "")); + if (!year) { + return []; + } + const response = await getRawEventData(year); + return UniqueList( + response + .split("\n") + .map((i) => i.trim()) + .filter((i) => i != ""), + ); } async function getTeamData(year) { - if (!year) { - return []; - } - const response = await getRawTeamData(year); - return UniqueList(response.split("\n").map(i => i.trim()).filter(i => i != "")); + if (!year) { + return []; + } + const response = await getRawTeamData(year); + return UniqueList( + response + .split("\n") + .map((i) => i.trim()) + .filter((i) => i != ""), + ); } async function getPeopleData(year) { - if (!year) { - return []; - } - const response = await getRawPeopleData(year); - return UniqueList(response.split("\n").map(i => i.trim()).filter(i => i != "")); + if (!year) { + return []; + } + const response = await getRawPeopleData(year); + return UniqueList( + response + .split("\n") + .map((i) => i.trim()) + .filter((i) => i != ""), + ); } export { getEventData, getPeopleData, getTeamData }; diff --git a/src/Util/PhotoInfoHelper.js b/src/Util/PhotoInfoHelper.js index de386b0..deeccdb 100644 --- a/src/Util/PhotoInfoHelper.js +++ b/src/Util/PhotoInfoHelper.js @@ -1,4 +1,10 @@ -import { TAG_ALBUM, TAG_EVENT, TAG_PERSON, TAG_PHOTOGRAPHER, TAG_TEAM } from "../consts"; +import { + TAG_ALBUM, + TAG_EVENT, + TAG_PERSON, + TAG_PHOTOGRAPHER, + TAG_TEAM, +} from "../consts"; import UniqueList from "./UniqueList"; @@ -7,115 +13,129 @@ const MAX_INT = 65535; const convertRel = (text) => convertNum(text) / MAX_INT; function getPosition(encodedPosition) { - return { - left: convertRel(encodedPosition.substr(0, 4)), - top: convertRel(encodedPosition.substr(4, 4)), - right: convertRel(encodedPosition.substr(8, 4)), - bottom: convertRel(encodedPosition.substr(12, 4)), - }; + return { + left: convertRel(encodedPosition.substr(0, 4)), + top: convertRel(encodedPosition.substr(4, 4)), + right: convertRel(encodedPosition.substr(8, 4)), + bottom: convertRel(encodedPosition.substr(12, 4)), + }; } function serizalizePosition(position) { - return Math.round(position.left * MAX_INT).toString(16).padStart(4, "0") + - Math.round(position.top * MAX_INT).toString(16).padStart(4, "0") + - Math.round(position.right * MAX_INT).toString(16).padStart(4, "0") + - Math.round(position.bottom * MAX_INT).toString(16).padStart(4, "0"); + return ( + Math.round(position.left * MAX_INT) + .toString(16) + .padStart(4, "0") + + Math.round(position.top * MAX_INT) + .toString(16) + .padStart(4, "0") + + Math.round(position.right * MAX_INT) + .toString(16) + .padStart(4, "0") + + Math.round(position.bottom * MAX_INT) + .toString(16) + .padStart(4, "0") + ); } function parsePerson(personTag) { - const openBracket = personTag.indexOf("("); - if (openBracket === -1) { - return null; - } - const name = personTag.substr(0, openBracket); - const closingBracket = personTag.indexOf(")", openBracket); - const encodedPosition = personTag.substr(openBracket + 1, closingBracket - openBracket - 1); - if (name === "") { - return null; - } - return { - name: name, - position: getPosition(encodedPosition) - }; + const openBracket = personTag.indexOf("("); + if (openBracket === -1) { + return null; + } + const name = personTag.substr(0, openBracket); + const closingBracket = personTag.indexOf(")", openBracket); + const encodedPosition = personTag.substr( + openBracket + 1, + closingBracket - openBracket - 1, + ); + if (name === "") { + return null; + } + return { + name: name, + position: getPosition(encodedPosition), + }; } function serizalizePerson(person) { - return person.name + "(" + serizalizePosition(person.position) + ")"; + return person.name + "(" + serizalizePosition(person.position) + ")"; } const convertNum = (text) => parseInt("0x" + text); function ParsePhotoInfo(tags, description) { - let photoInfo = { - event: [], - team: [], - person: [], - album: [], - photographer: [], - }; - photoInfo.photographer.push(description?.replaceAll("Photographer: ", "")); - if (tags === null || tags === undefined) { - return photoInfo; - } - for (const tag of tags) { - if (tag === null) { - continue; - } - if (tag.startsWith(TAG_EVENT)) { - photoInfo.event.push(tag.replaceAll(TAG_EVENT + "$", "")); - continue; - } - if (tag.startsWith(TAG_TEAM)) { - photoInfo.team.push(tag.replaceAll(TAG_TEAM + "$", "")); - continue; - } - if (tag.startsWith(TAG_PERSON)) { - photoInfo.person.push({ name: tag.replaceAll(TAG_PERSON + "$", "") }); - continue; - } - if (tag.startsWith(TAG_ALBUM)) { - photoInfo.album.push(tag.replaceAll(TAG_ALBUM + "$", "")); - continue; - } - if (tag.startsWith(TAG_PHOTOGRAPHER)) { - photoInfo.photographer.push(tag.replaceAll(TAG_PHOTOGRAPHER + "$", "")); - continue; - } - const parsedPerson = parsePerson(tag); - if (parsedPerson !== null) { - photoInfo.person.push(parsedPerson); - } - } - photoInfo.event = UniqueList(photoInfo.event); - photoInfo.team = UniqueList(photoInfo.team); - photoInfo.album = UniqueList(photoInfo.album); - photoInfo.photographer = UniqueList(photoInfo.photographer); - + let photoInfo = { + event: [], + team: [], + person: [], + album: [], + photographer: [], + }; + photoInfo.photographer.push(description?.replaceAll("Photographer: ", "")); + if (tags === null || tags === undefined) { return photoInfo; -} - -function SerializePhotoInfo(photoInfo) { - if (!photoInfo) { - return []; + } + for (const tag of tags) { + if (tag === null) { + continue; + } + if (tag.startsWith(TAG_EVENT)) { + photoInfo.event.push(tag.replaceAll(TAG_EVENT + "$", "")); + continue; } - let tags = []; - for (const event of photoInfo.event) { - tags.push(TAG_EVENT + "$" + event); + if (tag.startsWith(TAG_TEAM)) { + photoInfo.team.push(tag.replaceAll(TAG_TEAM + "$", "")); + continue; } - for (const team of photoInfo.team) { - tags.push(TAG_TEAM + "$" + team); + if (tag.startsWith(TAG_PERSON)) { + photoInfo.person.push({ + name: tag.replaceAll(TAG_PERSON + "$", ""), + }); + continue; } - for (const album of photoInfo.album) { - tags.push(TAG_ALBUM + "$" + album); + if (tag.startsWith(TAG_ALBUM)) { + photoInfo.album.push(tag.replaceAll(TAG_ALBUM + "$", "")); + continue; } - for (const photographer of photoInfo.photographer) { - tags.push(TAG_PHOTOGRAPHER + "$" + photographer); + if (tag.startsWith(TAG_PHOTOGRAPHER)) { + photoInfo.photographer.push(tag.replaceAll(TAG_PHOTOGRAPHER + "$", "")); + continue; } - for (const person of photoInfo.person) { - tags.push(serizalizePerson(person)); + const parsedPerson = parsePerson(tag); + if (parsedPerson !== null) { + photoInfo.person.push(parsedPerson); } - return tags.map(tag => `"${tag.trim()}"`); + } + photoInfo.event = UniqueList(photoInfo.event); + photoInfo.team = UniqueList(photoInfo.team); + photoInfo.album = UniqueList(photoInfo.album); + photoInfo.photographer = UniqueList(photoInfo.photographer); + + return photoInfo; } +function SerializePhotoInfo(photoInfo) { + if (!photoInfo) { + return []; + } + let tags = []; + for (const event of photoInfo.event) { + tags.push(TAG_EVENT + "$" + event); + } + for (const team of photoInfo.team) { + tags.push(TAG_TEAM + "$" + team); + } + for (const album of photoInfo.album) { + tags.push(TAG_ALBUM + "$" + album); + } + for (const photographer of photoInfo.photographer) { + tags.push(TAG_PHOTOGRAPHER + "$" + photographer); + } + for (const person of photoInfo.person) { + tags.push(serizalizePerson(person)); + } + return tags.map((tag) => `"${tag.trim()}"`); +} export { ParsePhotoInfo, SerializePhotoInfo }; diff --git a/src/Util/PhotoLoader.js b/src/Util/PhotoLoader.js index cbe8581..91d010d 100644 --- a/src/Util/PhotoLoader.js +++ b/src/Util/PhotoLoader.js @@ -6,7 +6,6 @@ import { places, TAG_ALBUM } from "../consts"; import PhotoService from "./PhotoService"; import UniqueList from "./UniqueList"; - /** * Custom hook that loads photos based on the current app context. * @returns {Object} - An object containing the following properties: @@ -16,130 +15,177 @@ import UniqueList from "./UniqueList"; * - photosList: An array of all photo objects. */ const usePhotoLoader = () => { - const { data, events } = useAppContext(); - - /** - * State hook that stores a Map object containing photo objects grouped by event. - * @type {[Map, function]} - A tuple containing the current state value and a function to update it. - */ - const [photosByEvent, setPhotosByEvent] = useState(new Map()); - const [page, setPage] = useState(1); - const [totalPages, setTotalPages] = useState(1); - const [internalEvent, setInternalEvent] = useState(undefined); - const [fetching, setFetching] = useState(false); - const [axiosCancelController, setAxiosCancelController] = useState(new AbortController()); - - useEffect(() => { - axiosCancelController.abort(); - setPhotosByEvent(new Map()); - setPage(1); - setInternalEvent(data.event); - setTotalPages(1); - setAxiosCancelController(new AbortController()); - }, [data.year, data.event, data.text, data.team, data.person]); - - - function appendPhotos(photosByEvent, event, photos) { - const appendedEventPhotos = [...(photosByEvent.get(event) || []), ...photos]; - return new Map(photosByEvent.set(event, UniqueList(appendedEventPhotos, photo => photo.id))); + const { data, events } = useAppContext(); + + /** + * State hook that stores a Map object containing photo objects grouped by event. + * @type {[Map, function]} - A tuple containing the current state value and a function to update it. + */ + const [photosByEvent, setPhotosByEvent] = useState(new Map()); + const [page, setPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [internalEvent, setInternalEvent] = useState(undefined); + const [fetching, setFetching] = useState(false); + const [axiosCancelController, setAxiosCancelController] = useState( + new AbortController(), + ); + + useEffect(() => { + axiosCancelController.abort(); + setPhotosByEvent(new Map()); + setPage(1); + setInternalEvent(data.event); + setTotalPages(1); + setAxiosCancelController(new AbortController()); + }, [data.year, data.event, data.text, data.team, data.person]); + + function appendPhotos(photosByEvent, event, photos) { + const appendedEventPhotos = [ + ...(photosByEvent.get(event) || []), + ...photos, + ]; + return new Map( + photosByEvent.set( + event, + UniqueList(appendedEventPhotos, (photo) => photo.id), + ), + ); + } + + function getNextEvent(currentEvent) { + if (currentEvent == null || events === undefined) return null; + const index = events.findIndex((event) => event === currentEvent); + if (index === -1 || index + 1 < events.length) { + return events[index + 1]; } - - function getNextEvent(currentEvent) { - if (currentEvent == null || events === undefined) - return null; - const index = events.findIndex(event => event === currentEvent); - if (index === -1 || index + 1 < events.length) { - return events[index + 1]; - } - return null; + return null; + } + + function albumFromTags(tags) { + const albumTag = TAG_ALBUM.toLowerCase(); + const tag = tags.split(" ").find((tag) => tag.startsWith(albumTag)); + if (tag === undefined) { + return "Unknown"; } - - function albumFromTags(tags) { - const albumTag = TAG_ALBUM.toLowerCase(); - const tag = tags.split(" ").find(tag => tag.startsWith(albumTag)); - if (tag === undefined) { - return "Unknown"; - } - const targetTag = tag.replaceAll(albumTag, ""); - const found_tag = places.map(({ year }) => year).find((year) => (year.toLowerCase().replace(/[-_\s]/g, "") === targetTag)); - if (found_tag === undefined) { - return "Unknown"; - } - return found_tag; + const targetTag = tag.replaceAll(albumTag, ""); + const found_tag = places + .map(({ year }) => year) + .find((year) => year.toLowerCase().replace(/[-_\s]/g, "") === targetTag); + if (found_tag === undefined) { + return "Unknown"; + } + return found_tag; + } + + /** + * Returns a flattened array of all photos from all events. + * @returns {Array} - An array of photo objects. + */ + const photosList = useMemo( + () => [].concat(...Array.from(photosByEvent.values())), + [photosByEvent], + ); + + /** + * Returns a boolean indicating whether there are more photos to load. + * @returns {boolean} - A boolean indicating whether there are more photos to load. + */ + function hasMorePhotos() { + return page <= totalPages || getNextEvent(internalEvent) !== null; + } + + /** + * Loads more photos. + * Only one loadMorePhotos call can be active at a time. + */ + const loadMorePhotos = async () => { + if (fetching) { + return; } - /** - * Returns a flattened array of all photos from all events. - * @returns {Array} - An array of photo objects. - */ - const photosList = useMemo(() => [].concat(...Array.from(photosByEvent.values())), - [photosByEvent]); - - /** - * Returns a boolean indicating whether there are more photos to load. - * @returns {boolean} - A boolean indicating whether there are more photos to load. - */ - function hasMorePhotos() { - return page <= totalPages || getNextEvent(internalEvent) !== null; + setFetching(true); + + const nextEvent = getNextEvent(internalEvent); + if (page > totalPages && nextEvent !== null) { + setPage(1); + setTotalPages(1); + setInternalEvent(nextEvent); + setFetching(false); + return; } - /** - * Loads more photos. - * Only one loadMorePhotos call can be active at a time. - */ - const loadMorePhotos = async () => { - if (fetching) { - return; - } - - setFetching(true); - - const nextEvent = getNextEvent(internalEvent); - if (page > totalPages && nextEvent !== null) { - setPage(1); - setTotalPages(1); - setInternalEvent(nextEvent); - setFetching(false); - return; - } - - const config = { - signal: axiosCancelController.signal, - }; - - try { - let response; - if (internalEvent) { - response = await PhotoService.getAllWithEvent(data.year, encodeURIComponent(internalEvent), page, config); - } else if (data.team) { - response = await PhotoService.getAllWithTeam(data.year, encodeURIComponent(data.team), page, config); - } else if (data.person) { - response = await PhotoService.getAllWithPerson(data.year, encodeURIComponent(data.person), page, config); - } else if (data.text) { - response = await PhotoService.getAllWithText(encodeURIComponent(data.text), page, config); - } - - if (response) { - const newPhotosByEvent = appendPhotos(photosByEvent, internalEvent, response.data.photos.photo.map(({ url_m, url_o, url_l, id, width_o, width_l, height_o, height_l, tags }) => ({ - url_preview: url_m ?? url_o, - url: url_l ?? url_o, - width: width_l ?? width_o, - height: height_l ?? height_o, - id, - origin: url_o, - year: albumFromTags(tags), - }))); - setTotalPages(response.data.photos.pages); - setPage(page + 1); - - setPhotosByEvent(newPhotosByEvent); - } - } finally { - setFetching(false); - } + const config = { + signal: axiosCancelController.signal, }; - return { hasMorePhotos, loadMorePhotos, photosByEvent, photosList }; + try { + let response; + if (internalEvent) { + response = await PhotoService.getAllWithEvent( + data.year, + encodeURIComponent(internalEvent), + page, + config, + ); + } else if (data.team) { + response = await PhotoService.getAllWithTeam( + data.year, + encodeURIComponent(data.team), + page, + config, + ); + } else if (data.person) { + response = await PhotoService.getAllWithPerson( + data.year, + encodeURIComponent(data.person), + page, + config, + ); + } else if (data.text) { + response = await PhotoService.getAllWithText( + encodeURIComponent(data.text), + page, + config, + ); + } + + if (response) { + const newPhotosByEvent = appendPhotos( + photosByEvent, + internalEvent, + response.data.photos.photo.map( + ({ + url_m, + url_o, + url_l, + id, + width_o, + width_l, + height_o, + height_l, + tags, + }) => ({ + url_preview: url_m ?? url_o, + url: url_l ?? url_o, + width: width_l ?? width_o, + height: height_l ?? height_o, + id, + origin: url_o, + year: albumFromTags(tags), + }), + ), + ); + setTotalPages(response.data.photos.pages); + setPage(page + 1); + + setPhotosByEvent(newPhotosByEvent); + } + } finally { + setFetching(false); + } + }; + + return { hasMorePhotos, loadMorePhotos, photosByEvent, photosList }; }; export default usePhotoLoader; diff --git a/src/Util/PhotoService.js b/src/Util/PhotoService.js index 3288e4d..71fcc5f 100644 --- a/src/Util/PhotoService.js +++ b/src/Util/PhotoService.js @@ -1,38 +1,65 @@ import axios from "axios"; -import { api_key, PER_PAGE, TAG_ALBUM, TAG_EVENT, TAG_TEAM, user_id } from "../consts"; +import { + api_key, + PER_PAGE, + TAG_ALBUM, + TAG_EVENT, + TAG_TEAM, + user_id, +} from "../consts"; export default class PhotoService { - // Parameters help: https://www.flickr.com/services/api/flickr.photos.search.html - static extras = "tags,machine_tags,url_m,url_c,url_l,url_o,description,date_upload,date_taken"; - static parameters = `&sort=date-taken-desc&per_page=${PER_PAGE}&extras=${PhotoService.extras}&format=json&nojsoncallback=?`; + // Parameters help: https://www.flickr.com/services/api/flickr.photos.search.html + static extras = + "tags,machine_tags,url_m,url_c,url_l,url_o,description,date_upload,date_taken"; + static parameters = `&sort=date-taken-desc&per_page=${PER_PAGE}&extras=${PhotoService.extras}&format=json&nojsoncallback=?`; - static getAllWithEvent(year, event = "Photo%20Tour", page = 1, config = {}) { - return this.getAll(`https://api.flickr.com/services/rest?method=flickr.photos.search&api_key=${api_key}&user_id=${user_id}&tags=${TAG_EVENT}$${event},${TAG_ALBUM}$${year}&tag_mode=all&page=${page}` + PhotoService.parameters, config); - } + static getAllWithEvent(year, event = "Photo%20Tour", page = 1, config = {}) { + return this.getAll( + `https://api.flickr.com/services/rest?method=flickr.photos.search&api_key=${api_key}&user_id=${user_id}&tags=${TAG_EVENT}$${event},${TAG_ALBUM}$${year}&tag_mode=all&page=${page}` + + PhotoService.parameters, + config, + ); + } - static getAllWithTeam(year, team, page = 1, config = {}) { - return this.getAll(`https://api.flickr.com/services/rest?method=flickr.photos.search&api_key=${api_key}&user_id=${user_id}&tags=${TAG_ALBUM}$${year},${TAG_TEAM}$${team}&tag_mode=all&page=${page}` + PhotoService.parameters, config); - } + static getAllWithTeam(year, team, page = 1, config = {}) { + return this.getAll( + `https://api.flickr.com/services/rest?method=flickr.photos.search&api_key=${api_key}&user_id=${user_id}&tags=${TAG_ALBUM}$${year},${TAG_TEAM}$${team}&tag_mode=all&page=${page}` + + PhotoService.parameters, + config, + ); + } - static getAllWithPerson(year, person, page = 1, config = {}) { - return PhotoService.getAll(`https://api.flickr.com/services/rest?method=flickr.photos.search&api_key=${api_key}&user_id=${user_id}&tags=${TAG_ALBUM}$${year}&tag_mode=all&page=${page}&text=${person}` + PhotoService.parameters, config); - } + static getAllWithPerson(year, person, page = 1, config = {}) { + return PhotoService.getAll( + `https://api.flickr.com/services/rest?method=flickr.photos.search&api_key=${api_key}&user_id=${user_id}&tags=${TAG_ALBUM}$${year}&tag_mode=all&page=${page}&text=${person}` + + PhotoService.parameters, + config, + ); + } - static getAllWithText(text, page = 1, config = {}) { - return PhotoService.getAll(`https://api.flickr.com/services/rest?method=flickr.photos.search&api_key=${api_key}&user_id=${user_id}&page=${page}&text=${text}%20and%20${TAG_ALBUM}$` + PhotoService.parameters, config); - } + static getAllWithText(text, page = 1, config = {}) { + return PhotoService.getAll( + `https://api.flickr.com/services/rest?method=flickr.photos.search&api_key=${api_key}&user_id=${user_id}&page=${page}&text=${text}%20and%20${TAG_ALBUM}$` + + PhotoService.parameters, + config, + ); + } - static getPhotoInfo(id, config = {}) { - return PhotoService.getAll(`https://api.flickr.com/services/rest?method=flickr.photos.getInfo&api_key=${api_key}&user_id=${user_id}&format=json&nojsoncallback=?&photo_id=${id}`, config); - } + static getPhotoInfo(id, config = {}) { + return PhotoService.getAll( + `https://api.flickr.com/services/rest?method=flickr.photos.getInfo&api_key=${api_key}&user_id=${user_id}&format=json&nojsoncallback=?&photo_id=${id}`, + config, + ); + } - static async getAll(link, config = {}) { - try { - const response = await axios.get(link, config); - return response; - } catch (e) { - console.log(e); - } + static async getAll(link, config = {}) { + try { + const response = await axios.get(link, config); + return response; + } catch (e) { + console.log(e); } + } } diff --git a/src/Util/UniqueList.js b/src/Util/UniqueList.js index d4856be..b2d86cb 100644 --- a/src/Util/UniqueList.js +++ b/src/Util/UniqueList.js @@ -1,8 +1,8 @@ - const UniqueList = (list, key = (v) => v) => { - const onlyUnique = (value, index, self) => self.findIndex(photo => key(photo) === key(value)) === index; + const onlyUnique = (value, index, self) => + self.findIndex((photo) => key(photo) === key(value)) === index; - return list.filter(onlyUnique); + return list.filter(onlyUnique); }; export default UniqueList; diff --git a/src/components/AppContext.jsx b/src/components/AppContext.jsx index 4506e36..15cb1e4 100644 --- a/src/components/AppContext.jsx +++ b/src/components/AppContext.jsx @@ -24,226 +24,231 @@ import { getEventData, getPeopleData, getTeamData } from "../Util/DataLoader"; * @type {DefaultContext} */ const defaultContext = { - year: LAST_YEAR, - event: null, - text: null, - person: null, - team: null, - fullscreenPhotoId: null, - slideShow: false, + year: LAST_YEAR, + event: null, + text: null, + person: null, + team: null, + fullscreenPhotoId: null, + slideShow: false, }; const AppContext = createContext(null); function parseSearchParams(searchParams) { - let searchParamsData = {}; - if (searchParams.has("photo")) { - searchParamsData.fullscreenPhotoId = searchParams.get("photo"); + let searchParamsData = {}; + if (searchParams.has("photo")) { + searchParamsData.fullscreenPhotoId = searchParams.get("photo"); + } + if (searchParams.has("slideshow")) { + searchParamsData.slideShow = searchParams.get("slideshow"); + } + if (searchParams.has("query")) { + searchParamsData.text = decodeURIComponent(searchParams.get("query")); + searchParamsData.year = null; + } else { + if (searchParams.has("album")) { + searchParamsData.year = decodeURIComponent(searchParams.get("album")); } - if (searchParams.has("slideshow")) { - searchParamsData.slideShow = searchParams.get("slideshow"); - } - if (searchParams.has("query")) { - searchParamsData.text = decodeURIComponent(searchParams.get("query")); - searchParamsData.year = null; + if (searchParams.has("event")) { + searchParamsData.event = decodeURIComponent(searchParams.get("event")); + } else if (searchParams.has("team")) { + searchParamsData.team = decodeURIComponent(searchParams.get("team")); + } else if (searchParams.has("person")) { + searchParamsData.person = decodeURIComponent(searchParams.get("person")); } else { - if (searchParams.has("album")) { - searchParamsData.year = decodeURIComponent(searchParams.get("album")); - } - if (searchParams.has("event")) { - searchParamsData.event = decodeURIComponent(searchParams.get("event")); - } else if (searchParams.has("team")) { - searchParamsData.team = decodeURIComponent(searchParams.get("team")); - } else if (searchParams.has("person")) { - searchParamsData.person = decodeURIComponent(searchParams.get("person")); - } else { - searchParamsData.event = DEFAULT_EVENT; - } + searchParamsData.event = DEFAULT_EVENT; } - return searchParamsData; + } + return searchParamsData; } -function serializeSearchParams({ year, event, text, person, team, fullscreenPhotoId, slideShow }) { - let searchParams = {}; - if (year != null) { - searchParams.album = year; - } - if (event != null) { - searchParams.event = event; - } - if (person != null) { - searchParams.person = person; - } - if (team != null) { - searchParams.team = team; - } - if (text != null) { - searchParams.query = text; - } - if (fullscreenPhotoId != null) { - searchParams.photo = fullscreenPhotoId; - } - if (slideShow) { - searchParams.slideshow = true; - } - return searchParams; +function serializeSearchParams({ + year, + event, + text, + person, + team, + fullscreenPhotoId, + slideShow, +}) { + let searchParams = {}; + if (year != null) { + searchParams.album = year; + } + if (event != null) { + searchParams.event = event; + } + if (person != null) { + searchParams.person = person; + } + if (team != null) { + searchParams.team = team; + } + if (text != null) { + searchParams.query = text; + } + if (fullscreenPhotoId != null) { + searchParams.photo = fullscreenPhotoId; + } + if (slideShow) { + searchParams.slideshow = true; + } + return searchParams; } function attachYearIfNull(data) { - if (!data.year) { - data.year = LAST_YEAR; - } - return data; + if (!data.year) { + data.year = LAST_YEAR; + } + return data; } - const AppContextProvider = ({ children }) => { - const [searchParams, setSearchParams] = useSearchParams(); + const [searchParams, setSearchParams] = useSearchParams(); - /** - * The state object and its setter function for AppContext. - * @typedef {Object} AppState - * @property {DefaultContext} data. - * @property {function} setData - Setter function for the data object. Try not to use this directly. - */ - const [data, setData] = useState({ - ...defaultContext, - ...parseSearchParams(searchParams), - }); + /** + * The state object and its setter function for AppContext. + * @typedef {Object} AppState + * @property {DefaultContext} data. + * @property {function} setData - Setter function for the data object. Try not to use this directly. + */ + const [data, setData] = useState({ + ...defaultContext, + ...parseSearchParams(searchParams), + }); - const desktop = useMediaQuery("(min-width: 900px)"); - const mobile = !desktop; + const desktop = useMediaQuery("(min-width: 900px)"); + const mobile = !desktop; - const [isOpenMenu, setIsOpenMenu] = useState(desktop); + const [isOpenMenu, setIsOpenMenu] = useState(desktop); - const isSlideShow = data.slideShow; + const isSlideShow = data.slideShow; - useEffect(() => { - if (desktop) { - setIsOpenMenu(true); - } else { - setIsOpenMenu(false); - } - }, [desktop]); + useEffect(() => { + if (desktop) { + setIsOpenMenu(true); + } else { + setIsOpenMenu(false); + } + }, [desktop]); - useEffect(() => { - setSearchParams( - serializeSearchParams(data)); - }, [data, setSearchParams]); + useEffect(() => { + setSearchParams(serializeSearchParams(data)); + }, [data, setSearchParams]); - /** - * Sets the year. - * This function removes all other data from the context. - */ - const setYear = (newYear) => { - setData({ - ...defaultContext, - year: newYear, - event: DEFAULT_EVENT, - }); - }; + /** + * Sets the year. + * This function removes all other data from the context. + */ + const setYear = (newYear) => { + setData({ + ...defaultContext, + year: newYear, + event: DEFAULT_EVENT, + }); + }; - const setText = (newText) => { - setData(prevState => { - return { - ...prevState, - year: null, - event: null, - text: newText, - person: null, - team: null, - }; - }); - }; + const setText = (newText) => { + setData((prevState) => { + return { + ...prevState, + year: null, + event: null, + text: newText, + person: null, + team: null, + }; + }); + }; - const setEvent = (newEvent) => { - setData(prevState => { - return attachYearIfNull({ - ...prevState, - event: newEvent, - text: null, - person: null, - team: null, - }); - }); - }; + const setEvent = (newEvent) => { + setData((prevState) => { + return attachYearIfNull({ + ...prevState, + event: newEvent, + text: null, + person: null, + team: null, + }); + }); + }; - const setPerson = (newPerson) => { - setData(prevState => { - return attachYearIfNull({ - ...prevState, - event: null, - text: null, - person: newPerson, - team: null, - }); - }); - }; + const setPerson = (newPerson) => { + setData((prevState) => { + return attachYearIfNull({ + ...prevState, + event: null, + text: null, + person: newPerson, + team: null, + }); + }); + }; - const setTeam = (newTeam) => { - setData(prevState => { - return attachYearIfNull({ - ...prevState, - event: null, - text: null, - person: null, - team: newTeam, - }); - }); - }; + const setTeam = (newTeam) => { + setData((prevState) => { + return attachYearIfNull({ + ...prevState, + event: null, + text: null, + person: null, + team: newTeam, + }); + }); + }; - const setFullscreenPhotoId = (newFullscreenPhotoId) => { - setData(prevState => { - return { - ...prevState, - fullscreenPhotoId: newFullscreenPhotoId, - }; - }); - }; + const setFullscreenPhotoId = (newFullscreenPhotoId) => { + setData((prevState) => { + return { + ...prevState, + fullscreenPhotoId: newFullscreenPhotoId, + }; + }); + }; - const setIsSlideShow = (newIsSlideShow) => { - setData(prevState => { - return { - ...prevState, - slideShow: newIsSlideShow, - }; - }); - }; + const setIsSlideShow = (newIsSlideShow) => { + setData((prevState) => { + return { + ...prevState, + slideShow: newIsSlideShow, + }; + }); + }; - const [events, setEvents] = useState([]); - const [people, setPeople] = useState([]); - const [teams, setTeams] = useState([]); + const [events, setEvents] = useState([]); + const [people, setPeople] = useState([]); + const [teams, setTeams] = useState([]); - useEffect(() => { - let isCancelled = false; + useEffect(() => { + let isCancelled = false; - getEventData(data.year) - .then(eventsData => { - if (!isCancelled) { - setEvents(eventsData); - } - }); + getEventData(data.year).then((eventsData) => { + if (!isCancelled) { + setEvents(eventsData); + } + }); - getPeopleData(data.year) - .then(peopleData => { - if (!isCancelled) { - setPeople(peopleData); - } - }); + getPeopleData(data.year).then((peopleData) => { + if (!isCancelled) { + setPeople(peopleData); + } + }); - getTeamData(data.year) - .then(teamData => { - if (!isCancelled) { - setTeams(teamData); - } - }); + getTeamData(data.year).then((teamData) => { + if (!isCancelled) { + setTeams(teamData); + } + }); - return () => { - isCancelled = true; - }; - }, [data.year]); + return () => { + isCancelled = true; + }; + }, [data.year]); - return ( { setIsOpenMenu, desktop, mobile, - events, + events, people, - teams - }}> - {children} - ); + teams, + }} + > + {children} + + ); }; /** @@ -288,11 +295,11 @@ const AppContextProvider = ({ children }) => { * }}. */ const useAppContext = () => { - const context = useContext(AppContext); - if (context === undefined || context === null) { - throw new Error("useAppContext must be called within AppContextProvider"); - } - return context; + const context = useContext(AppContext); + if (context === undefined || context === null) { + throw new Error("useAppContext must be called within AppContextProvider"); + } + return context; }; export { AppContextProvider, useAppContext }; diff --git a/src/components/Body/Body.jsx b/src/components/Body/Body.jsx index b80f914..b6d99cf 100644 --- a/src/components/Body/Body.jsx +++ b/src/components/Body/Body.jsx @@ -13,122 +13,133 @@ import PhotoGridByYear from "./PhotoGridByYear"; import "../../styles/Body.css"; const Body = () => { - const { data, setFullscreenPhotoId, setIsSlideShow, desktop } = useAppContext(); - const scrollRef = useRef(null); - - const { hasMorePhotos, loadMorePhotos, photosByEvent, photosList } = usePhotoLoader(); - - const [fullscreenPhoto, setFullscreenPhoto] = useState(null); - const [fullscreenIndex, setFullscreenIndex] = useState(null); - const [rightArrow, setRightArrow] = useState(null); - const [leftArrow, setLeftArrow] = useState(null); - - const handleClick = (id) => { - setFullscreenPhotoId(id); - }; - - useEffect(() => { - const id = data.fullscreenPhotoId; - if (id != null) { - const index = photosList.findIndex(photo => photo.id === id); - - if (index === -1 && hasMorePhotos()) { - loadMorePhotos(); - return; + const { data, setFullscreenPhotoId, setIsSlideShow, desktop } = + useAppContext(); + const scrollRef = useRef(null); + + const { hasMorePhotos, loadMorePhotos, photosByEvent, photosList } = + usePhotoLoader(); + + const [fullscreenPhoto, setFullscreenPhoto] = useState(null); + const [fullscreenIndex, setFullscreenIndex] = useState(null); + const [rightArrow, setRightArrow] = useState(null); + const [leftArrow, setLeftArrow] = useState(null); + + const handleClick = (id) => { + setFullscreenPhotoId(id); + }; + + useEffect(() => { + const id = data.fullscreenPhotoId; + if (id != null) { + const index = photosList.findIndex((photo) => photo.id === id); + + if (index === -1 && hasMorePhotos()) { + loadMorePhotos(); + return; + } + + const photo = photosList[index]; + setFullscreenIndex(index); + setFullscreenPhoto(photo); + + setLeftArrow(index !== 0); + setRightArrow(index + 1 < photosList.length || hasMorePhotos()); + + if (photosList.length <= index + 4 && hasMorePhotos()) { + loadMorePhotos(); + } + } else { + setFullscreenIndex(null); + setFullscreenPhoto(null); + setLeftArrow(false); + setRightArrow(false); + } + }, [data.fullscreenPhotoId, photosList, hasMorePhotos, loadMorePhotos]); + + useEffect(() => { + if (data.fullscreenPhotoId === null) { + setIsSlideShow(false); + } + }, [data.fullscreenPhotoId]); + + const handleRotationRight = () => { + let newIndex = fullscreenIndex + 1; + if (newIndex >= photosList.length) { + newIndex = 0; + } + handleClick(photosList[newIndex].id); + }; + + const handleRotationLeft = () => { + handleClick(photosList[fullscreenIndex - 1].id); + }; + + useEffect(() => { + const target = document.getElementsByTagName("body")[0]; + const listener = (e) => { + if (fullscreenPhoto) { + switch (e.key) { + case "ArrowLeft": + if (leftArrow) { + handleRotationLeft(); } - - const photo = photosList[index]; - setFullscreenIndex(index); - setFullscreenPhoto(photo); - - setLeftArrow(index !== 0); - setRightArrow(index + 1 < photosList.length || hasMorePhotos()); - - if (photosList.length <= index + 4 && hasMorePhotos()) { - loadMorePhotos(); + break; + case "ArrowRight": + if (rightArrow) { + handleRotationRight(); } - } else { - setFullscreenIndex(null); - setFullscreenPhoto(null); - setLeftArrow(false); - setRightArrow(false); + break; + case "Escape": + setFullscreenPhotoId(null); + break; + default: + break; } - }, [data.fullscreenPhotoId, photosList, hasMorePhotos, loadMorePhotos]); - - useEffect(() => { - if (data.fullscreenPhotoId === null) { - setIsSlideShow(false); - } - }, [data.fullscreenPhotoId]); - - const handleRotationRight = () => { - let newIndex = fullscreenIndex + 1; - if (newIndex >= photosList.length) { - newIndex = 0; - } - handleClick(photosList[newIndex].id); + } }; - - const handleRotationLeft = () => { - handleClick(photosList[fullscreenIndex - 1].id); + target.onkeydown = listener; + return () => { + target.removeEventListener("onkeydown", listener); }; - - useEffect(() => { - const target = document.getElementsByTagName("body")[0]; - const listener = (e) => { - if (fullscreenPhoto) { - switch (e.key) { - case "ArrowLeft": - if (leftArrow) { - handleRotationLeft(); - } - break; - case "ArrowRight": - if (rightArrow) { - handleRotationRight(); - } - break; - case "Escape": - setFullscreenPhotoId(null); - break; - default: - break; - } - } - }; - target.onkeydown = listener; - return () => { - target.removeEventListener("onkeydown", listener); - }; - }, [fullscreenPhoto]); - - return ( -
- {desktop && data.text &&

{data.text}

} - Loading ...} - useWindow={false} - getScrollParent={() => scrollRef.current} - > - {Array.from(photosByEvent).map(([event, photos]) => - - {event && {event}} - - - )} - - {!hasMorePhotos() && photosList.length === 0 && No photo} - {fullscreenPhoto != null && } -
- ); + }, [fullscreenPhoto]); + + return ( +
+ {desktop && data.text &&

{data.text}

} + + Loading ... + + } + useWindow={false} + getScrollParent={() => scrollRef.current} + > + {Array.from(photosByEvent).map(([event, photos]) => ( + + {event && {event}} + + + ))} + + {!hasMorePhotos() && photosList.length === 0 && ( + No photo + )} + {fullscreenPhoto != null && ( + + )} +
+ ); }; export default Body; diff --git a/src/components/Body/Control.jsx b/src/components/Body/Control.jsx index 8ae9cd6..811bb3a 100644 --- a/src/components/Body/Control.jsx +++ b/src/components/Body/Control.jsx @@ -6,23 +6,29 @@ import { IconButton } from "@mui/material"; import { useAppContext } from "../AppContext"; const Control = () => { - const { setFullscreenPhotoId, isSlideShow, setIsSlideShow } = useAppContext(); + const { setFullscreenPhotoId, isSlideShow, setIsSlideShow } = useAppContext(); - const slideShow = () => { - setIsSlideShow(!isSlideShow); - }; + const slideShow = () => { + setIsSlideShow(!isSlideShow); + }; - return ( -
- slideShow()}> - {isSlideShow ? : - } - - setFullscreenPhotoId(null)}> - - -
- ); + return ( +
+ slideShow()}> + {isSlideShow ? ( + + ) : ( + + )} + + setFullscreenPhotoId(null)} + > + + +
+ ); }; export default Control; diff --git a/src/components/Body/FaceDiv.jsx b/src/components/Body/FaceDiv.jsx index bfd2c9b..2dcd6d9 100644 --- a/src/components/Body/FaceDiv.jsx +++ b/src/components/Body/FaceDiv.jsx @@ -4,10 +4,10 @@ import "../../styles/theme-variables.css"; import "../../styles/FaceDiv.css"; const Rectangle = styled.fieldset` - top: ${props => props.top * 100 + "%"}; - bottom: ${props => (100 - props.bottom * 100 - 3) + "%"}; - left: ${props => props.left * 100 + "%"}; - right: ${props => (100 - props.right * 100) + "%"}; + top: ${(props) => props.top * 100 + "%"}; + bottom: ${(props) => 100 - props.bottom * 100 - 3 + "%"}; + left: ${(props) => props.left * 100 + "%"}; + right: ${(props) => 100 - props.right * 100 + "%"}; position: absolute; color: var(--colorRectangle); padding: 3px 6px; @@ -15,21 +15,20 @@ const Rectangle = styled.fieldset` transform: rotatex(180deg); `; - const FaceDiv = ({ person, hidden, setFace }) => { - return ( - setFace(null)} - onMouseEnter={() => setFace(person)} - > - {person.name} - - ); + return ( + setFace(null)} + onMouseEnter={() => setFace(person)} + > + {person.name} + + ); }; export default FaceDiv; diff --git a/src/components/Body/ImageWithFaceSelection.jsx b/src/components/Body/ImageWithFaceSelection.jsx index a976edc..881f58f 100644 --- a/src/components/Body/ImageWithFaceSelection.jsx +++ b/src/components/Body/ImageWithFaceSelection.jsx @@ -1,6 +1,12 @@ import { useEffect, useState } from "react"; import ReactCrop from "react-image-crop"; -import { Autocomplete, Box, createFilterOptions, Paper, TextField } from "@mui/material"; +import { + Autocomplete, + Box, + createFilterOptions, + Paper, + TextField, +} from "@mui/material"; import { getPeopleData } from "../../Util/DataLoader"; import { useAppContext } from "../AppContext"; @@ -12,103 +18,94 @@ import "../../styles/Body.css"; import "react-image-crop/dist/ReactCrop.css"; const ImageWithFaceSelection = ({ photo, alt = "", face, setFace }) => { - const { data } = useAppContext(); - const { photoInfo, editMode, appendPerson } = usePhotoInfo(); - const [crop, setCrop] = useState(); + const { data } = useAppContext(); + const { photoInfo, editMode, appendPerson } = usePhotoInfo(); + const [crop, setCrop] = useState(); - const [people, setPeople] = useState([]); + const [people, setPeople] = useState([]); - useEffect(() => { - let isCancelled = false; + useEffect(() => { + let isCancelled = false; - getPeopleData(data.year) - .then(peopleData => { - if (!isCancelled) { - setPeople(peopleData); - } - }); - return () => { - isCancelled = true; - }; - }, [data.year]); + getPeopleData(data.year).then((peopleData) => { + if (!isCancelled) { + setPeople(peopleData); + } + }); + return () => { + isCancelled = true; + }; + }, [data.year]); - function calculateImageSize(naturalWidth, naturalHeight) { - const screenWidth = window.innerWidth; - const screenHeight = window.innerHeight; - const ratio = Math.min(screenWidth / naturalWidth, screenHeight / naturalHeight); - return { - width: naturalWidth * ratio, - height: naturalHeight * ratio - }; - } + function calculateImageSize(naturalWidth, naturalHeight) { + const screenWidth = window.innerWidth; + const screenHeight = window.innerHeight; + const ratio = Math.min( + screenWidth / naturalWidth, + screenHeight / naturalHeight, + ); + return { + width: naturalWidth * ratio, + height: naturalHeight * ratio, + }; + } - const { width, height } = calculateImageSize(photo.width, photo.height); + const { width, height } = calculateImageSize(photo.width, photo.height); - function createPerson(name, crop) { - if (name) { - const person = { - name: name, - position: { - top: crop.y / height, - left: crop.x / width, - right: (crop.x + crop.width) / width, - bottom: (crop.y + crop.height) / height, - } - }; - appendPerson(person); - setCrop(null); - } + function createPerson(name, crop) { + if (name) { + const person = { + name: name, + position: { + top: crop.y / height, + left: crop.x / width, + right: (crop.x + crop.width) / width, + bottom: (crop.y + crop.height) / height, + }, + }; + appendPerson(person); + setCrop(null); } + } + const filterOptions = createFilterOptions({ limit: 200 }); - const filterOptions = createFilterOptions({ limit: 200 }); - - return ( - - setCrop(c)} - disabled={!editMode}> - {alt} - {photoInfo?.person?.map(person => ( - - {crop && crop?.height !== 0 && - - ( - - )} - onChange={(event, newValue) => createPerson(newValue, crop)} - /> - - } - - ); + return ( + + setCrop(c)} disabled={!editMode}> + {alt} + {photoInfo?.person?.map((person) => ( + + {crop && crop?.height !== 0 && ( + + ( + + )} + onChange={(event, newValue) => createPerson(newValue, crop)} + /> + + )} + + ); }; export default ImageWithFaceSelection; diff --git a/src/components/Body/Lightbox.jsx b/src/components/Body/Lightbox.jsx index 39220a6..bb107d7 100644 --- a/src/components/Body/Lightbox.jsx +++ b/src/components/Body/Lightbox.jsx @@ -9,31 +9,41 @@ import ImageWithFaceSelection from "./ImageWithFaceSelection"; import "../../styles/Body.css"; const Lightbox = ({ - photo, - handleRotationLeft, - handleRotationRight, - leftArrow, - rightArrow + photo, + handleRotationLeft, + handleRotationRight, + leftArrow, + rightArrow, }) => { - const { editMode, face, setFace } = usePhotoInfo(); + const { editMode, face, setFace } = usePhotoInfo(); - return ( -
- - {!editMode && } - - {leftArrow && !editMode &&
- - - -
} - {rightArrow && !editMode &&
- - - -
} + return ( +
+ + {!editMode && } + + {leftArrow && !editMode && ( +
+ + +
- ); + )} + {rightArrow && !editMode && ( +
+ + + +
+ )} +
+ ); }; export default Lightbox; diff --git a/src/components/Body/MyModal.jsx b/src/components/Body/MyModal.jsx index 49794e6..fb39d2e 100644 --- a/src/components/Body/MyModal.jsx +++ b/src/components/Body/MyModal.jsx @@ -6,28 +6,39 @@ import Slideshow from "./Slideshow"; import "../../styles/Body.css"; const MyModal = ({ - photo, - handleRotationLeft, - handleRotationRight, - leftArrow, - rightArrow + photo, + handleRotationLeft, + handleRotationRight, + leftArrow, + rightArrow, }) => { - const { setFullscreenPhotoId, isSlideShow } = useAppContext(); + const { setFullscreenPhotoId, isSlideShow } = useAppContext(); - const handleClick = (e) => { - if (e.target.classList.contains("dismiss")) { - setFullscreenPhotoId(null); - } - }; + const handleClick = (e) => { + if (e.target.classList.contains("dismiss")) { + setFullscreenPhotoId(null); + } + }; - return ( -
- {isSlideShow - ? - : } -
- ); + return ( +
+ {isSlideShow ? ( + + ) : ( + + )} +
+ ); }; export default MyModal; diff --git a/src/components/Body/PhotoGrid.jsx b/src/components/Body/PhotoGrid.jsx index bd07587..85e12ea 100644 --- a/src/components/Body/PhotoGrid.jsx +++ b/src/components/Body/PhotoGrid.jsx @@ -1,18 +1,18 @@ export default function PhotoGrid({ photos, handleClick }) { - return ( -
- {photos.map((photo) => { - return ( -
- {photo.url_preview} handleClick(photo.id)} - /> -
- ); - })} -
- ); + return ( +
+ {photos.map((photo) => { + return ( +
+ {photo.url_preview} handleClick(photo.id)} + /> +
+ ); + })} +
+ ); } diff --git a/src/components/Body/PhotoGridByYear.jsx b/src/components/Body/PhotoGridByYear.jsx index 15f9133..10d286d 100644 --- a/src/components/Body/PhotoGridByYear.jsx +++ b/src/components/Body/PhotoGridByYear.jsx @@ -5,44 +5,46 @@ import { places } from "../../consts"; import PhotoGrid from "./PhotoGrid"; const PhotoGridByYear = ({ photos, handleClick }) => { - const photosByYear = useMemo(() => { - const photosByYear = {}; - photos.forEach((photo) => { - if (!photosByYear[photo.year]) { - photosByYear[photo.year] = []; - } - photosByYear[photo.year].push(photo); - }); - return photosByYear; - }, [photos]); - - - if (Object.keys(photosByYear).length < 2) { - return ; + const photosByYear = useMemo(() => { + const photosByYear = {}; + photos.forEach((photo) => { + if (!photosByYear[photo.year]) { + photosByYear[photo.year] = []; + } + photosByYear[photo.year].push(photo); + }); + return photosByYear; + }, [photos]); + + if (Object.keys(photosByYear).length < 2) { + return ; + } + + const compareYears = (a, b) => { + const yearA = a[0]; + const yearB = b[0]; + const yearIndex = (targetYear) => + places.findIndex(({ year }) => year === targetYear); + const indexDiff = yearIndex(yearB) - yearIndex(yearA); + if (indexDiff !== 0) { + return indexDiff; } - - - const compareYears = (a, b) => { - const yearA = a[0]; - const yearB = b[0]; - const yearIndex = (targetYear) => places.findIndex(({ year }) => year === targetYear); - const indexDiff = yearIndex(yearB) - yearIndex(yearA); - if (indexDiff !== 0) { - return indexDiff; - } - return yearA.localeCompare(yearB); - }; - - return ( -
- {Object.entries(photosByYear).sort(compareYears).reverse().map(([year, photos]) => ( -
-

{year}

- -
- ))} -
- ); + return yearA.localeCompare(yearB); + }; + + return ( +
+ {Object.entries(photosByYear) + .sort(compareYears) + .reverse() + .map(([year, photos]) => ( +
+

{year}

+ +
+ ))} +
+ ); }; export default PhotoGridByYear; diff --git a/src/components/Body/PhotoInfo/PhotoInfoContext.jsx b/src/components/Body/PhotoInfo/PhotoInfoContext.jsx index d422676..3a7be9a 100644 --- a/src/components/Body/PhotoInfo/PhotoInfoContext.jsx +++ b/src/components/Body/PhotoInfo/PhotoInfoContext.jsx @@ -7,77 +7,77 @@ import { useAppContext } from "../../AppContext"; const PhotoInfoContext = createContext(null); const PhotoInfoProvider = ({ children }) => { - const { data } = useAppContext(); + const { data } = useAppContext(); - const [photoInfo, setPhotoInfo] = useState(null); - const [editMode, setEditMode] = useState(false); - const [face, setFace] = useState(null); + const [photoInfo, setPhotoInfo] = useState(null); + const [editMode, setEditMode] = useState(false); + const [face, setFace] = useState(null); - useEffect(() => { - const fullscreenPhotoId = data.fullscreenPhotoId; - if (fullscreenPhotoId === null) { - return; - } - PhotoService.getPhotoInfo(fullscreenPhotoId) - .then(response => { - const tags = response.data?.photo?.tags?.tag?.map(tag => tag.raw); - const description = response.data?.photo?.description._content; - const newPhotoInfo = ParsePhotoInfo(tags, description); - setPhotoInfo(newPhotoInfo); - } - ); - }, [data.fullscreenPhotoId]); - - function setEvents(newEvents) { - setPhotoInfo({ ...photoInfo, events: newEvents }); + useEffect(() => { + const fullscreenPhotoId = data.fullscreenPhotoId; + if (fullscreenPhotoId === null) { + return; } + PhotoService.getPhotoInfo(fullscreenPhotoId).then((response) => { + const tags = response.data?.photo?.tags?.tag?.map((tag) => tag.raw); + const description = response.data?.photo?.description._content; + const newPhotoInfo = ParsePhotoInfo(tags, description); + setPhotoInfo(newPhotoInfo); + }); + }, [data.fullscreenPhotoId]); - function setPerson(newPerson) { - setPhotoInfo({ ...photoInfo, person: newPerson }); - } + function setEvents(newEvents) { + setPhotoInfo({ ...photoInfo, events: newEvents }); + } - function appendPerson(newPerson) { - setPerson([...photoInfo.person, newPerson]); - } + function setPerson(newPerson) { + setPhotoInfo({ ...photoInfo, person: newPerson }); + } - function setAlbum(newAlbum) { - setPhotoInfo({ ...photoInfo, album: newAlbum }); - } + function appendPerson(newPerson) { + setPerson([...photoInfo.person, newPerson]); + } - function setPhotographer(newPhotographer) { - setPhotoInfo({ ...photoInfo, photographer: newPhotographer }); - } + function setAlbum(newAlbum) { + setPhotoInfo({ ...photoInfo, album: newAlbum }); + } - function setTeam(newTeam) { - setPhotoInfo({ ...photoInfo, team: newTeam }); - } + function setPhotographer(newPhotographer) { + setPhotoInfo({ ...photoInfo, photographer: newPhotographer }); + } - return ( - - {children} - - ); + function setTeam(newTeam) { + setPhotoInfo({ ...photoInfo, team: newTeam }); + } + + return ( + + {children} + + ); }; const usePhotoInfo = () => { - const context = useContext(PhotoInfoContext); - if (context === undefined || context === null) { - throw new Error("usePhotoInfo must be called within PhotoInfoProvider"); - } - return context; + const context = useContext(PhotoInfoContext); + if (context === undefined || context === null) { + throw new Error("usePhotoInfo must be called within PhotoInfoProvider"); + } + return context; }; export { PhotoInfoProvider, usePhotoInfo }; diff --git a/src/components/Body/PhotoInfo/PhotoInfoDetails.jsx b/src/components/Body/PhotoInfo/PhotoInfoDetails.jsx index 1e06d2b..56caa0b 100644 --- a/src/components/Body/PhotoInfo/PhotoInfoDetails.jsx +++ b/src/components/Body/PhotoInfo/PhotoInfoDetails.jsx @@ -5,261 +5,241 @@ import { useAppContext } from "../../AppContext"; import { usePhotoInfo } from "./PhotoInfoContext"; - const ComaSeparated = (list) => { - return list.map((item, index) => [ - index > 0 && ", ", - item - ]); + return list.map((item, index) => [index > 0 && ", ", item]); }; // TODO: Unify const PhotographerInfo = () => { - const { photoInfo } = usePhotoInfo(); - - if (!photoInfo || photoInfo.photographer.length === 0) { - return null; - } - - const photographer = photoInfo.photographer.map(photographer => ( - - {photographer} - - )); - - return ( -
- Photographer: {ComaSeparated(photographer)} -
- ); + const { photoInfo } = usePhotoInfo(); + + if (!photoInfo || photoInfo.photographer.length === 0) { + return null; + } + + const photographer = photoInfo.photographer.map((photographer) => ( + {photographer} + )); + + return
Photographer: {ComaSeparated(photographer)}
; }; const PhotographerEdit = () => { - const { photoInfo, setPhotographer } = usePhotoInfo(); - - if (!photoInfo) { - return null; - } - - return ( - { - setPhotographer(newValue); - }} - renderInput={(params) => ( - - )} - /> - ); + const { photoInfo, setPhotographer } = usePhotoInfo(); + + if (!photoInfo) { + return null; + } + + return ( + { + setPhotographer(newValue); + }} + renderInput={(params) => ( + + )} + /> + ); }; const AlbumInfo = () => { - const { photoInfo } = usePhotoInfo(); + const { photoInfo } = usePhotoInfo(); - if (!photoInfo || photoInfo.album.length === 0) { - return null; - } + if (!photoInfo || photoInfo.album.length === 0) { + return null; + } - const formatLink = (album) => `?album=${album}`.replaceAll(" ", "+"); + const formatLink = (album) => `?album=${album}`.replaceAll(" ", "+"); - const albumLinks = photoInfo.album.map(album => - - {album} - ); + const albumLinks = photoInfo.album.map((album) => ( + + {album} + + )); - return ( -
- Album: {ComaSeparated(albumLinks)} -
- ); + return
Album: {ComaSeparated(albumLinks)}
; }; const AlbumEdit = () => { - const { photoInfo, setAlbum } = usePhotoInfo(); - - if (!photoInfo) { - return null; - } - - return ( - year)} - value={photoInfo.album} - onChange={(event, newValue) => { - setAlbum(newValue); - }} - renderInput={(params) => ( - - )} - /> - ); + const { photoInfo, setAlbum } = usePhotoInfo(); + + if (!photoInfo) { + return null; + } + + return ( + year)} + value={photoInfo.album} + onChange={(event, newValue) => { + setAlbum(newValue); + }} + renderInput={(params) => ( + + )} + /> + ); }; const EventInfo = () => { - const { photoInfo } = usePhotoInfo(); + const { photoInfo } = usePhotoInfo(); - if (!photoInfo || photoInfo.event.length === 0) { - return null; - } + if (!photoInfo || photoInfo.event.length === 0) { + return null; + } - const formatLink = (event) => `?album=${photoInfo.album[0]}&event=${event}`.replaceAll(" ", "+"); + const formatLink = (event) => + `?album=${photoInfo.album[0]}&event=${event}`.replaceAll(" ", "+"); - const eventLinks = photoInfo.event.map(event => - - {event} - ); + const eventLinks = photoInfo.event.map((event) => ( + + {event} + + )); - return ( -
- Event: {ComaSeparated(eventLinks)} -
- ); + return
Event: {ComaSeparated(eventLinks)}
; }; const EventEdit = () => { - const { photoInfo, setEvent } = usePhotoInfo(); - const { events } = useAppContext(); - - if (!photoInfo) { - return null; - } - - return ( - { - setEvent(newValue); - }} - renderInput={(params) => ( - - )} - /> - ); + const { photoInfo, setEvent } = usePhotoInfo(); + const { events } = useAppContext(); + + if (!photoInfo) { + return null; + } + + return ( + { + setEvent(newValue); + }} + renderInput={(params) => ( + + )} + /> + ); }; const TeamInfo = () => { - const { photoInfo } = usePhotoInfo(); + const { photoInfo } = usePhotoInfo(); - if (!photoInfo || photoInfo.team.length === 0) { - return null; - } + if (!photoInfo || photoInfo.team.length === 0) { + return null; + } - const formatLink = (team) => `?album=${photoInfo.album[0]}&team=${team}`.replaceAll(" ", "+"); + const formatLink = (team) => + `?album=${photoInfo.album[0]}&team=${team}`.replaceAll(" ", "+"); - const teamLinks = photoInfo.team.map(team => - - {team} - ); + const teamLinks = photoInfo.team.map((team) => ( + + {team} + + )); - return ( -
- Team: {ComaSeparated(teamLinks)} -
- ); + return
Team: {ComaSeparated(teamLinks)}
; }; const TeamEdit = () => { - const { photoInfo, setTeam } = usePhotoInfo(); - const { teams } = useAppContext(); - - if (!photoInfo) { - return null; - } - - return ( - { - setTeam(newValue); - }} - renderInput={(params) => ( - - )} - /> - ); + const { photoInfo, setTeam } = usePhotoInfo(); + const { teams } = useAppContext(); + + if (!photoInfo) { + return null; + } + + return ( + { + setTeam(newValue); + }} + renderInput={(params) => ( + + )} + /> + ); }; const PersonInfo = ({ setFace }) => { - const { photoInfo } = usePhotoInfo(); - - if (!photoInfo || photoInfo.person.length === 0) { - return null; - } - - const sortedPersons = photoInfo.person.sort((a, b) => a.position.left - b.position.left); - - const formatLink = (person) => `?album=${photoInfo.album[0]}&person=${person.name}`.replaceAll(" ", "+"); - - const personLinks = sortedPersons.map(person => - setFace(null)} - onMouseEnter={() => setFace(person)}> - {person.name} - ); - - - return ( -
- Person: {ComaSeparated(personLinks)} -
- ); + const { photoInfo } = usePhotoInfo(); + + if (!photoInfo || photoInfo.person.length === 0) { + return null; + } + + const sortedPersons = photoInfo.person.sort( + (a, b) => a.position.left - b.position.left, + ); + + const formatLink = (person) => + `?album=${photoInfo.album[0]}&person=${person.name}`.replaceAll(" ", "+"); + + const personLinks = sortedPersons.map((person) => ( + setFace(null)} + onMouseEnter={() => setFace(person)} + > + {person.name} + + )); + + return
Person: {ComaSeparated(personLinks)}
; }; const PersonEdit = () => { - const { photoInfo, setPerson } = usePhotoInfo(); - - if (!photoInfo) { - return null; - } - - return ( - person.name)} - onChange={(event, newValue) => { - setPerson(newValue); - }} - fullWidth={false} - renderInput={(params) => ( - - )} + const { photoInfo, setPerson } = usePhotoInfo(); + + if (!photoInfo) { + return null; + } + + return ( + person.name)} + onChange={(event, newValue) => { + setPerson(newValue); + }} + fullWidth={false} + renderInput={(params) => ( + - ); + )} + /> + ); }; -export { AlbumEdit, AlbumInfo, EventEdit, EventInfo, PersonEdit, PersonInfo, PhotographerEdit, PhotographerInfo, TeamEdit, TeamInfo }; +export { + AlbumEdit, + AlbumInfo, + EventEdit, + EventInfo, + PersonEdit, + PersonInfo, + PhotographerEdit, + PhotographerInfo, + TeamEdit, + TeamInfo, +}; diff --git a/src/components/Body/PhotoInfo/PhotoInfoPanel.jsx b/src/components/Body/PhotoInfo/PhotoInfoPanel.jsx index 9517664..46c4763 100644 --- a/src/components/Body/PhotoInfo/PhotoInfoPanel.jsx +++ b/src/components/Body/PhotoInfo/PhotoInfoPanel.jsx @@ -15,132 +15,165 @@ import { SerializePhotoInfo } from "../../../Util/PhotoInfoHelper"; import { useAppContext } from "../../AppContext"; import { usePhotoInfo } from "./PhotoInfoContext"; -import { AlbumEdit, AlbumInfo, EventEdit, EventInfo, PersonEdit,PersonInfo, PhotographerEdit, PhotographerInfo, TeamEdit, TeamInfo } from "./PhotoInfoDetails"; +import { + AlbumEdit, + AlbumInfo, + EventEdit, + EventInfo, + PersonEdit, + PersonInfo, + PhotographerEdit, + PhotographerInfo, + TeamEdit, + TeamInfo, +} from "./PhotoInfoDetails"; import "../../../styles/PhotoInfo.css"; const PhotoInfoPanel = ({ setFace, photo }) => { - const { desktop } = useAppContext(); - const { editMode, setEditMode, photoInfo } = usePhotoInfo(); - - const [hidden, setHidden] = useState(false); - - const toolTipsHidden = editMode; - const changesPane = editMode && desktop; - - const photoLink = FLICKR_IMAGE_PREFIX + photo.id; - const tags = SerializePhotoInfo(photoInfo).join(", "); - - function copyToClipboard() { - setEditMode(false); - navigator.clipboard.writeText(tags); - enqueueSnackbar("New tags copied to clipboard", { variant: "success", autoHideDuration: 2000 }); - } - - const emailBody = `Photo link: ${photoLink}\n\nGallery link: ${window.location.href}\n\nTags: ${tags}`; - const emailSubject = `Photo info update ${photo.id}`; - - const mailtoLink = `mailto:${SUGGESTIONS_EMAIL}?subject=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(emailBody)}`; - - const gmailLink = `https://mail.google.com/mail/u/0/?view=cm&fs=1&tf=1&to=${encodeURIComponent(SUGGESTIONS_EMAIL)}&su=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(emailBody)}`; - - function toogleHidden() { - setHidden(!hidden); - } - - return ( -
- {!hidden && !changesPane && - - - - - - } - - {changesPane && - - - - - - - - - - - - - - - - - } - -
- {editMode && - - - setEditMode(false)}> - - - - - setEditMode(false)}> - - - - - - copyToClipboard()}> - - - - - setEditMode(false)}> - - - - } - {!toolTipsHidden && - - setEditMode(true)}> - - - } - {!toolTipsHidden && - - - {hidden - ? - : } - - } - {!toolTipsHidden && - - - - - } - {!toolTipsHidden && - - - - - } -
-
- ); + const { desktop } = useAppContext(); + const { editMode, setEditMode, photoInfo } = usePhotoInfo(); + + const [hidden, setHidden] = useState(false); + + const toolTipsHidden = editMode; + const changesPane = editMode && desktop; + + const photoLink = FLICKR_IMAGE_PREFIX + photo.id; + const tags = SerializePhotoInfo(photoInfo).join(", "); + + function copyToClipboard() { + setEditMode(false); + navigator.clipboard.writeText(tags); + enqueueSnackbar("New tags copied to clipboard", { + variant: "success", + autoHideDuration: 2000, + }); + } + + const emailBody = `Photo link: ${photoLink}\n\nGallery link: ${window.location.href}\n\nTags: ${tags}`; + const emailSubject = `Photo info update ${photo.id}`; + + const mailtoLink = `mailto:${SUGGESTIONS_EMAIL}?subject=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(emailBody)}`; + + const gmailLink = `https://mail.google.com/mail/u/0/?view=cm&fs=1&tf=1&to=${encodeURIComponent(SUGGESTIONS_EMAIL)}&su=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(emailBody)}`; + + function toogleHidden() { + setHidden(!hidden); + } + + return ( +
+ {!hidden && !changesPane && ( + + + + + + + + )} + + {changesPane && ( + + + + + + + + + + + + + + + + + + )} + +
+ {editMode && ( + + + setEditMode(false)} + > + + + + + setEditMode(false)} + > + + + + + + copyToClipboard()}> + + + + + setEditMode(false)}> + + + + + )} + {!toolTipsHidden && ( + + setEditMode(true)}> + + + + )} + {!toolTipsHidden && ( + + + {hidden ? ( + + ) : ( + + )} + + + )} + {!toolTipsHidden && ( + + + + + + )} + {!toolTipsHidden && ( + + + + + + )} +
+
+ ); }; export default PhotoInfoPanel; diff --git a/src/components/Body/Slideshow.jsx b/src/components/Body/Slideshow.jsx index 2af4896..a1420fc 100644 --- a/src/components/Body/Slideshow.jsx +++ b/src/components/Body/Slideshow.jsx @@ -5,26 +5,26 @@ import Control from "./Control"; import "../../styles/Body.css"; const Slideshow = ({ photo, handleRotationRight }) => { - React.useEffect(() => { - const interval = setInterval(() => { - handleRotationRight(); - }, 3000); + React.useEffect(() => { + const interval = setInterval(() => { + handleRotationRight(); + }, 3000); - return () => { - clearInterval(interval); - }; - }, [handleRotationRight]); + return () => { + clearInterval(interval); + }; + }, [handleRotationRight]); - return ( -
-
- -
- {"fullsize"} -
-
+ return ( +
+
+ +
+ {"fullsize"}
- ); +
+
+ ); }; export default Slideshow; diff --git a/src/components/Header/Filters.jsx b/src/components/Header/Filters.jsx index 0d2b979..bda3ace 100644 --- a/src/components/Header/Filters.jsx +++ b/src/components/Header/Filters.jsx @@ -11,48 +11,84 @@ import Search from "./Search"; import Selector from "./Selector"; const Filters = () => { - const { data, setYear, setEvent, setPerson, setTeam, setIsOpenMenu, desktop, events, people, teams } = useAppContext(); + const { + data, + setYear, + setEvent, + setPerson, + setTeam, + setIsOpenMenu, + desktop, + events, + people, + teams, + } = useAppContext(); - function formatOptions(a) { - return a.map(x => { - return { data: x, label: x }; - }); - } + function formatOptions(a) { + return a.map((x) => { + return { data: x, label: x }; + }); + } - function formatYearOption(a) { - return a.map(({ year, place }) => { - return { data: year, label: year + " " + place }; - }); - } + function formatYearOption(a) { + return a.map(({ year, place }) => { + return { data: year, label: year + " " + place }; + }); + } - function selectItem(item, func) { - if (item === "clear") { - setYear(data.year); - } else { - func(item.data); - } + function selectItem(item, func) { + if (item === "clear") { + setYear(data.year); + } else { + func(item.data); } + } - return ( - - {!desktop && - { - const year = selectedItem.data; - setYear(year); - }} value={data.year} disableClearable />} - { - selectItem(selectedItem, setEvent); - }} value={data.event} /> - { - selectItem(selectedItem, setTeam); - }} value={data.team} /> - { - selectItem(selectedItem, setPerson); - }} value={data.person} /> - - - ); + return ( + + {!desktop && ( + { + const year = selectedItem.data; + setYear(year); + }} + value={data.year} + disableClearable + /> + )} + { + selectItem(selectedItem, setEvent); + }} + value={data.event} + /> + { + selectItem(selectedItem, setTeam); + }} + value={data.team} + /> + { + selectItem(selectedItem, setPerson); + }} + value={data.person} + /> + + + ); }; export default Filters; - diff --git a/src/components/Header/Header.jsx b/src/components/Header/Header.jsx index e53657e..f98ec33 100644 --- a/src/components/Header/Header.jsx +++ b/src/components/Header/Header.jsx @@ -8,31 +8,37 @@ import { MobileLogo } from "../Logo"; import Filters from "./Filters"; import MobileYearWrappper from "./MobileYearWrapper"; - const Header = () => { - const { isOpenMenu, setIsOpenMenu, mobile } = useAppContext(); + const { isOpenMenu, setIsOpenMenu, mobile } = useAppContext(); - const toggleMenu = () => { - setIsOpenMenu(!isOpenMenu); - }; + const toggleMenu = () => { + setIsOpenMenu(!isOpenMenu); + }; - return ( - - {mobile && - - - - {!isOpenMenu ? - : } - - - } - - - - {mobile && } + return ( + + {mobile && ( + + + + {!isOpenMenu ? ( + + ) : ( + + )} + - ); + )} + + + + {mobile && } + + ); }; export default Header; diff --git a/src/components/Header/MobileYearWrapper.jsx b/src/components/Header/MobileYearWrapper.jsx index f326de4..cf317de 100644 --- a/src/components/Header/MobileYearWrapper.jsx +++ b/src/components/Header/MobileYearWrapper.jsx @@ -3,28 +3,31 @@ import { Box, Typography } from "@mui/material"; import { places } from "../../consts"; import { useAppContext } from "../AppContext"; - const MobileYearWrapper = () => { - const { data, setIsOpenMenu } = useAppContext(); - - const toggleMenu = () => { - setIsOpenMenu(true); - }; - - if (data.text) { - return - {data.text} - ; - } + const { data, setIsOpenMenu } = useAppContext(); - const place = places.find(({ year }) => year === data.year); + const toggleMenu = () => { + setIsOpenMenu(true); + }; + if (data.text) { return ( - - {place?.contestName} {place?.year} - {place?.place} - + + {data.text} + ); + } + + const place = places.find(({ year }) => year === data.year); + + return ( + + + {place?.contestName} {place?.year} + + {place?.place} + + ); }; export default MobileYearWrapper; diff --git a/src/components/Header/Search.jsx b/src/components/Header/Search.jsx index 3c1ed2b..8a10526 100644 --- a/src/components/Header/Search.jsx +++ b/src/components/Header/Search.jsx @@ -5,40 +5,40 @@ import { IconButton, Paper, TextField } from "@mui/material"; import { useAppContext } from "../AppContext"; const Search = () => { - const { setText, setIsOpenMenu, mobile } = useAppContext(); + const { setText, setIsOpenMenu, mobile } = useAppContext(); - const [inputText, setInputText] = useState(""); - const set = (e) => { - e.preventDefault(); - if (inputText !== "") { - setText(inputText); - setInputText(""); + const [inputText, setInputText] = useState(""); + const set = (e) => { + e.preventDefault(); + if (inputText !== "") { + setText(inputText); + setInputText(""); - if (mobile) { - setIsOpenMenu(false); - } - } - }; + if (mobile) { + setIsOpenMenu(false); + } + } + }; - return ( - -
- setInputText(e.target.value)} - placeholder="Global search..." - InputProps={{ - endAdornment: ( - - - - ) - }} - /> - -
- ); + return ( + +
+ setInputText(e.target.value)} + placeholder="Global search..." + InputProps={{ + endAdornment: ( + + + + ), + }} + /> + +
+ ); }; export default Search; diff --git a/src/components/Header/Selector.jsx b/src/components/Header/Selector.jsx index 04a591b..7d6d34c 100644 --- a/src/components/Header/Selector.jsx +++ b/src/components/Header/Selector.jsx @@ -1,50 +1,63 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import { Autocomplete, createFilterOptions, Paper, TextField } from "@mui/material"; +import { + Autocomplete, + createFilterOptions, + Paper, + TextField, +} from "@mui/material"; import { styled } from "@mui/material/styles"; const TextFieldWithIcon = styled(TextField)(() => ({ - "& .MuiInputBase-input.MuiAutocomplete-input": { - marginLeft: "25px" - }, - "& .MuiInputLabel-root": { - marginLeft: "25px" - }, + "& .MuiInputBase-input.MuiAutocomplete-input": { + marginLeft: "25px", + }, + "& .MuiInputLabel-root": { + marginLeft: "25px", + }, })); const filterOptions = createFilterOptions({ limit: 200 }); -const Selector = ({ leftIcon, onChange, name, value, options, disableClearable = false }) => { - return ( - - option.data === value} - onChange={(event, newValue, reason) => { - if (reason === "selectOption") { - onChange(newValue); - } else { - onChange("clear"); - } - }} - renderInput={(params) => - - } - popupIcon={} - disableClearable={disableClearable} - /> - - ); +const Selector = ({ + leftIcon, + onChange, + name, + value, + options, + disableClearable = false, +}) => { + return ( + + option.data === value} + onChange={(event, newValue, reason) => { + if (reason === "selectOption") { + onChange(newValue); + } else { + onChange("clear"); + } + }} + renderInput={(params) => ( + + )} + popupIcon={} + disableClearable={disableClearable} + /> + + ); }; export default Selector; diff --git a/src/components/Logo.jsx b/src/components/Logo.jsx index 4de448f..a14870e 100644 --- a/src/components/Logo.jsx +++ b/src/components/Logo.jsx @@ -4,19 +4,27 @@ import { LAST_YEAR } from "../consts"; import logo from "../images/white.svg"; const MobileLogo = () => { - return ( - - - - ); + return ( + + + + ); }; const DesktopLogo = () => { - return ( - - - - ); + return ( + + + + ); }; export { DesktopLogo, MobileLogo }; diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index a3fa723..d89eb95 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -3,11 +3,11 @@ import TableOfContents from "./TableOfContents"; import "../styles/Sidebar.css"; const Sidebar = () => { - return ( -
- -
- ); + return ( +
+ +
+ ); }; export default Sidebar; diff --git a/src/components/TableOfContents.jsx b/src/components/TableOfContents.jsx index 4045491..8ce6e11 100644 --- a/src/components/TableOfContents.jsx +++ b/src/components/TableOfContents.jsx @@ -5,33 +5,48 @@ import { useAppContext } from "./AppContext"; import "../styles/TableOfContents.css"; const TableOfContents = () => { - const { data, setYear } = useAppContext(); + const { data, setYear } = useAppContext(); - const handleClick = (_, selectedYear) => { - setYear(selectedYear); - document.querySelector(".body").scrollTo({ - top: 0, - behavior: "smooth" - }); - }; + const handleClick = (_, selectedYear) => { + setYear(selectedYear); + document.querySelector(".body").scrollTo({ + top: 0, + behavior: "smooth", + }); + }; - return ( - - ); + return ( + + ); }; export default TableOfContents; diff --git a/src/consts.js b/src/consts.js index 6e32f19..3c29c7d 100644 --- a/src/consts.js +++ b/src/consts.js @@ -3,20 +3,24 @@ console.log(import.meta.env); const consts = await import(`../${dataFolder}/consts.js`); export const getRawEventData = async (year) => { - return (await import(`../${dataFolder}/${year}.event`)).default; + return (await import(`../${dataFolder}/${year}.event`)).default; }; export const getRawTeamData = async (year) => { - return (await import(`../${dataFolder}/${year}.team`)).default; + return (await import(`../${dataFolder}/${year}.team`)).default; }; export const getRawPeopleData = async (year) => { - return (await import(`../${dataFolder}/${year}.people`)).default; + return (await import(`../${dataFolder}/${year}.people`)).default; }; console.log(`Using ${dataFolder} folder for consts.js`); console.log(`Loaded \n${JSON.stringify(consts, undefined, 4)}`); -export const places = consts.places.map(([year, place, contestName]) => ({ "year": year, "place": place, "contestName": contestName })); +export const places = consts.places.map(([year, place, contestName]) => ({ + year: year, + place: place, + contestName: contestName, +})); export const api_key = consts.api_key; export const user_id = consts.user_id; export const title = consts.title; @@ -35,4 +39,3 @@ export const FLICKR_IMAGE_PREFIX = consts.FLICKR_IMAGE_PREFIX; export const SUGGESTIONS_EMAIL = consts.SUGGESTIONS_EMAIL; // todo: somehow replace this without redeclaration export const DEBUG = import.meta.env.mode === "development"; - diff --git a/src/index.jsx b/src/index.jsx index 2e93df3..62dd10b 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -9,15 +9,15 @@ import App from "./App"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - - - - - - - - - + + + + + + + + + , ); // If you want to start measuring performance in your app, pass a function diff --git a/src/styles/App.css b/src/styles/App.css index 96d66cb..95aa151 100644 --- a/src/styles/App.css +++ b/src/styles/App.css @@ -1,7 +1,6 @@ @import "./theme-variables.css"; * { - font-family: "helvetica", "Urbanist", serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + font-family: "helvetica", "Urbanist", serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } - diff --git a/src/styles/Body.css b/src/styles/Body.css index 101bb10..17997ed 100644 --- a/src/styles/Body.css +++ b/src/styles/Body.css @@ -1,211 +1,207 @@ @import "./theme-variables.css"; .body { - height: calc(100vh - 150px); - gap: 2em; - overflow: auto; - border-radius: 8px; + height: calc(100vh - 150px); + gap: 2em; + overflow: auto; + border-radius: 8px; } .body .control { - background: var(--opacityBGColor); - position: fixed; - top: 40px; - right: 40px; - cursor: pointer; + background: var(--opacityBGColor); + position: fixed; + top: 40px; + right: 40px; + cursor: pointer; } .body::-webkit-scrollbar { - width: 10px; - border-radius: 3px; + width: 10px; + border-radius: 3px; } .body::-webkit-scrollbar-track { - background-color: var(--additionalBGColor); + background-color: var(--additionalBGColor); } .body::-webkit-scrollbar-thumb { - background-color: var(--mainBGColor); - outline: 1px solid var(--additionalColor); - border-radius: 3px; + background-color: var(--mainBGColor); + outline: 1px solid var(--additionalColor); + border-radius: 3px; } .masonry { - display: flex; - flex-flow: row wrap; - gap: 3px; - width: 100%; + display: flex; + flex-flow: row wrap; + gap: 3px; + width: 100%; } - .body .preview { - object-fit: cover; - object-position: top; - width: 100%; + object-fit: cover; + object-position: top; + width: 100%; - border-radius: 8px; - height: 100%; + border-radius: 8px; + height: 100%; } .body .download { - text-decoration: none; + text-decoration: none; } @media (min-width: 900px) { - .masonry-brick { - flex: auto; - cursor: pointer; - height: 300px; - min-width: min(150px, 30%); - max-width: 35%; - margin: 0 0 3px 0; - /* Some gutter */ - } - - .masonry-brick:nth-child(7n+1) { - width: 33%; + .masonry-brick { + flex: auto; + cursor: pointer; + height: 300px; + min-width: min(150px, 30%); + max-width: 35%; + margin: 0 0 3px 0; + /* Some gutter */ + } - } + .masonry-brick:nth-child(7n + 1) { + width: 33%; + } - .masonry-brick:nth-child(7n+2) { - width: 33%; - } + .masonry-brick:nth-child(7n + 2) { + width: 33%; + } - .masonry-brick:nth-child(7n+3) { - width: 33%; - } + .masonry-brick:nth-child(7n + 3) { + width: 33%; + } - .masonry-brick:nth-child(7n+4) { - width: 25%; - } + .masonry-brick:nth-child(7n + 4) { + width: 25%; + } - .masonry-brick:nth-child(7n+5) { - width: 25%; - } + .masonry-brick:nth-child(7n + 5) { + width: 25%; + } - .masonry-brick:nth-child(7n+6) { - width: 25%; - } + .masonry-brick:nth-child(7n + 6) { + width: 25%; + } - .masonry-brick:nth-child(7n+7) { - width: 24%; - } + .masonry-brick:nth-child(7n + 7) { + width: 24%; + } } @media (max-width: 900px) { - .masonry-brick { - flex: auto; - cursor: pointer; - min-width: min(150px, 30%); - margin: 0 0 3px 0; - /* Some gutter */ - } - - .masonry-brick:nth-child(3n+1) { - width: 45%; + .masonry-brick { + flex: auto; + cursor: pointer; + min-width: min(150px, 30%); + margin: 0 0 3px 0; + /* Some gutter */ + } - } + .masonry-brick:nth-child(3n + 1) { + width: 45%; + } - .masonry-brick:nth-child(3n+2) { - width: 45%; - } + .masonry-brick:nth-child(3n + 2) { + width: 45%; + } - .masonry-brick:nth-child(3n+3) { - width: 90%; - } + .masonry-brick:nth-child(3n + 3) { + width: 90%; + } } .body .wrapper { - display: flex; - align-items: center; - position: relative; - justify-content: center; - margin: auto; - + display: flex; + align-items: center; + position: relative; + justify-content: center; + margin: auto; } .body .face { - position: absolute; + position: absolute; } .overlay { - z-index: 100; - width: 100vw; - height: 100vh; - position: fixed; - top: 0; - left: 0; - right: 0; - background: var(--opacityBGColor); - display: flex; - align-items: center; - justify-content: space-around; + z-index: 100; + width: 100vw; + height: 100vh; + position: fixed; + top: 0; + left: 0; + right: 0; + background: var(--opacityBGColor); + display: flex; + align-items: center; + justify-content: space-around; } .overlay .img-container { - width: 100vw; - height: 100vh; - display: flex; - justify-content: center; - flex-direction: column; + width: 100vw; + height: 100vh; + display: flex; + justify-content: center; + flex-direction: column; } .overlay .full { - max-width: 100%; - max-height: 100%; - height: auto; - object-fit: contain; - display: block; - box-shadow: 3px 5px 7px var(--opacityBGColor); -} - -.overlay>span { - position: absolute; - top: 20px; - right: 20px; - font-size: 30px; - color: var(--mainColor); - z-index: 999; - cursor: pointer; + max-width: 100%; + max-height: 100%; + height: auto; + object-fit: contain; + display: block; + box-shadow: 3px 5px 7px var(--opacityBGColor); +} + +.overlay > span { + position: absolute; + top: 20px; + right: 20px; + font-size: 30px; + color: var(--mainColor); + z-index: 999; + cursor: pointer; } .overlay-arrows_left { - cursor: pointer; - display: flex; - background: var(--opacityBGColor); - justify-content: center; - position: absolute; - top: 50%; - width: 50px; - height: 50px; - left: 0; - z-index: 999; + cursor: pointer; + display: flex; + background: var(--opacityBGColor); + justify-content: center; + position: absolute; + top: 50%; + width: 50px; + height: 50px; + left: 0; + z-index: 999; } .overlay-arrows_left svg { - width: 50px; - height: 50px; + width: 50px; + height: 50px; } .overlay-arrows_right svg { - width: 50px; - height: 50px; + width: 50px; + height: 50px; } .overlay-arrows_right { - cursor: pointer; - display: flex; - background: var(--opacityBGColor); - justify-content: center; - position: absolute; - right: 0; - width: 50px; - height: 50px; - top: 50%; - z-index: 999; + cursor: pointer; + display: flex; + background: var(--opacityBGColor); + justify-content: center; + position: absolute; + right: 0; + width: 50px; + height: 50px; + top: 50%; + z-index: 999; } .photo-list-message { - font-size: 2rem; - margin-top: 2rem; -} \ No newline at end of file + font-size: 2rem; + margin-top: 2rem; +} diff --git a/src/styles/DropdownMenu.css b/src/styles/DropdownMenu.css index d07e7c6..1716a9c 100644 --- a/src/styles/DropdownMenu.css +++ b/src/styles/DropdownMenu.css @@ -1,23 +1,21 @@ @media (max-width: 900px) { - .header-input-wrapper { - display: none; - width: 100%; - right: 0; - flex-flow: column wrap; - background: var(--opacityBGColor); - } + .header-input-wrapper { + display: none; + width: 100%; + right: 0; + flex-flow: column wrap; + background: var(--opacityBGColor); + } - .search { - padding-top: 0; - padding-bottom: 0; - } + .search { + padding-top: 0; + padding-bottom: 0; + } - - .button { - position: absolute; - right: 15px; - top: 15px; - z-index: 1; - } - -} \ No newline at end of file + .button { + position: absolute; + right: 15px; + top: 15px; + z-index: 1; + } +} diff --git a/src/styles/FaceDiv.css b/src/styles/FaceDiv.css index bc6e871..6455277 100644 --- a/src/styles/FaceDiv.css +++ b/src/styles/FaceDiv.css @@ -1,3 +1,3 @@ .hidden { - opacity: 0; -} \ No newline at end of file + opacity: 0; +} diff --git a/src/styles/PhotoInfo.css b/src/styles/PhotoInfo.css index 5799929..a4e9e6f 100644 --- a/src/styles/PhotoInfo.css +++ b/src/styles/PhotoInfo.css @@ -1,42 +1,41 @@ @import "theme-variables.css"; .photoInfo { - width: 100%; - background: var(--opacityBGColor); - color: var(--mainColor); - position: fixed; - bottom: 0; - padding: 30px 70px 30px 20px; - box-sizing: border-box; - text-align: left; - transition: 1s; + width: 100%; + background: var(--opacityBGColor); + color: var(--mainColor); + position: fixed; + bottom: 0; + padding: 30px 70px 30px 20px; + box-sizing: border-box; + text-align: left; + transition: 1s; } - .photoInfo a { - color: var(--mainColor); + color: var(--mainColor); } .photoInfo a:visited { - color: white; + color: white; } .icon-button { - color: var(--mainColor); + color: var(--mainColor); } .icon-button:visited { - color: white; + color: white; } .control-bottom { - right: 0; - padding-right: 20px; - position: absolute; - display: flex; - flex-direction: row; - bottom: 0; - padding-top: 7px; - padding-bottom: 7px; - cursor: pointer; -} \ No newline at end of file + right: 0; + padding-right: 20px; + position: absolute; + display: flex; + flex-direction: row; + bottom: 0; + padding-top: 7px; + padding-bottom: 7px; + cursor: pointer; +} diff --git a/src/styles/Search.css b/src/styles/Search.css index 6cb04b6..e7a1944 100644 --- a/src/styles/Search.css +++ b/src/styles/Search.css @@ -1,42 +1,43 @@ @import "theme-variables.css"; .search { - width: 100%; - padding: 30px 0; + width: 100%; + padding: 30px 0; } .search form { - display: flex; - align-items: center; - height: 56px; - border-radius: 8px; - position: relative; - margin: 0 auto; + display: flex; + align-items: center; + height: 56px; + border-radius: 8px; + position: relative; + margin: 0 auto; } .search form { - background: var(--additionalBGColor); + background: var(--additionalBGColor); } -.search input, .search button { - border: none; - outline: none; - background: transparent; +.search input, +.search button { + border: none; + outline: none; + background: transparent; } .search input { - font-size: 1rem; - color: var(--mainColor); - box-sizing: border-box; - padding-right: 40px; - padding-left: 10px; - width: 100%; - height: 42px; + font-size: 1rem; + color: var(--mainColor); + box-sizing: border-box; + padding-right: 40px; + padding-left: 10px; + width: 100%; + height: 42px; } .search button { - height: 42px; - width: 42px; - position: absolute; - right: 0; - cursor: pointer; + height: 42px; + width: 42px; + position: absolute; + right: 0; + cursor: pointer; } diff --git a/src/styles/Sidebar.css b/src/styles/Sidebar.css index 4f30b8f..9a4db9d 100644 --- a/src/styles/Sidebar.css +++ b/src/styles/Sidebar.css @@ -1,3 +1,3 @@ .sidebar { - margin-left: 1rem; -} \ No newline at end of file + margin-left: 1rem; +} diff --git a/src/styles/TableOfContents.css b/src/styles/TableOfContents.css index 6125874..fdd5710 100644 --- a/src/styles/TableOfContents.css +++ b/src/styles/TableOfContents.css @@ -1,66 +1,62 @@ @import "theme-variables.css"; nav { - - direction: rtl; - position: -webkit-sticky; /* For Safari */ - top: 24px; /* How far down the page you want your ToC to live */ - - /* Give table of contents a scrollbar */ - height: calc(100vh - 150px); - overflow: auto; - font-style: normal; - font-weight: 700; - font-size: 21px; - line-height: 24px; + direction: rtl; + position: -webkit-sticky; /* For Safari */ + top: 24px; /* How far down the page you want your ToC to live */ + + /* Give table of contents a scrollbar */ + height: calc(100vh - 150px); + overflow: auto; + font-style: normal; + font-weight: 700; + font-size: 21px; + line-height: 24px; } nav::-webkit-scrollbar { - width: 10px; - border-radius: 3px; + width: 10px; + border-radius: 3px; } nav::-webkit-scrollbar-track { - background-color: var(--additionalBGColor); - border-radius: 3px; + background-color: var(--additionalBGColor); + border-radius: 3px; } nav::-webkit-scrollbar-thumb { - background-color: var(--mainBGColor); - outline: 1px solid var(--additionalColor); - border-radius: 3px; + background-color: var(--mainBGColor); + outline: 1px solid var(--additionalColor); + border-radius: 3px; } .year-wrapper { - direction: ltr; - padding-top: 20px; - padding-left: 20px; - cursor: pointer; + direction: ltr; + padding-top: 20px; + padding-left: 20px; + cursor: pointer; } @media (min-width: 900px) { - - .year { - color: var(--mainColor); - font-size: 1.4rem; - } - - .place { - color: var(--additionalColor); - font-size: 1.2rem; - - } - - .big-year { - line-height: 42px; - font-size: 1.7rem; - margin-top: 10px; - } - - - .big-place { - line-height: 32px; - font-size: 1.4rem; - margin-top: 10px; - /*word-break: break-all;*/ - } -} \ No newline at end of file + .year { + color: var(--mainColor); + font-size: 1.4rem; + } + + .place { + color: var(--additionalColor); + font-size: 1.2rem; + } + + .big-year { + line-height: 42px; + font-size: 1.7rem; + margin-top: 10px; + } + + .big-place { + line-height: 32px; + font-size: 1.4rem; + margin-top: 10px; + /*word-break: break-all;*/ + } +} diff --git a/src/styles/theme-variables.css b/src/styles/theme-variables.css index 86f3ff4..b7c7238 100644 --- a/src/styles/theme-variables.css +++ b/src/styles/theme-variables.css @@ -1,9 +1,9 @@ :root { - --mainBGColor: #1A1A1A; - --mainColor: white; - --additionalColor: #8A8A8A; - --opacityBGColor: rgba(27, 27, 27, 0.5); - --additionalBGColor: #2E2E2E; - --colorRectangle: yellow; - --borderRectangle: yellow; -} \ No newline at end of file + --mainBGColor: #1a1a1a; + --mainColor: white; + --additionalColor: #8a8a8a; + --opacityBGColor: rgba(27, 27, 27, 0.5); + --additionalBGColor: #2e2e2e; + --colorRectangle: yellow; + --borderRectangle: yellow; +} diff --git a/src/theme.js b/src/theme.js index aa34183..370f438 100644 --- a/src/theme.js +++ b/src/theme.js @@ -1,73 +1,73 @@ import { createTheme } from "@mui/material/styles"; const theme = createTheme({ - palette: { - mode: "dark", - background: { - default: "#1A1A1A", - paper: "#2E2E2E", - transparent: "rgb(27, 27, 27)", + palette: { + mode: "dark", + background: { + default: "#1A1A1A", + paper: "#2E2E2E", + transparent: "rgb(27, 27, 27)", + }, + text: { + secondary: "#8A8A8A", + }, + }, + components: { + MuiTypography: { + styleOverrides: { + h1: { + fontSize: "2rem", + fontWeight: "bold", + marginTop: "0.83rem", + marginBottom: "0.83rem", + }, + h5: { + color: "#8A8A8A", + }, + }, + }, + MuiPaper: { + defaultProps: { + elevation: 0, + }, + styleOverrides: { + root: { + width: "100%", + borderRadius: "8px", }, - text: { - secondary: "#8A8A8A", - } + }, }, - components: { - MuiTypography: { - styleOverrides: { - h1: { - fontSize: "2rem", - fontWeight: "bold", - marginTop: "0.83rem", - marginBottom: "0.83rem", - }, - h5: { - color: "#8A8A8A", - }, - }, + MuiAutocomplete: { + styleOverrides: { + root: { + margin: 0, + padding: 0, + }, + paper: { + backgroundColor: "#2E2E2E", + color: "#8A8A8A", }, - MuiPaper: { - defaultProps: { - elevation: 0, - }, - styleOverrides: { - root: { - width: "100%", - borderRadius: "8px", - }, - }, + option: { + paddingTop: 4, + paddingBottom: 4, + }, + }, + }, + MuiOutlinedInput: { + styleOverrides: { + root: { + paddingLeft: "10px", + paddingRight: 0, }, - MuiAutocomplete: { - styleOverrides: { - root: { - margin: 0, - padding: 0, - }, - paper: { - backgroundColor: "#2E2E2E", - color: "#8A8A8A", - }, - option: { - paddingTop: 4, - paddingBottom: 4, - } - }, + notchedOutline: { + display: "none", }, - MuiOutlinedInput: { - styleOverrides: { - root: { - paddingLeft: "10px", - paddingRight: 0, - }, - notchedOutline: { - display: "none", - }, - input: { - paddingLeft: "0px", - }, - }, + input: { + paddingLeft: "0px", }, + }, }, + }, }); export default theme; diff --git a/vite.config.mjs b/vite.config.mjs index e3a950e..bbf8987 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -3,24 +3,27 @@ import { defineConfig } from "vite"; import dataRawPlugin from "vite-raw-plugin"; export default defineConfig({ - plugins: [react(), dataRawPlugin({ - fileRegex: /\.(?:team|event|people)$/ - })], - server: { - port: 3000, - }, - build: { - target: "esnext", - emptyOutDir: true, - outDir: process.env.PUBLIC_URL, - rollupOptions: { - output: { - manualChunks: (id) => { - if (id.includes("react")) { - return "r"; - } - } - } - } + plugins: [ + react(), + dataRawPlugin({ + fileRegex: /\.(?:team|event|people)$/, + }), + ], + server: { + port: 3000, + }, + build: { + target: "esnext", + emptyOutDir: true, + outDir: process.env.PUBLIC_URL, + rollupOptions: { + output: { + manualChunks: (id) => { + if (id.includes("react")) { + return "r"; + } + }, + }, }, + }, });