├── LICENSE.md ├── README.md └── weather-forecast ├── .gitignore ├── package-lock.json ├── package.json ├── public ├── backup_favicon.ico ├── fallback.ico ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.scss ├── App.test.tsx ├── App.tsx ├── actions │ └── index.ts ├── components │ ├── BootUpWindow │ │ ├── BootUpWindow.scss │ │ └── BootUpWindow.tsx │ ├── ChakraAlert │ │ └── ChakraAlert.tsx │ ├── ChakraCookies │ │ ├── ChakraCookies.scss │ │ └── ChakraCookies.tsx │ ├── ContextMenu │ │ ├── ContextMenu.scss │ │ └── ContextMenu.tsx │ ├── DailyCon │ │ ├── DailyCon.scss │ │ └── DailyCon.tsx │ ├── DailyTemps │ │ ├── DailyTemps.scss │ │ └── DailyTemps.tsx │ ├── Dock │ │ ├── Dock.scss │ │ └── Dock.tsx │ ├── DropdownComponent │ │ ├── DropdownComponent.scss │ │ └── DropdownComponent.tsx │ ├── ForecastList │ │ ├── ForecastList.scss │ │ └── ForecastList.tsx │ ├── InputField │ │ ├── InputField.scss │ │ └── InputField.tsx │ ├── NavBar │ │ ├── NavBar.scss │ │ └── NavBar.tsx │ ├── Page │ │ ├── Page.scss │ │ └── Page.tsx │ ├── QueryBoard │ │ ├── QueryBoard.scss │ │ └── QueryBoard.tsx │ ├── QueryBoardLinks │ │ ├── QueryBoardLinks.scss │ │ └── QueryBoardLinks.tsx │ ├── SelectedDayForecast │ │ ├── SelectedDayForecast.scss │ │ └── SelectedDayForecast.tsx │ ├── SelectedWeatherSlice │ │ ├── SelectedWeatherSlice.scss │ │ └── SelectedWeatherSlice.tsx │ ├── SettingsDropdown │ │ ├── SettingsDropdown.scss │ │ └── SettingsDropdown.tsx │ ├── WallpaperMenu │ │ ├── WallpaperMenu.scss │ │ └── WallpaperMenu.tsx │ ├── WeatherBoard │ │ ├── WeatherBoard.scss │ │ └── WeatherBoard.tsx │ └── Window │ │ ├── Window.scss │ │ └── Window.tsx ├── custom.d.ts ├── framer-motion.d.ts ├── index.css ├── index.tsx ├── logo.svg ├── react-app-env.d.ts ├── reducers │ └── reducer.ts ├── reportWebVitals.ts ├── resources │ ├── audio │ │ ├── bootsound.mp3 │ │ └── bootsound.wav │ ├── fonts │ │ ├── sfbold.otf │ │ ├── sflight.otf │ │ ├── sfmedium.otf │ │ ├── sfregular.otf │ │ └── sfsemibold.otf │ └── images │ │ ├── airdrop.png │ │ ├── apple.png │ │ ├── applelogo.png │ │ ├── bigsur.jpg │ │ ├── bigsurgraphic.jpg │ │ ├── catalina.jpg │ │ ├── catalina_day.jpg │ │ ├── cookies.png │ │ ├── cursor.png │ │ ├── default@2x.png │ │ ├── dome.jpg │ │ ├── fullscreen.png │ │ ├── grabbing.png │ │ ├── iridescence.jpg │ │ ├── lake.jpg │ │ ├── linkedin.png │ │ ├── mojave.jpg │ │ ├── monterey.jpg │ │ ├── paperplane.PNG │ │ ├── peak.jpg │ │ ├── pointing.png │ │ ├── preview_bigsur.jpg │ │ ├── preview_bigsurgraphic.jpg │ │ ├── preview_bigsurgraphic.webp │ │ ├── preview_catalina.jpg │ │ ├── preview_catalina.png │ │ ├── preview_catalina.webp │ │ ├── preview_dome.jpg │ │ ├── preview_dome.webp │ │ ├── preview_iridescence.jpg │ │ ├── preview_iridescence.webp │ │ ├── preview_lake.jpg │ │ ├── preview_lake.webp │ │ ├── preview_mojave.jpg │ │ ├── preview_mojave.webp │ │ ├── preview_monterey.jpg │ │ ├── preview_monterey.webp │ │ ├── preview_peak.jpg │ │ ├── preview_peak.webp │ │ ├── preview_solargrad.jpg │ │ ├── preview_solargrad.webp │ │ ├── preview_thedesert.jpg │ │ ├── preview_thedesert.webp │ │ ├── preview_ventura.jpg │ │ ├── refresh.png │ │ ├── showcase.gif │ │ ├── solargrad.jpg │ │ ├── svg │ │ ├── airdrop.svg │ │ ├── animations.svg │ │ ├── arrow.svg │ │ ├── chat.svg │ │ ├── close.svg │ │ ├── githubcat.svg │ │ ├── loading.svg │ │ ├── loading2.svg │ │ ├── location.svg │ │ ├── lock.svg │ │ ├── minimize.svg │ │ ├── notch.svg │ │ ├── search.svg │ │ ├── settings.svg │ │ ├── stretch.svg │ │ ├── tick.svg │ │ └── weather │ │ │ ├── Ash.svg │ │ │ ├── Clear.svg │ │ │ ├── Clear_day.svg │ │ │ ├── Clear_night.svg │ │ │ ├── Clouds.svg │ │ │ ├── Drizzle.svg │ │ │ ├── Dust.svg │ │ │ ├── Fog.svg │ │ │ ├── Haze.svg │ │ │ ├── Mist.svg │ │ │ ├── Rain.svg │ │ │ ├── Sand.svg │ │ │ ├── Smoke.svg │ │ │ ├── Snow.svg │ │ │ ├── Squall.svg │ │ │ ├── Thunderstorm.svg │ │ │ ├── Tornado.svg │ │ │ ├── highest.svg │ │ │ ├── humidity.svg │ │ │ ├── location.svg │ │ │ ├── lowest.svg │ │ │ ├── raindrop.png │ │ │ ├── raindrops.svg │ │ │ └── wind.svg │ │ ├── thedesert.jpg │ │ ├── ventura.jpg │ │ └── webp │ │ ├── applemusic.png │ │ ├── arcade.png │ │ ├── calculator.png │ │ ├── calculator.webp │ │ ├── calendar.png │ │ ├── calendar.webp │ │ ├── discord.png │ │ ├── finder.png │ │ ├── finder.webp │ │ ├── github.png │ │ ├── github.webp │ │ ├── netflix.png │ │ ├── photos.png │ │ ├── scalable.png │ │ ├── spotify.png │ │ ├── twitter.png │ │ ├── vscode.png │ │ ├── vscode.webp │ │ └── weather.png ├── setupTests.ts ├── types │ ├── DropdownItemType.ts │ ├── action.ts │ ├── intervalType.ts │ ├── query.ts │ ├── sortedInterval.ts │ ├── store.ts │ ├── wallpaperObjectType.ts │ └── weather.ts └── utils │ └── helpers │ ├── checkDropdown.ts │ ├── checkSettings.ts │ ├── clearStorage.ts │ ├── getDate.ts │ ├── getDropdownContent.ts │ ├── getPosition.ts │ ├── identifyDockItem.ts │ ├── inputWork.ts │ ├── nightTimes.ts │ ├── openContextMenu.ts │ ├── openWeatherApp.ts │ ├── positionWork.ts │ ├── returnColor.ts │ ├── submitInput.ts │ ├── toggleBorder.ts │ ├── toggleFullscreen.ts │ ├── toggleMinimize.ts │ ├── toggleVisibility.ts │ ├── toggleWallpaperMin.ts │ ├── toggleWallpaperVis.ts │ ├── updateSysColor.ts │ └── wallpapers.ts └── tsconfig.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Gianluca Jahn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

MacOS in React

4 |

A MacOS Clone built in TypeScript React with SASS and Framer Motion, tested with Jest.

5 | 6 | ![](https://img.shields.io/badge/contributors-2-white) 7 | ![](https://img.shields.io/badge/commits-172-white) 8 | ![](https://img.shields.io/badge/test%20coverage-96%25-brightgreen) 9 | ![](https://img.shields.io/badge/open%20source-true-brightgreen) 10 | ![](https://img.shields.io/github/stars/gianlucajahn/macOS-react?style=social) 11 | ![](https://img.shields.io/github/followers/gianlucajahn?style=social) 12 | 13 |
14 | 15 | ![](https://i.ibb.co/S0S9bN3/banner.png) 16 |

17 | Desktop showcase without any modals 18 |

19 |
20 | 21 | ## Short Description 22 | A MacOS Clone built with React. Imitates the usual desktop features, wallpaper settings, system settings, an integrated weather app (and more to come). Detailed feature-list can be found below. 23 | 24 | ## 🔴 Demo 25 | 🧪 [Live Demo](https://gianlucajahn.github.io/macOS-react) available. Click "Live Demo" to open it. 26 | 27 | ## Showcase 28 | You can see images and a GIF of the project in user interaction below. The GIF does not show all features of the application, it only shows the most elementary user interactions.

29 | 30 | ![](https://i.imgur.com/7sVidqH.png) 31 | ![](https://i.imgur.com/8KfyVi6.png) 32 | ![](https://i.imgur.com/mCU5H4l.png) 33 | ![](/weather-forecast/src/resources/images/showcase.gif) 34 | 35 | ## Features 36 | - ✔️ Bootup Window similar to the original MacOS 37 | - ✔️ System Settings, Color and Wallpaper preferences 38 | - ✔️ Local storage and session storage saving user preferences 39 | - ✔️ Integrated Weather App including geolocation to fetch forecasts for the user's location 40 | - ✔️ Wallpaper Selection window to switch desktop backgrounds 41 | - ✔️ Custom Context Menu and System Navigation Bar 42 | - ✔️ Smooth Animations w/ [Framer Motion](https://github.com/framer/motion) 43 | - ❌ (WIP) Calculator and Calendar Apps 44 | 45 | ## Stack 46 | - Framework: React 47 | - Language: TypeScript 48 | - Motion Library: Framer Motion 49 | - Component Library: none 50 | - Stylesheet: SCSS 51 | 52 | ## Motivation 53 | My initial motivation in building this was honestly not to build it at all. Because as you can see by this project's folder structure, it was in fact not supposed to be a mac os clone but just a simple weather app when I was starting out. I was trying to revisit API usage to get muscle memory and a better understanding of working with APIs in general down but ended up being a bit bored and built a MacOS around it. 54 | 55 | ## 🤝 Contributions 56 | This project is open to contributions! I am still working on some features (Calculator, Calendar, VSCode, Music App) and there's probably a ton of things to add/change/fix. Feel free to fork it and create a PR! 57 | 58 | ## Credits 59 | [PuruVJ](https://github.com/PuruVJ): I built this clone entirely inspired by this MacOS Clone in Svelte, although I reverse-engineered it, meaning I didn't re-use anything from the project. 60 | 61 | All rights to all pictures, products and names on this website belong to Apple Inc. I only used them to build an environment for myself to learn coding in React with. This page is not being used commercially. If you are an owner of the copyrighted material, please let me know if you have any issues with this page and I'll take it down immediately 62 | -------------------------------------------------------------------------------- /weather-forecast/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # keys 12 | /src/utils/keys 13 | /static/js 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env.local 21 | .env.development.local 22 | .env.test.local 23 | .env.production.local 24 | 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | -------------------------------------------------------------------------------- /weather-forecast/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather-forecast", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://gianlucajahn.github.io/macOS-react/", 6 | "dependencies": { 7 | "@chakra-ui/react": "^2.4.1", 8 | "@emotion/react": "^11.10.5", 9 | "@emotion/styled": "^11.10.5", 10 | "@reduxjs/toolkit": "^1.9.0", 11 | "@testing-library/jest-dom": "^5.16.5", 12 | "@testing-library/react": "^13.4.0", 13 | "@testing-library/user-event": "^13.5.0", 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "^16.18.3", 16 | "@types/react": "^18.0.25", 17 | "@types/react-dom": "^18.0.9", 18 | "framer-motion": "^7.10.2", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0", 21 | "react-draggable": "^4.4.5", 22 | "react-redux": "^8.0.5", 23 | "react-scripts": "5.0.1", 24 | "react-uuid": "^2.0.0", 25 | "redux": "^4.2.0", 26 | "typescript": "^4.9.3", 27 | "uniqid": "^5.4.0", 28 | "web-vitals": "^2.1.4" 29 | }, 30 | "scripts": { 31 | "start": "react-scripts start", 32 | "build": "react-scripts build", 33 | "test": "react-scripts test", 34 | "eject": "react-scripts eject", 35 | "predeploy": "npm run build", 36 | "deploy": "gh-pages -d build" 37 | }, 38 | "eslintConfig": { 39 | "extends": [ 40 | "react-app", 41 | "react-app/jest" 42 | ] 43 | }, 44 | "browserslist": { 45 | "production": [ 46 | ">0.2%", 47 | "not dead", 48 | "not op_mini all" 49 | ], 50 | "development": [ 51 | "last 1 chrome version", 52 | "last 1 firefox version", 53 | "last 1 safari version" 54 | ] 55 | }, 56 | "devDependencies": { 57 | "gh-pages": "^4.0.0", 58 | "redux-devtools-extension": "^2.13.9" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /weather-forecast/public/backup_favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/public/backup_favicon.ico -------------------------------------------------------------------------------- /weather-forecast/public/fallback.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/public/fallback.ico -------------------------------------------------------------------------------- /weather-forecast/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/public/favicon.ico -------------------------------------------------------------------------------- /weather-forecast/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 20 | macOS in React 21 | 22 | 23 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /weather-forecast/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/public/logo192.png -------------------------------------------------------------------------------- /weather-forecast/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/public/logo512.png -------------------------------------------------------------------------------- /weather-forecast/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /weather-forecast/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /weather-forecast/src/App.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/App.scss -------------------------------------------------------------------------------- /weather-forecast/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /weather-forecast/src/App.tsx: -------------------------------------------------------------------------------- 1 | // Imports 2 | import "./App.scss"; 3 | import React, { createContext, useReducer, useEffect } from "react"; 4 | import { ChakraProvider } from "@chakra-ui/react"; 5 | import reducer from "./reducers/reducer"; 6 | import sampleStore from "./utils/keys/samples/sampleStore"; 7 | import WeatherBoard from "./components/WeatherBoard/WeatherBoard"; 8 | import QueryBoard from "./components/QueryBoard/QueryBoard"; 9 | import Draggable from "react-draggable"; 10 | import NavBar from "./components/NavBar/NavBar"; 11 | import Page from "./components/Page/Page"; 12 | import Dock from "./components/Dock/Dock"; 13 | import ContextMenu from "./components/ContextMenu/ContextMenu"; 14 | import BootSound from "./resources/audio/bootsound.mp3"; 15 | import WallpaperMenu from "./components/WallpaperMenu/WallpaperMenu"; 16 | import wallpapers from "./utils/helpers/wallpapers"; 17 | 18 | // Create store (redux naming convention) 19 | export const store = createContext(null); 20 | 21 | // Create store provider to wrap subcomponents in 22 | const StoreProvider = ({ children }: any) => ( 23 | 24 | {children} 25 | 26 | ); 27 | 28 | function App() { 29 | return ( 30 | 31 | 32 | 33 | 34 | { 37 | if (e.target.id !== "handle") { 38 | return false; 39 | } 40 | }} 41 | > 42 |
43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 |
51 |
52 |
53 | ); 54 | } 55 | 56 | export default App; 57 | -------------------------------------------------------------------------------- /weather-forecast/src/actions/index.ts: -------------------------------------------------------------------------------- 1 | export const geolocate = () => { 2 | return { 3 | type: "GEO", 4 | }; 5 | }; 6 | -------------------------------------------------------------------------------- /weather-forecast/src/components/BootUpWindow/BootUpWindow.scss: -------------------------------------------------------------------------------- 1 | @keyframes load { 2 | 0% { 3 | color: black; 4 | } 5 | 100% { 6 | color: grey; 7 | } 8 | } 9 | 10 | .bootup-window { 11 | background-color: black; 12 | width: 100vw; 13 | height: 100vh; 14 | display: flex; 15 | flex-direction: column; 16 | transition: 1s all; 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | z-index: 90; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | 25 | .boot { 26 | transition: 1s opacity; 27 | cursor: none; 28 | animation: 0.1s ease-out 0s 1 load !important; 29 | 30 | .logo { 31 | height: 109px; 32 | -webkit-user-drag: none; 33 | pointer-events: none; 34 | width: 105px; 35 | justify-self: flex-start; 36 | margin-top: 7px; 37 | margin-right: 12px; 38 | } 39 | 40 | .bar-container { 41 | margin-top: 37px; 42 | padding: 0; 43 | position: relative; 44 | 45 | .bar-background { 46 | margin: 0; 47 | background-color: #424242; 48 | border: none; 49 | border-radius: 9999px; 50 | width: 150px; 51 | height: 4px; 52 | } 53 | 54 | .bar { 55 | position: absolute; 56 | height: 4px; 57 | width: 0px; 58 | border-radius: 9999px; 59 | border: none; 60 | top: 0; 61 | background-color: white; 62 | } 63 | } 64 | } 65 | 66 | .vanished { 67 | opacity: 0; 68 | z-index: 90; 69 | background-color: black; 70 | width: 100vw; 71 | position: absolute; 72 | top: 0; 73 | left: 0; 74 | height: 100vh; 75 | display: flex; 76 | transition: 1s all; 77 | cursor: none; 78 | 79 | .logo { 80 | visibility: hidden; 81 | justify-self: flex-start; 82 | margin-top: 7px; 83 | margin-right: 12px; 84 | } 85 | 86 | .bar-container { 87 | visibility: hidden; 88 | margin-top: 37px; 89 | padding: 0; 90 | position: relative; 91 | 92 | .bar-background { 93 | margin: 0; 94 | background-color: #424242; 95 | border: none; 96 | border-radius: 9999px; 97 | width: 150px; 98 | height: 4px; 99 | } 100 | 101 | .bar { 102 | position: absolute; 103 | height: 4px; 104 | width: 0px; 105 | border-radius: 9999px; 106 | border: none; 107 | top: 0; 108 | background-color: white; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /weather-forecast/src/components/BootUpWindow/BootUpWindow.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from "react"; 2 | import { motion } from "framer-motion"; 3 | import { store } from "../../App"; 4 | import "./BootUpWindow.scss"; 5 | import BootSound from "../../resources/audio/bootsound.mp3"; 6 | 7 | export default function BootUpWindow() { 8 | const [state, dispatch] = useContext(store); 9 | 10 | useEffect(() => { 11 | setTimeout(() => { 12 | const bootUpWindow = document.getElementById("boot"); 13 | bootUpWindow!.classList.remove("bootup-window"); 14 | bootUpWindow!.classList.add("vanished"); 15 | playSound(); 16 | }, 3350); 17 | 18 | setTimeout(() => { 19 | dispatch({ 20 | type: "booting/FINISH", 21 | }); 22 | }, 4350); 23 | }, []); 24 | 25 | const animations = { 26 | initial: { width: "0px" }, 27 | animate: { width: "150px" }, 28 | }; 29 | 30 | let audio = new Audio(BootSound); 31 | audio.volume = 0.4; 32 | 33 | const playSound = () => { 34 | if (!state.soundPlayed) { 35 | dispatch({ 36 | type: "sound/PLAY", 37 | }); 38 | 39 | setTimeout(() => { 40 | if (document.getElementById("boot") === null) { 41 | return; 42 | } else { 43 | audio.play(); 44 | } 45 | }, 2200); 46 | } 47 | }; 48 | 49 | return ( 50 |
51 | Logo 56 |
57 |
58 | 65 |
66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /weather-forecast/src/components/ChakraAlert/ChakraAlert.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | AlertDialog, 4 | AlertDialogBody, 5 | AlertDialogContent, 6 | AlertDialogFooter, 7 | AlertDialogHeader, 8 | AlertDialogOverlay, 9 | Button, 10 | } from "@chakra-ui/react"; 11 | import toggleFullscreen from "../../utils/helpers/toggleFullscreen"; 12 | 13 | export interface ChakraAlertProps { 14 | isOpen: any; 15 | cancelRef: any; 16 | onClose: any; 17 | } 18 | 19 | export default function ChakraAlert(props: ChakraAlertProps) { 20 | const { isOpen, cancelRef, onClose } = props; 21 | 22 | return ( 23 | 28 | 29 | 30 | 31 | Enter Fullscreen 32 | 33 | 34 | 35 | Are you sure you want to enter fullscreen mode? You can exit via ESC 36 | or by clicking the fullscreen button again. 37 | 38 | 39 | 40 | 43 | 53 | 54 | 55 | 56 | 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /weather-forecast/src/components/ChakraCookies/ChakraCookies.scss: -------------------------------------------------------------------------------- 1 | .cookies-btn { 2 | margin-left: 10em; 3 | width: 41px !important; 4 | position: relative; 5 | transition: 0.25s all; 6 | 7 | .cookies { 8 | height: 17px; 9 | margin-bottom: 2px; 10 | } 11 | } 12 | 13 | .tooltip { 14 | visibility: hidden; 15 | opacity: 0; 16 | width: 100px; 17 | top: -42px; 18 | backdrop-filter: blur(50px); 19 | background-color: rgba(0, 0, 0, 0.6); 20 | color: #fff; 21 | text-align: center; 22 | padding: 3px 7px; 23 | font-family: "SF Regular"; 24 | font-size: 14px; 25 | transition: 0.3s all ease-in-out; 26 | border: 1px solid #ccc; 27 | box-shadow: 0px 0px 5px black; 28 | border-radius: 6px; 29 | 30 | position: absolute; 31 | z-index: 1; 32 | } 33 | 34 | .cookies-btn:hover .tooltip { 35 | visibility: visible; 36 | opacity: 1; 37 | top: -50px; 38 | } 39 | -------------------------------------------------------------------------------- /weather-forecast/src/components/ChakraCookies/ChakraCookies.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Button, Slide, useDisclosure } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import "./ChakraCookies.scss"; 4 | 5 | export default function ChakraCookies() { 6 | return ( 7 | <> 8 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /weather-forecast/src/components/ContextMenu/ContextMenu.scss: -------------------------------------------------------------------------------- 1 | @import "../Page/Page.scss"; 2 | 3 | .context-menu { 4 | width: 16em; 5 | display: flex; 6 | flex-direction: column; 7 | gap: 3px; 8 | cursor: url("../../resources/images/cursor.png"), auto; 9 | border: 1px solid rgba(255, 255, 255, 0.335); 10 | border-radius: 8px; 11 | z-index: 20 !important; 12 | background-color: rgba(0, 0, 0, 0.19); 13 | position: absolute; 14 | padding: 11px 4px 8px 7px; 15 | left: 0; 16 | opacity: 1; 17 | top: 30px; 18 | box-shadow: 0px 0px 3.35px black; 19 | backdrop-filter: blur(15px); 20 | 21 | .first { 22 | margin-top: 2px; 23 | } 24 | 25 | .context-item { 26 | color: white; 27 | margin-bottom: 3px; 28 | padding: 0px 8px; 29 | font-size: 14.6px; 30 | font-family: "SF Regular"; 31 | transform: scaleY(1.03); 32 | border: none; 33 | border-radius: 4px; 34 | 35 | &:hover { 36 | background-color: $systemColor; 37 | color: black !important; 38 | cursor: url("../../resources/images/pointing.png"), auto; 39 | } 40 | } 41 | 42 | .context-divider { 43 | margin-bottom: 3px; 44 | margin-top: 2px; 45 | margin: 2px 3px 3px 0px; 46 | height: 1px; 47 | background-color: rgba(255, 255, 255, 0.335); 48 | } 49 | } 50 | 51 | #context-menu { 52 | visibility: hidden; 53 | position: absolute; 54 | } -------------------------------------------------------------------------------- /weather-forecast/src/components/ContextMenu/ContextMenu.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { store } from "../../App"; 3 | import toggleWallpaperVis from "../../utils/helpers/toggleWallpaperVis"; 4 | import "./ContextMenu.scss"; 5 | 6 | export default function ContextMenu() { 7 | const [state, dispatch] = useContext(store); 8 | 9 | const openWallpaperWindow = (e: React.MouseEvent) => { 10 | if (state.settings.wallpaper.open) { 11 | toggleWallpaperVis(e); 12 | } 13 | 14 | dispatch({ 15 | type: "wallpaper/TOGGLE", 16 | }); 17 | }; 18 | 19 | return ( 20 |
21 |
New Folder
22 | 23 |
24 | 25 |
Get Info
26 | 27 |
32 | Change Desktop Background 33 |
34 | 35 |
36 | 37 |
Use Stacks
38 | 39 |
Sort By
40 | 41 |
Clean Up
42 | 43 |
Clean Up By
44 | 45 |
Show View Options
46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /weather-forecast/src/components/DailyCon/DailyCon.scss: -------------------------------------------------------------------------------- 1 | .daily-con { 2 | display: flex; 3 | gap: 8px; 4 | text-align: start; 5 | 6 | h3 { 7 | margin-left: 14px; 8 | margin-right: 100px; 9 | width: 120px; 10 | } 11 | 12 | * { 13 | max-height: 24px; 14 | } 15 | 16 | *:not(h3) { 17 | width: 24px; 18 | margin-left: 200px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /weather-forecast/src/components/DailyCon/DailyCon.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { store } from "../../App"; 3 | import "./DailyCon.scss"; 4 | 5 | // SVG Imports 6 | import { ReactComponent as Ash } from "../../resources/images/svg/weather/Ash.svg"; 7 | import { ReactComponent as Clear } from "../../resources/images/svg/weather/Clear.svg"; 8 | import { ReactComponent as Clouds } from "../../resources/images/svg/weather/Clouds.svg"; 9 | import { ReactComponent as Drizzle } from "../../resources/images/svg/weather/Drizzle.svg"; 10 | import { ReactComponent as Dust } from "../../resources/images/svg/weather/Dust.svg"; 11 | import { ReactComponent as Fog } from "../../resources/images/svg/weather/Fog.svg"; 12 | import { ReactComponent as Haze } from "../../resources/images/svg/weather/Haze.svg"; 13 | import { ReactComponent as Mist } from "../../resources/images/svg/weather/Mist.svg"; 14 | import { ReactComponent as Rain } from "../../resources/images/svg/weather/Rain.svg"; 15 | import { ReactComponent as Sand } from "../../resources/images/svg/weather/Sand.svg"; 16 | import { ReactComponent as Smoke } from "../../resources/images/svg/weather/Smoke.svg"; 17 | import { ReactComponent as Snow } from "../../resources/images/svg/weather/Snow.svg"; 18 | import { ReactComponent as Squall } from "../../resources/images/svg/weather/Squall.svg"; 19 | import { ReactComponent as Thunderstorm } from "../../resources/images/svg/weather/Thunderstorm.svg"; 20 | import { ReactComponent as Tornado } from "../../resources/images/svg/weather/Tornado.svg"; 21 | import sortedIntervalType from "../../types/sortedInterval"; 22 | 23 | export interface DailyConProps { 24 | day: sortedIntervalType[]; 25 | } 26 | 27 | export default function DailyCon(props: DailyConProps) { 28 | const [state, dispatch] = useContext(store); 29 | const { day } = props; 30 | 31 | // determine weather condition with highest occurence within 32 | // the day array 33 | const determineDailyConString = (day: sortedIntervalType[]) => { 34 | const conditions: string[] = []; 35 | day.map((interval: any, i: number) => { 36 | conditions.push(interval.weather); 37 | }); 38 | 39 | conditions.sort(); 40 | var max = 0, 41 | result, 42 | freq = 0; 43 | for (var i = 0; i < conditions.length; i++) { 44 | if (conditions[i] === conditions[i + 1]) { 45 | freq++; 46 | } else { 47 | freq = 0; 48 | } 49 | if (freq > max) { 50 | result = conditions[i]; 51 | max = freq; 52 | } 53 | } 54 | 55 | // render corresponding SVG depending on the outcome 56 | if (result === undefined) { 57 | result = day[0].weather; 58 | } 59 | return result; 60 | }; 61 | 62 | const determineDailyCon = (day: sortedIntervalType[]) => { 63 | const conditions: string[] = []; 64 | day.map((interval: sortedIntervalType, i: number) => { 65 | conditions.push(interval.weather); 66 | }); 67 | 68 | conditions.sort(); 69 | var max = 0, 70 | result, 71 | freq = 0; 72 | for (var i = 0; i < conditions.length; i++) { 73 | if (conditions[i] === conditions[i + 1]) { 74 | freq++; 75 | } else { 76 | freq = 0; 77 | } 78 | if (freq > max) { 79 | result = conditions[i]; 80 | max = freq; 81 | } 82 | } 83 | 84 | // render corresponding SVG depending on the outcome 85 | if (result === undefined) { 86 | result = day[0].weather; 87 | } 88 | let outcome: string = result; 89 | switch (outcome) { 90 | case "Clear": 91 | return ; 92 | case "Clouds": 93 | return ; 94 | case "Ash": 95 | return ; 96 | case "Drizzle": 97 | return ; 98 | case "Dust": 99 | return ; 100 | case "Fog": 101 | return ; 102 | case "Snow": 103 | return ; 104 | case "Haze": 105 | return ; 106 | case "Mist": 107 | return ; 108 | case "Rain": 109 | return ; 110 | case "Sand": 111 | return ; 112 | case "Smoke": 113 | return ; 114 | case "Squall": 115 | return ; 116 | case "Thunderstorm": 117 | return ; 118 | case "Tornado": 119 | return ; 120 | } 121 | }; 122 | 123 | return ( 124 |
125 | {determineDailyCon(day)} 126 |

{determineDailyConString(day)}

127 |
128 | ); 129 | } 130 | -------------------------------------------------------------------------------- /weather-forecast/src/components/DailyTemps/DailyTemps.scss: -------------------------------------------------------------------------------- 1 | .daily-temps { 2 | display: flex; 3 | gap: 20px; 4 | margin-left: 20px; 5 | 6 | div { 7 | display: flex; 8 | gap: 6px; 9 | align-items: center; 10 | 11 | h3 { 12 | } 13 | 14 | .highest-h3 { 15 | width: 40px; 16 | } 17 | 18 | .lowest-h3 { 19 | width: 40px; 20 | } 21 | 22 | .icon { 23 | fill: white; 24 | height: 17px; 25 | margin-right: 10px; 26 | 27 | } 28 | 29 | .highest-icon { 30 | width: 16px; 31 | } 32 | 33 | .lowest-icon { 34 | width: 16px; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /weather-forecast/src/components/DailyTemps/DailyTemps.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import "./DailyTemps.scss"; 3 | import { ReactComponent as Highest } from "../../resources/images/svg/weather/highest.svg"; 4 | import { ReactComponent as Lowest } from "../../resources/images/svg/weather/lowest.svg"; 5 | import sortedIntervalType from "../../types/sortedInterval"; 6 | 7 | export interface DailyTempsProps { 8 | day: sortedIntervalType[]; 9 | } 10 | 11 | export default function DailyTemps(props: DailyTempsProps) { 12 | const { day } = props; 13 | 14 | const determineTemps = () => { 15 | let lowestTemp: number = 100; 16 | let highestTemp: number = -100; 17 | day.forEach((interval: sortedIntervalType, i: number) => { 18 | if (interval.minTemp < lowestTemp) { 19 | lowestTemp = interval.minTemp; 20 | } 21 | 22 | if (interval.maxTemp > highestTemp) { 23 | highestTemp = interval.maxTemp; 24 | } 25 | }); 26 | 27 | return [lowestTemp, highestTemp]; 28 | }; 29 | 30 | const temps = determineTemps(); 31 | 32 | return ( 33 |
34 |
35 | 36 |

{temps[0]}°

37 |
38 |
39 | 40 |

{temps[1]}°

41 |
42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /weather-forecast/src/components/Dock/Dock.scss: -------------------------------------------------------------------------------- 1 | .dock { 2 | display: flex; 3 | background-color: hsla(240, 3%, 11%, 0.4); 4 | box-shadow: 0px 0px 4px black; 5 | height: 71px !important; 6 | justify-content: center; 7 | gap: 1px; 8 | align-items: center; 9 | border: 1px solid rgb(85, 84, 84); 10 | border-radius: 18px; 11 | min-width: 618px; 12 | margin-top: 50px; 13 | position: absolute; 14 | bottom: 9px; 15 | backdrop-filter: blur(20px); 16 | overflow-y: visible !important; 17 | 18 | .finder { 19 | margin-right: 1px; 20 | } 21 | 22 | .dock-item { 23 | display: flex; 24 | flex-direction: column; 25 | align-items: center; 26 | margin-bottom: 5px; 27 | margin-top: 4px; 28 | transition: 0.175s all ease-in-out; 29 | position: relative; 30 | 31 | .tool-tip { 32 | position: absolute; 33 | top: -40px; 34 | border-radius: 6px; 35 | background-color: rgba(0, 0, 0, 0.4); 36 | color: white; 37 | display: flex; 38 | align-items: center; 39 | justify-content: center; 40 | padding: 2px 8px 3px 8px; 41 | border: 1px solid #707272; 42 | backdrop-filter: blur(20px); 43 | transition: 0.2s all; 44 | opacity: 0; 45 | visibility: hidden; 46 | } 47 | } 48 | 49 | .no-point { 50 | margin-bottom: 9px; 51 | } 52 | 53 | .dock-icon { 54 | height: 3.59rem; 55 | cursor: url("../../resources//images/pointing.png"), auto; 56 | transition: 0.175s all ease-in-out; 57 | } 58 | 59 | .division { 60 | width: 1px; 61 | height: 62px; 62 | background-color: rgb(85, 84, 84); 63 | margin-left: 2px; 64 | margin-right: 2px; 65 | } 66 | } 67 | 68 | .dock-item:hover { 69 | margin-bottom: 35px; 70 | 71 | &:hover { 72 | .tool-tip { 73 | opacity: 1; 74 | visibility: visible; 75 | } 76 | 77 | .dock-icon { 78 | height: 90px; 79 | width: 88px; 80 | } 81 | } 82 | } 83 | 84 | .distance-1 { 85 | margin-bottom: 22px !important; 86 | 87 | .dock-icon { 88 | height: 70px; 89 | width: 69px; 90 | } 91 | } 92 | 93 | .distance-1.no-point { 94 | margin-bottom: 25px !important; 95 | } 96 | 97 | .distance-2 { 98 | margin-bottom: 14px !important; 99 | 100 | .dock-icon { 101 | height: 62px; 102 | width: 61px; 103 | } 104 | } 105 | 106 | .hovered.no-point { 107 | margin-bottom: 40px !important; 108 | } 109 | 110 | .point { 111 | height: 4px; 112 | width: 4px; 113 | border-radius: 9999px; 114 | background-color: white; 115 | justify-self: center; 116 | margin-right: 2px; 117 | } 118 | -------------------------------------------------------------------------------- /weather-forecast/src/components/DropdownComponent/DropdownComponent.scss: -------------------------------------------------------------------------------- 1 | @import "../Page/Page.scss"; 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | .dropdown-menu { 9 | display: flex; 10 | flex-direction: column; 11 | gap: 3px; 12 | cursor: url("../../resources/images/cursor.png"), auto; 13 | border: 1px solid rgba(255, 255, 255, 0.335); 14 | border-radius: 8px; 15 | z-index: 20 !important; 16 | background-color: rgba(0, 0, 0, 0.19); 17 | position: absolute; 18 | width: 200px; 19 | padding: 11px 4px 8px 7px; 20 | left: 0; 21 | top: 30px; 22 | box-shadow: 0px 0px 3.35px black; 23 | 24 | .divider { 25 | margin: -2px 3px 1px 0px; 26 | height: 1px; 27 | background-color: rgba(255, 255, 255, 0.335); 28 | } 29 | 30 | .dropdown-item { 31 | padding: 0px 8px; 32 | font-size: 14.6px; 33 | font-family: "SF Regular"; 34 | transform: scaleY(1.03); 35 | border: none; 36 | border-radius: 4px; 37 | } 38 | } 39 | 40 | .dd-logo { 41 | left: -10px; 42 | } 43 | 44 | .dd-finder { 45 | left: -6px; 46 | } 47 | 48 | .dd-logo, 49 | .dd-finder, 50 | .dd-file, 51 | .dd-edit, 52 | .dd-view { 53 | width: 257px; 54 | } 55 | 56 | .dd-go, 57 | .dd-help { 58 | width: 255px; 59 | } 60 | 61 | .dd-windows { 62 | width: 292px; 63 | } 64 | 65 | .dropdown-menu::after { 66 | backdrop-filter: blur(25px); 67 | content: ""; 68 | display: block; 69 | height: 100%; 70 | width: 100%; 71 | z-index: -1 !important; 72 | position: absolute; 73 | left: 0; 74 | top: 0; 75 | } 76 | 77 | .selectable { 78 | 79 | &:hover { 80 | background-color: $systemColor; 81 | color: black !important; 82 | cursor: url("../../resources/images/pointing.png"), auto; 83 | } 84 | } 85 | 86 | .unselectable { 87 | cursor: url("../../resources/images/cursor.png"), auto; 88 | } 89 | -------------------------------------------------------------------------------- /weather-forecast/src/components/DropdownComponent/DropdownComponent.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import getDropdownContent from "../../utils/helpers/getDropdownContent"; 3 | import { store } from "../../App"; 4 | import "./DropdownComponent.scss"; 5 | import clearStorage from "../../utils/helpers/clearStorage"; 6 | 7 | export default function DropdownComponent() { 8 | const [state, dispatch] = useContext(store); 9 | const content = getDropdownContent(state); 10 | 11 | return ( 12 |
33 | {content.map((item, i) => { 34 | if (item.name === "divider") { 35 | return
; 36 | } 37 | 38 | return ( 39 |
{ 63 | if (item.name === "Restart") { 64 | localStorage.clear(); 65 | window.location.reload(); 66 | } 67 | 68 | dispatch({ 69 | type: "section/CHECK", 70 | payload: e.target, 71 | }); 72 | }} 73 | > 74 | {item.name} 75 |
76 | ); 77 | })} 78 |
79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /weather-forecast/src/components/ForecastList/ForecastList.scss: -------------------------------------------------------------------------------- 1 | .forecast-list { 2 | margin-top: 10px; 3 | padding: 12px 12px; 4 | border-radius: 34px; 5 | background-color: rgba(0, 0, 0, 0.3); 6 | 7 | .weekly { 8 | padding: 14px 32px 17px 32px; 9 | border: none; 10 | border-radius: 24px; 11 | display: flex; 12 | flex-direction: column; 13 | gap: 3px; 14 | background: rgb(52, 59, 87); 15 | background: linear-gradient( 16 | 0deg, 17 | rgba(52, 59, 87, 1) 0%, 18 | rgba(27, 32, 54, 1) 100% 19 | ); 20 | box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.45); 21 | } 22 | 23 | .day-container { 24 | display: flex; 25 | cursor: url("../../resources/images/pointing.png"), auto; 26 | 27 | * { 28 | pointer-events: none; 29 | } 30 | 31 | .day { 32 | width: 100px; 33 | } 34 | 35 | h3 { 36 | color: white; 37 | font-family: "SF Medium"; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /weather-forecast/src/components/ForecastList/ForecastList.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { store } from "../../App"; 3 | import sortedIntervalType from "../../types/sortedInterval"; 4 | import DailyCon from "../DailyCon/DailyCon"; 5 | import DailyTemps from "../DailyTemps/DailyTemps"; 6 | import "./ForecastList.scss"; 7 | 8 | export default function ForecastList() { 9 | const [state, dispatch] = useContext(store); 10 | 11 | const selectDay = (e: React.MouseEvent) => { 12 | const target = e.target as HTMLElement; 13 | 14 | dispatch({ 15 | type: 'select/SELECT', 16 | payload: target.id 17 | }); 18 | } 19 | 20 | return ( 21 |
22 |
23 | {state.weather.forecast !== 4 24 | ? state.weather.forecast.map( 25 | (dayArray: sortedIntervalType[], i: number) => { 26 | return ( 27 |
28 |

{dayArray[0].dateName}

29 | 30 | 31 |
32 | ); 33 | } 34 | ) 35 | : null} 36 |
37 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /weather-forecast/src/components/InputField/InputField.scss: -------------------------------------------------------------------------------- 1 | .input-container { 2 | position: relative; 3 | 4 | .search, 5 | .lock, 6 | .refresh { 7 | position: absolute; 8 | } 9 | 10 | .search { 11 | fill: #969696; 12 | height: 17px; 13 | left: 7.6em; 14 | top: 0.3em; 15 | cursor: url("../../resources/images/cursor.png"), auto; 16 | } 17 | 18 | .lock { 19 | fill: white; 20 | height: 15px; 21 | left: 8.6em; 22 | top: 0.35em; 23 | cursor: url("../../resources/images/cursor.png"), auto; 24 | } 25 | 26 | .refresh { 27 | height: 13px; 28 | right: 0.4em; 29 | top: 0.4em; 30 | cursor: url("../../resources/images/pointing.png"), auto; 31 | transition: 0.2s all; 32 | 33 | &:active { 34 | transform: scale(0.935); 35 | } 36 | } 37 | 38 | .inputField { 39 | width: 351.68px; 40 | padding-top: 2px; 41 | margin-left: 1px; 42 | border: none; 43 | overflow: hidden; 44 | padding-right: 2.5em; 45 | padding-bottom: 1px; 46 | border-radius: 4px; 47 | outline: none; 48 | height: 24px; 49 | border-top: 1px solid #79797b; 50 | background: rgb(92, 92, 94); 51 | background: linear-gradient( 52 | 360deg, 53 | rgba(92, 92, 94, 1) 0%, 54 | rgba(97, 97, 99, 1) 100% 55 | ); 56 | border-bottom: 1px solid #616163; 57 | padding-left: 12.1em; 58 | font-size: 13.2px; 59 | font-family: "SF Medium"; 60 | margin-bottom: 1px; 61 | letter-spacing: 0.015em; 62 | color: white; 63 | ::placeholder { 64 | color: #969696 !important; 65 | } 66 | } 67 | 68 | ::placeholder { 69 | color: #969696 !important; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /weather-forecast/src/components/InputField/InputField.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from "react"; 2 | import { store } from "../../App"; 3 | import inputWork from "../../utils/helpers/inputWork"; 4 | import { ReactComponent as Lock } from "../../resources/images/svg/lock.svg"; 5 | import { ReactComponent as Search } from "../../resources/images/svg/search.svg"; 6 | import "./InputField.scss"; 7 | 8 | export default function InputField() { 9 | const [state, dispatch] = useContext(store); 10 | 11 | return ( 12 |
13 | 14 | 15 | Refresh { 20 | dispatch({ 21 | type: 'loading/START' 22 | }) 23 | 24 | try { 25 | setTimeout(() => { 26 | inputWork(state.query).then((weather) => { 27 | dispatch({ 28 | type: "query/SUBMIT", 29 | payload: weather, 30 | }); 31 | }); 32 | }, 750) 33 | } catch { 34 | dispatch({ 35 | type: 'query/FAIL' 36 | }) 37 | } 38 | }} 39 | /> 40 | 41 | 49 | dispatch({ 50 | type: "query/SEARCH", 51 | payload: e.currentTarget.value, 52 | }) 53 | } 54 | onKeyUp={(e) => { 55 | if (e.key === "Enter") { 56 | dispatch({ 57 | type: 'loading/START' 58 | }); 59 | 60 | try { 61 | setTimeout(() => { 62 | inputWork(state.query).then((weather) => { 63 | dispatch({ 64 | type: "query/SUBMIT", 65 | payload: weather, 66 | }); 67 | }); 68 | }, 750) 69 | } catch { 70 | dispatch({ 71 | type: 'query/FAIL' 72 | }) 73 | } 74 | } 75 | }} 76 | /> 77 |
78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /weather-forecast/src/components/NavBar/NavBar.scss: -------------------------------------------------------------------------------- 1 | .nav-bar { 2 | width: 100vw; 3 | height: 29px; 4 | z-index: 12; 5 | position: absolute; 6 | top: 0; 7 | display: flex; 8 | background-color: rgba(0, 0, 0, 0.25); 9 | 10 | &::after { 11 | content: ""; 12 | position: absolute; 13 | inset: 0.01%; 14 | height: inherit; 15 | width: inherit; 16 | z-index: -1; 17 | background-color: black; 18 | opacity: 0.075; 19 | } 20 | 21 | .right { 22 | display: flex; 23 | align-items: center; 24 | font-size: 0.825em; 25 | font-family: "SF Regular"; 26 | position: absolute; 27 | right: 8px; 28 | top: 2px; 29 | 30 | .date { 31 | margin-right: 9px; 32 | } 33 | 34 | .settings { 35 | height: 26px; 36 | fill: white; 37 | padding: 5px 8px; 38 | margin-right: 9px; 39 | cursor: url("../../resources/images/pointing.png"), auto; 40 | 41 | path { 42 | pointer-events: none; 43 | } 44 | } 45 | } 46 | } 47 | 48 | .section { 49 | padding: 5px 8px; 50 | font-size: 13px; 51 | border: none; 52 | border-radius: 6px; 53 | font-family: "SF Regular"; 54 | color: white; 55 | cursor: url("../../resources/images/pointing.png"), auto; 56 | background-position: 50% 50%; 57 | transition: 2s all; 58 | position: relative; 59 | } 60 | 61 | .not-selected { 62 | background-color: ""; 63 | background-size: "5px 5px"; 64 | width: 5px; 65 | height: 5px; 66 | border-radius: 6px; 67 | transition: 0.175s all; 68 | position: absolute; 69 | top: 45%; 70 | left: 45% 71 | } 72 | 73 | .selected-logo { 74 | background-color: rgba(255, 255, 255, 0.25); 75 | width: 37px; 76 | border-radius: 6px; 77 | height: 29px; 78 | position: absolute; 79 | top: 0; 80 | left: 0; 81 | transition: 0.175s all; 82 | background-size: "29px 35px"; 83 | pointer-events: none; 84 | } 85 | 86 | .selected-finder { 87 | background-color: rgba(255, 255, 255, 0.25); 88 | width: 58px; 89 | border-radius: 6px; 90 | height: 29px; 91 | position: absolute; 92 | top: 0; 93 | left: 0; 94 | transition: 0.175s all; 95 | background-size: "29px 35px"; 96 | pointer-events: none; 97 | } 98 | 99 | .selected-file { 100 | background-color: rgba(255, 255, 255, 0.25); 101 | width: 38px; 102 | border-radius: 6px; 103 | height: 29px; 104 | position: absolute; 105 | top: 0; 106 | left: 0; 107 | transition: 0.175s all; 108 | background-size: "29px 35px"; 109 | pointer-events: none; 110 | } 111 | 112 | .selected-edit { 113 | background-color: rgba(255, 255, 255, 0.25); 114 | width: 39px; 115 | border-radius: 6px; 116 | height: 29px; 117 | position: absolute; 118 | top: 0; 119 | left: 0; 120 | transition: 0.175s all; 121 | background-size: "29px 35px"; 122 | pointer-events: none; 123 | } 124 | 125 | .selected-view { 126 | background-color: rgba(255, 255, 255, 0.25); 127 | width: 46px; 128 | border-radius: 6px; 129 | height: 29px; 130 | position: absolute; 131 | top: 0; 132 | left: 0; 133 | transition: 0.175s all; 134 | background-size: "29px 35px"; 135 | pointer-events: none; 136 | } 137 | 138 | .selected-go { 139 | background-color: rgba(255, 255, 255, 0.25); 140 | width: 33px; 141 | border-radius: 6px; 142 | height: 29px; 143 | position: absolute; 144 | top: 0; 145 | left: 0; 146 | transition: 0.175s all; 147 | background-size: "29px 35px"; 148 | pointer-events: none; 149 | } 150 | 151 | .selected-window { 152 | background-color: rgba(255, 255, 255, 0.25); 153 | width: 65px; 154 | border-radius: 6px; 155 | height: 29px; 156 | position: absolute; 157 | top: 0; 158 | left: 0; 159 | transition: 0.175s all; 160 | background-size: "29px 35px"; 161 | pointer-events: none; 162 | } 163 | 164 | .selected-help { 165 | background-color: rgba(255, 255, 255, 0.25); 166 | width: 44px; 167 | border-radius: 6px; 168 | height: 29px; 169 | position: absolute; 170 | top: 0; 171 | left: 0; 172 | transition: 0.175s all; 173 | background-size: "29px 35px"; 174 | pointer-events: none; 175 | } 176 | 177 | .finder { 178 | font-family: "SF Bold"; 179 | margin-right: 4px; 180 | } 181 | 182 | .logo { 183 | margin-left: 10px; 184 | padding: 5px 12px; 185 | margin-right: 8px; 186 | } 187 | 188 | 189 | .apple { 190 | height: 16px; 191 | } 192 | 193 | .not-selected { 194 | pointer-events: none; 195 | } -------------------------------------------------------------------------------- /weather-forecast/src/components/NavBar/NavBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from "react"; 2 | import { store } from "../../App"; 3 | import "./NavBar.scss"; 4 | import { ReactComponent as Settings } from "../../resources/images/svg/settings.svg"; 5 | import getDate from "../../utils/helpers/getDate"; 6 | import DropdownComponent from "../DropdownComponent/DropdownComponent"; 7 | import SettingsDropdown from "../SettingsDropdown/SettingsDropdown"; 8 | import { AnimatePresence, motion } from "framer-motion"; 9 | 10 | export default function () { 11 | const [state, dispatch] = useContext(store); 12 | 13 | useEffect(() => { 14 | dispatch({ 15 | type: "date/SET", 16 | }); 17 | }, []); 18 | 19 | setInterval(() => { 20 | dispatch({ 21 | type: "date/SET", 22 | }); 23 | }, 60000); 24 | 25 | const dispatchAction = (e: React.MouseEvent) => { 26 | const selectedSection = e.target as HTMLElement; 27 | if (selectedSection.classList.contains("dd")) { 28 | return; 29 | } 30 | dispatch({ 31 | type: "section/SELECT", 32 | payload: selectedSection.id, 33 | }); 34 | }; 35 | 36 | const toggleSettings = (e: React.MouseEvent) => { 37 | if (state.settings.open) { 38 | dispatch({ 39 | type: "settings/CLOSE", 40 | }); 41 | } else { 42 | dispatch({ 43 | type: "settings/OPEN", 44 | }); 45 | } 46 | }; 47 | 48 | return ( 49 | <> 50 |
51 |
52 | 65 | 66 |
67 | Finder 68 |
73 | {state.section === "finder" ? : null} 74 |
75 | 76 |
77 | File 78 |
83 | {state.section === "file" ? : null} 84 |
85 | 86 |
87 | Edit 88 |
93 | {state.section === "edit" ? : null} 94 |
95 | 96 |
97 | View 98 |
103 | {state.section === "view" ? : null} 104 |
105 |
106 | Go 107 |
112 | {state.section === "go" ? : null} 113 |
114 | 115 |
116 | Window 117 |
122 | {state.section === "windows" ? : null} 123 |
124 | 125 |
126 | Help 127 |
132 | {state.section === "help" ? : null} 133 |
134 | 135 |
136 |
137 | 138 | 139 |
140 |

{state.date[0]}

141 |

{state.date[1]}

142 |
143 |
144 | 145 | ); 146 | } 147 | -------------------------------------------------------------------------------- /weather-forecast/src/components/Page/Page.scss: -------------------------------------------------------------------------------- 1 | $systemColor: var(--user-color); 2 | 3 | .page { 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | height: 100vh; 9 | width: 100vw; 10 | -webkit-transition: background-image 0.3s ease-in-out; 11 | transition: background-image 0.3s ease-in-out; 12 | background-image: url("../../resources/images/catalina.jpg"); 13 | overflow: hidden; 14 | background-repeat: no-repeat; 15 | 16 | * { 17 | user-select: none; 18 | } 19 | 20 | .weather-window { 21 | height: 668px; 22 | width: 45.785vw; 23 | } 24 | 25 | .window { 26 | border: 1px solid #3f3f47; 27 | background-color: #1e1f23; 28 | border-radius: 4px; 29 | box-sizing: border-box; 30 | display: flex; 31 | transition: transform none; 32 | flex-direction: column; 33 | align-items: center; 34 | box-shadow: 0px 8px 15px #050505, 0px 12px 15px #050505, 35 | 0px 40px 70px #050505; 36 | margin-bottom: 10.35vh; 37 | opacity: 1; 38 | } 39 | 40 | .window-closed { 41 | border: 1px solid #3f3f47; 42 | background-color: #1e1f23; 43 | border-radius: 4px; 44 | transition: 0.4s all; 45 | transform: scale(0.01) !important; 46 | box-sizing: border-box; 47 | display: flex; 48 | flex-direction: column; 49 | align-items: center; 50 | box-shadow: 0px 8px 15px #050505, 0px 12px 15px #050505, 51 | 0px 40px 70px #050505; 52 | margin-bottom: 6.5vh; 53 | opacity: 0; 54 | } 55 | 56 | .window-minimized { 57 | border: 1px solid #3f3f47; 58 | background-color: #1e1f23; 59 | border-radius: 4px; 60 | transition: 0.7s all; 61 | margin-top: 900px; 62 | margin-left: 200px; 63 | transform: scale(0.01) !important; 64 | box-sizing: border-box; 65 | display: flex; 66 | flex-direction: column; 67 | align-items: center; 68 | box-shadow: 0px 8px 15px #050505, 0px 12px 15px #050505, 69 | 0px 40px 70px #050505; 70 | margin-bottom: 6.5vh; 71 | opacity: 0; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /weather-forecast/src/components/Page/Page.tsx: -------------------------------------------------------------------------------- 1 | import React, { Children, useContext, MouseEvent, useEffect } from "react"; 2 | import { store } from "../../App"; 3 | import checkDropdown from "../../utils/helpers/checkDropdown"; 4 | import checkSettings from "../../utils/helpers/checkSettings"; 5 | import openContextMenu from "../../utils/helpers/openContextMenu"; 6 | import wallpapers from "../../utils/helpers/wallpapers"; 7 | import BootUpWindow from "../BootUpWindow/BootUpWindow"; 8 | import "./Page.scss"; 9 | import updateSysColor from "../../utils/helpers/updateSysColor"; 10 | 11 | export default function Page({ children }: any) { 12 | const [state, dispatch] = useContext(store); 13 | 14 | const conditionalClick = (e: React.MouseEvent) => { 15 | const contextMenu = document.getElementById("context-menu"); 16 | // enable animation on left click after contextMenu has been displayed 17 | contextMenu!.style.transition = "0.2s all"; 18 | contextMenu!.style.opacity = "0"; 19 | contextMenu!.style.visibility = "hidden"; 20 | 21 | const isDropdown = checkDropdown(e); 22 | if (isDropdown === false) { 23 | dispatch({ 24 | type: "section/RESET", 25 | }); 26 | } 27 | 28 | const isSettings = checkSettings(e); 29 | if (isSettings === false) { 30 | dispatch({ 31 | type: "settings/CLOSE", 32 | }); 33 | } 34 | }; 35 | 36 | useEffect(() => { 37 | // check localStorage 38 | 39 | let color = localStorage.getItem("color"); 40 | let boot = JSON.parse(sessionStorage.getItem("boot")!); 41 | let wallpaper = JSON.parse(localStorage.getItem("wallpaper")!); 42 | if (boot !== null && boot !== undefined) { 43 | dispatch({ 44 | type: "state/BOOT", 45 | payload: boot.status, 46 | }); 47 | } 48 | if (wallpaper !== null && wallpaper !== undefined) { 49 | let updatedWallpaper = { 50 | open: false, 51 | name: wallpaper.name, 52 | surname: wallpaper.surname, 53 | preview: wallpaper.preview, 54 | src: wallpaper.src, 55 | }; 56 | 57 | dispatch({ 58 | type: "state/LOCAL", 59 | payload: updatedWallpaper, 60 | }); 61 | const page = document.getElementById("page"); 62 | const url = require(`../../resources/images/${wallpaper.surname}.jpg`); 63 | page!.style.backgroundImage = `url(${url})`; 64 | } 65 | if (color !== null && color !== undefined) { 66 | updateSysColor(color); 67 | 68 | dispatch({ 69 | type: "state/LOCALCOLOR", 70 | payload: color, 71 | }); 72 | } 73 | 74 | // preload images to avoid flashing white background when switching to a 75 | // wallpaper that hasn't yet been loaded 76 | wallpapers.forEach((picture) => { 77 | let img = new Image(); 78 | img.src = picture.src; 79 | }); 80 | }, []); 81 | 82 | useEffect(() => { 83 | // update localStorage 84 | 85 | if (state.booting) { 86 | return; 87 | } 88 | localStorage.setItem("color", state.settings.color); 89 | sessionStorage.setItem( 90 | "boot", 91 | JSON.stringify({ 92 | status: state.booting, 93 | }) 94 | ); 95 | localStorage.setItem("wallpaper", JSON.stringify(state.settings.wallpaper)); 96 | }, [state]); 97 | 98 | return ( 99 |
105 | {children} 106 | {state.booting ? : null} 107 |
108 | ); 109 | } 110 | -------------------------------------------------------------------------------- /weather-forecast/src/components/QueryBoard/QueryBoard.scss: -------------------------------------------------------------------------------- 1 | .query-board { 2 | width: 100%; 3 | display: flex; 4 | cursor: url("../../resources//images//grabbing.png"), auto; 5 | align-items: center; 6 | border-top: 1px solid #696971; 7 | border-bottom: 1px solid #141318; 8 | border-radius: 4px; 9 | border-bottom-left-radius: 0px; 10 | border-bottom-right-radius: 0px; 11 | background: rgb(43, 42, 47); 12 | background: linear-gradient( 13 | 360deg, 14 | rgba(43, 42, 47, 1) 0%, 15 | rgba(53, 53, 55, 1) 100% 16 | ); 17 | box-sizing: border-box; 18 | height: 2.55em; 19 | padding-left: 13px; 20 | 21 | .fullscreen-btn { 22 | margin-left: 0.4em; 23 | width: 41px !important; 24 | 25 | .fullscreen { 26 | height: 18px; 27 | margin-bottom: 1px; 28 | } 29 | } 30 | 31 | .dot { 32 | border-radius: 9999px; 33 | border: none; 34 | height: 0.83em; 35 | width: 0.81em; 36 | margin-right: 9px; 37 | cursor: url("../../resources/images/pointing.png"), auto; 38 | 39 | &:hover { 40 | box-shadow: inset 0 0 100px 100px rgba(255, 255, 255, 0.22); 41 | } 42 | } 43 | 44 | .red { 45 | background-color: rgb(255, 69, 58); 46 | } 47 | 48 | .yellow { 49 | background-color: rgb(255, 214, 10); 50 | } 51 | 52 | .green { 53 | background-color: rgb(48, 209, 88); 54 | margin-right: 0.9em; 55 | } 56 | 57 | .nav { 58 | height: 24px; 59 | width: 27px; 60 | background-color: #5b5c60; 61 | border-radius: 4px; 62 | border-top: 1px solid #747579; 63 | margin-right: 2px; 64 | display: flex; 65 | justify-content: center; 66 | align-items: center; 67 | transition: 0.25s all; 68 | cursor: url("../../resources/images/pointing.png"), auto; 69 | 70 | &:hover { 71 | background-color: #636466; 72 | } 73 | 74 | &:active { 75 | transform: scale(0.95); 76 | } 77 | 78 | .arrow { 79 | height: 14px; 80 | margin-bottom: 1px; 81 | } 82 | 83 | .forward { 84 | margin-left: 1px; 85 | fill: #848589; 86 | } 87 | 88 | .back { 89 | rotate: (180deg); 90 | fill: white; 91 | } 92 | 93 | .chat { 94 | height: 16px; 95 | width: 18px; 96 | margin-bottom: 1px; 97 | } 98 | 99 | .cat { 100 | height: 20px; 101 | margin-bottom: 1px; 102 | fill: white; 103 | } 104 | } 105 | 106 | .chat-btn { 107 | margin-left: 0.5625em; 108 | margin-right: 1.365em; 109 | width: 40px; 110 | } 111 | 112 | .github-btn { 113 | width: 40px; 114 | margin-right: 0.525em; 115 | } 116 | 117 | .forward-btn { 118 | cursor: not-allowed; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /weather-forecast/src/components/QueryBoard/QueryBoard.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./QueryBoard.scss"; 3 | import InputField from "../InputField/InputField"; 4 | import QueryBoardLinks from "../QueryBoardLinks/QueryBoardLinks"; 5 | import { useDisclosure } from "@chakra-ui/react"; 6 | import ChakraAlert from "../ChakraAlert/ChakraAlert"; 7 | import ChakraCookies from "../ChakraCookies/ChakraCookies"; 8 | 9 | export default function QueryBoard() { 10 | // Hooks for use in ChakraUI components 11 | const { isOpen, onOpen, onClose } = useDisclosure(); 12 | const cancelRef = React.useRef(null); 13 | 14 | return ( 15 |
16 | 17 | 18 | 19 | 20 | 21 | 37 | 38 | 39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /weather-forecast/src/components/QueryBoardLinks/QueryBoardLinks.scss: -------------------------------------------------------------------------------- 1 | .cookies-btn { 2 | margin-left: 10em; 3 | width: 41px !important; 4 | 5 | .cookies { 6 | height: 19px; 7 | margin-bottom: 1px; 8 | } 9 | } 10 | 11 | .fullscreen-btn { 12 | margin-left: 0.4em; 13 | width: 41px !important; 14 | 15 | .fullscreen { 16 | height: 18px; 17 | margin-bottom: 2px; 18 | } 19 | } 20 | 21 | .dot { 22 | border-radius: 9999px; 23 | border: none; 24 | height: 0.83em; 25 | width: 0.81em; 26 | margin-right: 9px; 27 | cursor: url("../../resources/images/pointing.png"), auto; 28 | transition: 0.2s all; 29 | display: flex; 30 | justify-content: center; 31 | align-items: center; 32 | 33 | .close, 34 | .minimize, 35 | .stretch { 36 | opacity: 0; 37 | transition: 0.15s all; 38 | pointer-events: none; 39 | } 40 | } 41 | 42 | .red { 43 | background-color: rgb(255, 69, 58); 44 | } 45 | 46 | .yellow { 47 | background-color: rgb(255, 214, 10); 48 | } 49 | 50 | .green { 51 | background-color: rgb(48, 209, 88); 52 | margin-right: 0.9em; 53 | } 54 | 55 | .nav { 56 | height: 24px; 57 | width: 27px; 58 | background-color: #5b5c60; 59 | border-radius: 4px; 60 | border-top: 1px solid #747579; 61 | margin-right: 2px; 62 | display: flex; 63 | justify-content: center; 64 | align-items: center; 65 | transition: 0.25s all; 66 | cursor: url("../../resources/images/pointing.png"), auto; 67 | &:hover { 68 | background-color: #636466; 69 | } 70 | 71 | .arrow { 72 | height: 14px; 73 | margin-bottom: 1px; 74 | } 75 | 76 | .forward { 77 | margin-left: 1px; 78 | fill: #848589; 79 | } 80 | 81 | .back { 82 | rotate: (180deg); 83 | fill: white; 84 | } 85 | 86 | .chat { 87 | height: 16px; 88 | width: 18px; 89 | margin-bottom: 1px; 90 | } 91 | 92 | .cat { 93 | height: 20px; 94 | margin-bottom: 1px; 95 | fill: white; 96 | } 97 | } 98 | 99 | .chat-btn { 100 | margin-left: 0.5625em; 101 | margin-right: 1.365em; 102 | width: 40px; 103 | } 104 | 105 | .github-btn { 106 | width: 40px; 107 | margin-right: 0.525em; 108 | } 109 | 110 | .forward-btn { 111 | cursor: not-allowed; 112 | } 113 | 114 | .dots { 115 | display: flex; 116 | } 117 | 118 | .dots:hover .dot { 119 | transform: scale(1.16); 120 | 121 | .close, 122 | .minimize, 123 | .stretch { 124 | opacity: 1; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /weather-forecast/src/components/QueryBoardLinks/QueryBoardLinks.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ReactComponent as Arrow } from "../../resources/images/svg/arrow.svg"; 3 | import { ReactComponent as Chat } from "../../resources/images/svg/chat.svg"; 4 | import { ReactComponent as Cat } from "../../resources/images/svg/githubcat.svg"; 5 | import { ReactComponent as Close } from "../../resources/images/svg/close.svg"; 6 | import { ReactComponent as Minimize } from "../../resources/images/svg/minimize.svg"; 7 | import { ReactComponent as Stretch } from "../../resources/images/svg/stretch.svg"; 8 | import "./QueryBoardLinks.scss"; 9 | import toggleVisibility from "../../utils/helpers/toggleVisibility"; 10 | import toggleMinimize from "../../utils/helpers/toggleMinimize"; 11 | 12 | export default function QueryBoardLinks() { 13 | return ( 14 | <> 15 |
16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 | 24 |
25 |
26 | 27 | 30 | 33 | 34 | 35 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /weather-forecast/src/components/SelectedDayForecast/SelectedDayForecast.scss: -------------------------------------------------------------------------------- 1 | @keyframes appear { 2 | 0% { 3 | opacity: 0; 4 | } 5 | 100% { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | 11 | .selected-day-forecast { 12 | border-radius: 26px; 13 | background: rgb(34, 39, 60); 14 | border-top-left-radius: 0px; 15 | border-top-right-radius: 0px; 16 | background: rgb(34, 39, 60); 17 | background: linear-gradient( 18 | 0deg, 19 | rgba(34, 39, 60, 1) 0%, 20 | rgba(18, 22, 47, 1) 100% 21 | ); 22 | box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.45); 23 | padding: 4px 20px 4px 20px; 24 | transition: 0.25s all; 25 | 26 | .intervals { 27 | display: flex; 28 | justify-content: center; 29 | gap: 16px; 30 | overflow: hidden; 31 | 32 | .interval-container { 33 | display: flex; 34 | padding: 10px 16px; 35 | flex-direction: column; 36 | justify-content: center; 37 | align-items: center; 38 | gap: 8px; 39 | transition: 0.25s all; 40 | animation: 0.4s ease-out 0s 1 appear; 41 | 42 | &:hover { 43 | transform: translateY(-10px) 44 | } 45 | 46 | .time { 47 | font-family: "SF Semibold"; 48 | color: rgba(255, 255, 255, 0.7); 49 | font-size: 18px; 50 | } 51 | 52 | .current-con { 53 | display: flex; 54 | justify-content: center; 55 | align-items: center; 56 | 57 | * { 58 | height: 40px; 59 | width: 40px; 60 | } 61 | } 62 | 63 | .temp { 64 | margin-top: 2px; 65 | font-family: "SF Semibold"; 66 | color: white; 67 | font-size: 18px; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /weather-forecast/src/components/SelectedDayForecast/SelectedDayForecast.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { store } from "../../App"; 3 | import "./SelectedDayForecast.scss"; 4 | // SVG Imports 5 | import { ReactComponent as Ash } from "../../resources/images/svg/weather/Ash.svg"; 6 | import { ReactComponent as Clear } from "../../resources/images/svg/weather/Clear.svg"; 7 | import { ReactComponent as ClearNight } from "../../resources/images/svg/weather/Clear_night.svg"; 8 | import { ReactComponent as Clouds } from "../../resources/images/svg/weather/Clouds.svg"; 9 | import { ReactComponent as Drizzle } from "../../resources/images/svg/weather/Drizzle.svg"; 10 | import { ReactComponent as Dust } from "../../resources/images/svg/weather/Dust.svg"; 11 | import { ReactComponent as Fog } from "../../resources/images/svg/weather/Fog.svg"; 12 | import { ReactComponent as Haze } from "../../resources/images/svg/weather/Haze.svg"; 13 | import { ReactComponent as Mist } from "../../resources/images/svg/weather/Mist.svg"; 14 | import { ReactComponent as Rain } from "../../resources/images/svg/weather/Rain.svg"; 15 | import { ReactComponent as Sand } from "../../resources/images/svg/weather/Sand.svg"; 16 | import { ReactComponent as Smoke } from "../../resources/images/svg/weather/Smoke.svg"; 17 | import { ReactComponent as Snow } from "../../resources/images/svg/weather/Snow.svg"; 18 | import { ReactComponent as Squall } from "../../resources/images/svg/weather/Squall.svg"; 19 | import { ReactComponent as Thunderstorm } from "../../resources/images/svg/weather/Thunderstorm.svg"; 20 | import { ReactComponent as Tornado } from "../../resources/images/svg/weather/Tornado.svg"; 21 | import nightTimes from "../../utils/helpers/nightTimes"; 22 | 23 | export default function SelectedDayForecast() { 24 | const [state, dispatch] = useContext(store); 25 | 26 | const returnCurrentCon = (weather: string, i: number) => { 27 | switch (weather) { 28 | case "Clear": 29 | if ( 30 | nightTimes.includes( 31 | state.selected 32 | ? state.selected[i].hours 33 | : state.weather.forecast[0][i].hours 34 | ) 35 | ) { 36 | return ; 37 | } 38 | return ; 39 | case "Clouds": 40 | return ; 41 | case "Ash": 42 | return ; 43 | case "Drizzle": 44 | return ; 45 | case "Dust": 46 | return ; 47 | case "Fog": 48 | return ; 49 | case "Snow": 50 | return ; 51 | case "Haze": 52 | return ; 53 | case "Mist": 54 | return ; 55 | case "Rain": 56 | return ; 57 | case "Sand": 58 | return ; 59 | case "Smoke": 60 | return ; 61 | case "Squall": 62 | return ; 63 | case "Thunderstorm": 64 | return ; 65 | case "Tornado": 66 | return ; 67 | } 68 | }; 69 | 70 | return ( 71 |
72 |
73 | {state.selected 74 | ? state.selected.map((interval: any, i: number) => { 75 | return ( 76 |
77 |

{interval.hours}

78 | 79 |
80 | {returnCurrentCon(interval.weather, i)} 81 |
82 | 83 |

{interval.temp}°

84 |
85 | ); 86 | }) 87 | : state.weather.forecast[0] 88 | ? state.weather.forecast[0].map((interval: any, i: number) => { 89 | return ( 90 |
91 |

{interval.hours}

92 | 93 |
94 | {returnCurrentCon(interval.weather, i)} 95 |
96 | 97 |

{interval.temp}°

98 |
99 | ); 100 | }) 101 | : null} 102 |
103 |
104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /weather-forecast/src/components/SelectedWeatherSlice/SelectedWeatherSlice.scss: -------------------------------------------------------------------------------- 1 | @keyframes appear { 2 | 0% { 3 | opacity: 0; 4 | } 5 | 100% { 6 | opacity: 1; 7 | } 8 | } 9 | 10 | 11 | .selected-weather-slice { 12 | width: inherit; 13 | border: none; 14 | border-radius: 26px; 15 | background: rgb(18, 22, 47); 16 | border-bottom-left-radius: 0px; 17 | border-bottom-right-radius: 0px; 18 | background: rgb(18, 22, 47); 19 | background: linear-gradient( 20 | 0deg, 21 | rgba(18, 22, 47, 1) 0%, 22 | rgba(0, 5, 34, 1) 100% 23 | ); 24 | box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.45); 25 | padding: 22px 34px 26px 34px; 26 | transition: 0.25s all; 27 | 28 | .top { 29 | display: flex; 30 | width: 100%; 31 | justify-content: space-between; 32 | animation: 0.4s ease-out 0s 1 appear; 33 | 34 | .location-container { 35 | display: flex; 36 | gap: 12px; 37 | align-items: center; 38 | 39 | .text { 40 | animation: 0.4s ease-out 0s 1 appear; 41 | } 42 | 43 | h3 { 44 | color: white; 45 | font-size: 26px; 46 | font-family: "SF Bold"; 47 | cursor: url("../../resources/images/pointing.png"), auto; 48 | } 49 | 50 | .location { 51 | height: 22px; 52 | cursor: pointer; 53 | transition: 0.25s all; 54 | cursor: url("../../resources/images/pointing.png"), auto; 55 | 56 | &:hover { 57 | transform: scale(1.05); 58 | } 59 | } 60 | } 61 | 62 | .current-con { 63 | padding-top: 2px; 64 | 65 | * { 66 | height: 36px; 67 | width: 36px; 68 | fill: white; 69 | transition: 0.25s all; 70 | cursor: url("../../resources/images/pointing.png"), auto; 71 | 72 | &:hover { 73 | transform: scale(1.03); 74 | } 75 | } 76 | } 77 | } 78 | 79 | .details { 80 | display: flex; 81 | justify-content: space-between; 82 | animation: 0.4s ease-out 0s 1 appear; 83 | 84 | h1 { 85 | color: white; 86 | font-size: 72px; 87 | font-family: "SF Light"; 88 | margin-top: -22px; 89 | } 90 | 91 | .data { 92 | margin-top: 12px; 93 | display: flex; 94 | flex-direction: column; 95 | justify-content: flex-end; 96 | text-align: end; 97 | animation: 0.4s ease-out 0s 1 appear; 98 | 99 | h3 { 100 | color: white; 101 | font-size: 17px; 102 | font-family: "SF Medium"; 103 | } 104 | 105 | .con { 106 | margin-bottom: 4px; 107 | font-size: 21px; 108 | } 109 | 110 | .humidity-container, 111 | .rain-container, 112 | .temp-container { 113 | margin-top: -2px; 114 | padding-top: 0; 115 | padding-bottom: 0; 116 | display: flex; 117 | align-items: center; 118 | gap: 4px; 119 | text-align: end; 120 | justify-content: flex-end; 121 | 122 | h3 { 123 | font-family: "SF Regular"; 124 | } 125 | 126 | .icon { 127 | height: 20px; 128 | fill: white; 129 | margin-bottom: 1px; 130 | } 131 | } 132 | 133 | .temp-container { 134 | h3.high { 135 | margin-right: 6px; 136 | } 137 | } 138 | } 139 | } 140 | } 141 | 142 | .temps { 143 | h3 { 144 | font-family: "SF Light"; 145 | margin-top: -2px; 146 | margin-bottom: 4px; 147 | animation: 0.4s ease-out 0s 1 appear; 148 | } 149 | 150 | h1 { 151 | animation: 0.4s ease-out 0s 1 appear; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /weather-forecast/src/components/SelectedWeatherSlice/SelectedWeatherSlice.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { store } from "../../App"; 3 | import "./SelectedWeatherSlice.scss"; 4 | // SVG Imports 5 | import Search from "../../resources/images/svg/search.svg"; 6 | import { ReactComponent as Location } from "../../resources/images/svg/location.svg"; 7 | import { ReactComponent as Ash } from "../../resources/images/svg/weather/Ash.svg"; 8 | import { ReactComponent as Clear } from "../../resources/images/svg/weather/Clear.svg"; 9 | import { ReactComponent as ClearNight } from "../../resources/images/svg/weather/Clear_night.svg"; 10 | import { ReactComponent as Clouds } from "../../resources/images/svg/weather/Clouds.svg"; 11 | import { ReactComponent as Drizzle } from "../../resources/images/svg/weather/Drizzle.svg"; 12 | import { ReactComponent as Dust } from "../../resources/images/svg/weather/Dust.svg"; 13 | import { ReactComponent as Fog } from "../../resources/images/svg/weather/Fog.svg"; 14 | import { ReactComponent as Haze } from "../../resources/images/svg/weather/Haze.svg"; 15 | import { ReactComponent as Mist } from "../../resources/images/svg/weather/Mist.svg"; 16 | import { ReactComponent as Rain } from "../../resources/images/svg/weather/Rain.svg"; 17 | import { ReactComponent as Sand } from "../../resources/images/svg/weather/Sand.svg"; 18 | import { ReactComponent as Smoke } from "../../resources/images/svg/weather/Smoke.svg"; 19 | import { ReactComponent as Snow } from "../../resources/images/svg/weather/Snow.svg"; 20 | import { ReactComponent as Squall } from "../../resources/images/svg/weather/Squall.svg"; 21 | import { ReactComponent as Thunderstorm } from "../../resources/images/svg/weather/Thunderstorm.svg"; 22 | import { ReactComponent as Tornado } from "../../resources/images/svg/weather/Tornado.svg"; 23 | import { ReactComponent as Humidity } from "../../resources/images/svg/weather/humidity.svg"; 24 | import { ReactComponent as Wind } from "../../resources/images/svg/weather/wind.svg"; 25 | import { ReactComponent as Highest } from "../../resources/images/svg/weather/highest.svg"; 26 | import { ReactComponent as Lowest } from "../../resources/images/svg/weather/lowest.svg"; 27 | import { ReactComponent as Raindrops } from "../../resources/images/svg/weather/raindrops.svg"; 28 | import nightTimes from "../../utils/helpers/nightTimes"; 29 | 30 | export default function SelectedWeatherSlice() { 31 | const [state, dispatch] = useContext(store); 32 | 33 | const returnCurrentCon = (weather: string) => { 34 | switch (weather) { 35 | case "Clear": 36 | if ( 37 | nightTimes.includes( 38 | state.selected 39 | ? state.selected[0].hours 40 | : state.weather.current.hours 41 | ) 42 | ) { 43 | return ; 44 | } 45 | return ; 46 | case "Clouds": 47 | return ; 48 | case "Ash": 49 | return ; 50 | case "Drizzle": 51 | return ; 52 | case "Dust": 53 | return ; 54 | case "Fog": 55 | return ; 56 | case "Snow": 57 | return ; 58 | case "Haze": 59 | return ; 60 | case "Mist": 61 | return ; 62 | case "Rain": 63 | return ; 64 | case "Sand": 65 | return ; 66 | case "Smoke": 67 | return ; 68 | case "Squall": 69 | return ; 70 | case "Thunderstorm": 71 | return ; 72 | case "Tornado": 73 | return ; 74 | } 75 | }; 76 | 77 | return ( 78 |
79 | 95 | 96 |
97 |
98 |

{state.date[1]}

99 |

100 | {state.selected 101 | ? state.selected[0].temp 102 | : state.weather.current.temp} 103 | ° 104 |

105 |
106 | 107 |
108 |

109 | {state.selected 110 | ? state.selected[0].weather 111 | : state.weather.current.weather} 112 |

113 | 114 |
115 |

116 | Humidity:{" "} 117 | {state.selected 118 | ? state.selected[0].humidity 119 | : state.weather.current.humidity} 120 | % 121 |

122 |
123 | 124 |
125 |

126 | Rain Probability:{" "} 127 | {state.selected 128 | ? state.selected[0].rain 129 | : state.weather.current.rain} 130 | % 131 |

132 |
133 | 134 |
135 |

136 | Wind Speed:{" "} 137 | {state.selected 138 | ? state.selected[0].wind 139 | : state.weather.current.wind} 140 | km/h 141 |

142 |
143 |
144 |
145 |
146 | ); 147 | } 148 | -------------------------------------------------------------------------------- /weather-forecast/src/components/WallpaperMenu/WallpaperMenu.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import Draggable from "react-draggable"; 3 | import { store } from "../../App"; 4 | import "./WallpaperMenu.scss"; 5 | import { ReactComponent as Close } from "../../resources/images/svg/close.svg"; 6 | import { ReactComponent as Minimize } from "../../resources/images/svg/minimize.svg"; 7 | import { ReactComponent as Stretch } from "../../resources/images/svg/stretch.svg"; 8 | import wallpapers from "../../utils/helpers/wallpapers"; 9 | import toggleWallpaperVis from "../../utils/helpers/toggleWallpaperVis"; 10 | import toggleWallpaperMin from "../../utils/helpers/toggleWallpaperMin"; 11 | import wallpaperObjectType from "../../types/wallpaperObjectType"; 12 | import returnColor from "../../utils/helpers/returnColor"; 13 | import toggleBorder from "../../utils/helpers/toggleBorder"; 14 | 15 | export default function WallpaperMenu() { 16 | const [state, dispatch] = useContext(store); 17 | 18 | const changeWallpaper = (wallpaper: any) => { 19 | dispatch({ 20 | type: "wallpaper/CHANGE", 21 | payload: { 22 | name: wallpaper.name, 23 | surname: wallpaper.surname, 24 | preview: `../../resources/images/preview_${wallpaper.surname}.jpg`, 25 | src: `../../resources/images/${wallpaper.surname}.jpg`, 26 | }, 27 | }); 28 | 29 | const page = document.getElementById("page"); 30 | const url = require(`../../resources/images/${wallpaper.surname}.jpg`); 31 | page!.style.backgroundImage = `url(${url})`; 32 | }; 33 | 34 | return ( 35 | { 38 | if (e.target.id !== "wallpaper-handle") { 39 | return false; 40 | } 41 | }} 42 | > 43 | {state.settings.wallpaper.open ? ( 44 |
45 |
46 |
47 |
48 | 49 |
50 |
51 | 52 |
53 |
54 | 55 |
56 |
57 | Wallpaper 58 |
59 | 60 |
61 |
62 | Current Wallpaper 71 |

{state.settings.wallpaper.name}

72 |

Dynamic Wallpaper

73 | 74 |
75 | 76 |

Change dark/light mode as wallpapers change

77 |
78 |
79 | 80 |
81 |

Dynamic Wallpapers

82 | 83 |
84 | {wallpapers.map( 85 | (wallpaperObject: wallpaperObjectType, i: number) => { 86 | return ( 87 |
88 | wallpaper changeWallpaper(wallpaperObject)} 95 | style={{ 96 | borderColor: returnColor(state.settings.color), 97 | }} 98 | /> 99 |

{wallpaperObject.name}

100 |
101 | ); 102 | } 103 | )} 104 |
105 |
106 |
107 |
108 | ) : ( 109 |
110 | )} 111 | 112 | ); 113 | } 114 | -------------------------------------------------------------------------------- /weather-forecast/src/components/WeatherBoard/WeatherBoard.scss: -------------------------------------------------------------------------------- 1 | h3 { 2 | color: white; 3 | font-family: Helvetica, sans-serif; 4 | } 5 | 6 | .weather-board { 7 | padding: 17px 22px 17px 22px; 8 | width: 100%; 9 | height: 100%; 10 | overflow: hidden; 11 | 12 | .loading { 13 | margin-top: 125px !important; 14 | margin-bottom: 268px !important; 15 | } 16 | 17 | .fail { 18 | font-size: 40px; 19 | color: white; 20 | font-family: "SF Bold"; 21 | position: absolute; 22 | margin-left: auto; 23 | margin-right: auto; 24 | margin-top: 200px; 25 | left: 0; 26 | right: 0; 27 | text-align: center; 28 | } 29 | 30 | .fail-text { 31 | font-size: 16px; 32 | color: #bebebe; 33 | font-family: "SF Bold"; 34 | position: absolute; 35 | margin-left: auto; 36 | margin-top: 270px; 37 | margin-right: auto; 38 | left: 0; 39 | right: 0; 40 | text-align: center; 41 | } 42 | 43 | .daily { 44 | padding: 12px 13px; 45 | border-radius: 34px; 46 | background-color: rgba(0, 0, 0, 0.3); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /weather-forecast/src/components/WeatherBoard/WeatherBoard.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from "react"; 2 | import { store } from "../../App"; 3 | import "./WeatherBoard.scss"; 4 | import positionWork from "../../utils/helpers/positionWork"; 5 | import SelectedWeatherSlice from "../SelectedWeatherSlice/SelectedWeatherSlice"; 6 | import SelectedDayForecast from "../SelectedDayForecast/SelectedDayForecast"; 7 | import ForecastList from "../ForecastList/ForecastList"; 8 | import { ReactComponent as Loading } from "../../resources/images/svg/loading.svg"; 9 | import { AnimatePresence, motion } from "framer-motion"; 10 | import uuid from "react-uuid"; 11 | 12 | export default function WeatherBoard() { 13 | const [state, dispatch] = useContext(store); 14 | 15 | useEffect(() => { 16 | // use async logic with .then() before dispatching to keep the reducer pure and synchronous 17 | positionWork().then((weather) => { 18 | dispatch({ 19 | type: "query/GEO", 20 | payload: weather, 21 | }); 22 | }); 23 | }, []); 24 | 25 | const animations = { 26 | initial: { opacity: 0, y: 20 }, 27 | animate: { opacity: 1, y: 0 }, 28 | exit: { opacity: 0 }, 29 | }; 30 | 31 | return ( 32 |
33 | 34 | {state.loading ? ( 35 | 42 | 43 | 44 | ) : state.weather.current === undefined ? ( 45 | 53 |

City not found

54 |

55 | Sorry, we couldn't find any weather data for the city you 56 | provided. 57 |
58 | Please try another one. 59 |

60 |
61 | ) : ( 62 | <> 63 | 72 | 73 | 74 | 75 | 76 | 84 | 85 | 86 | 87 | )} 88 |
89 |
90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /weather-forecast/src/components/Window/Window.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/components/Window/Window.scss -------------------------------------------------------------------------------- /weather-forecast/src/components/Window/Window.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./Window.scss"; 3 | 4 | export default function Window({ children }: any) { 5 | 6 | return ( 7 |
8 | {children} 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /weather-forecast/src/custom.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg" { 2 | const src: string; 3 | export default src; 4 | } -------------------------------------------------------------------------------- /weather-forecast/src/framer-motion.d.ts: -------------------------------------------------------------------------------- 1 | declare module "framer-motion/dist/framer-motion" { 2 | export * from "framer-motion"; 3 | } 4 | -------------------------------------------------------------------------------- /weather-forecast/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --user-color: #0a85ff; 3 | } 4 | 5 | body { 6 | margin: 0; 7 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 8 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 9 | sans-serif; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | cursor: url("./resources//images/cursor.png"), auto; 13 | overflow-x: hidden; 14 | overflow-y: hidden !important; 15 | } 16 | 17 | code { 18 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 19 | monospace; 20 | } 21 | 22 | @font-face { 23 | font-family: "SF Regular"; 24 | src: local("sfregular"), 25 | url(./resources/fonts/sfregular.otf) format("opentype"); 26 | } 27 | 28 | @font-face { 29 | font-family: "SF Medium"; 30 | src: local("sfmedium"), url(./resources/fonts/sfmedium.otf) format("opentype"); 31 | } 32 | 33 | @font-face { 34 | font-family: "SF Bold"; 35 | src: local("sfbold"), url(./resources/fonts/sfbold.otf) format("opentype"); 36 | } 37 | 38 | @font-face { 39 | font-family: "SF Semibold"; 40 | src: local("sfsemibold"), 41 | url(./resources/fonts/sfsemibold.otf) format("opentype"); 42 | } 43 | 44 | @font-face { 45 | font-family: "SF Light"; 46 | src: local("sflight"), url(./resources/fonts/sflight.otf) format("opentype"); 47 | } 48 | -------------------------------------------------------------------------------- /weather-forecast/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | const root = ReactDOM.createRoot( 8 | document.getElementById("root") as HTMLElement 9 | ); 10 | root.render( 11 | 12 | 13 | 14 | ); 15 | 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /weather-forecast/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare module '*.mp3'; 3 | -------------------------------------------------------------------------------- /weather-forecast/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/audio/bootsound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/audio/bootsound.mp3 -------------------------------------------------------------------------------- /weather-forecast/src/resources/audio/bootsound.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/audio/bootsound.wav -------------------------------------------------------------------------------- /weather-forecast/src/resources/fonts/sfbold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/fonts/sfbold.otf -------------------------------------------------------------------------------- /weather-forecast/src/resources/fonts/sflight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/fonts/sflight.otf -------------------------------------------------------------------------------- /weather-forecast/src/resources/fonts/sfmedium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/fonts/sfmedium.otf -------------------------------------------------------------------------------- /weather-forecast/src/resources/fonts/sfregular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/fonts/sfregular.otf -------------------------------------------------------------------------------- /weather-forecast/src/resources/fonts/sfsemibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/fonts/sfsemibold.otf -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/airdrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/airdrop.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/apple.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/applelogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/applelogo.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/bigsur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/bigsur.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/bigsurgraphic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/bigsurgraphic.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/catalina.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/catalina.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/catalina_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/catalina_day.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/cookies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/cookies.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/cursor.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/default@2x.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/dome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/dome.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/fullscreen.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/grabbing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/grabbing.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/iridescence.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/iridescence.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/lake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/lake.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/linkedin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/linkedin.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/mojave.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/mojave.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/monterey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/monterey.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/paperplane.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/paperplane.PNG -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/peak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/peak.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/pointing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/pointing.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_bigsur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_bigsur.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_bigsurgraphic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_bigsurgraphic.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_bigsurgraphic.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_bigsurgraphic.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_catalina.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_catalina.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_catalina.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_catalina.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_catalina.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_catalina.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_dome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_dome.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_dome.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_dome.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_iridescence.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_iridescence.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_iridescence.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_iridescence.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_lake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_lake.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_lake.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_lake.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_mojave.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_mojave.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_mojave.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_mojave.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_monterey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_monterey.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_monterey.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_monterey.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_peak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_peak.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_peak.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_peak.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_solargrad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_solargrad.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_solargrad.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_solargrad.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_thedesert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_thedesert.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_thedesert.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_thedesert.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/preview_ventura.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/preview_ventura.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/refresh.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/showcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/showcase.gif -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/solargrad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/solargrad.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/airdrop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/animations.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/chat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/githubcat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/loading.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/loading2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/location.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/lock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/minimize.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/notch.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/stretch.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/tick.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Ash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Clear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Clear_day.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Clear_night.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Clouds.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Drizzle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Dust.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Fog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Haze.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Mist.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Rain.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Sand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ic_fluent_weather_duststorm_48_filled 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Smoke.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Snow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Squall.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Thunderstorm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/Tornado.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/highest.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/humidity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/location.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/lowest.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/raindrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/svg/weather/raindrop.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/raindrops.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 12 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/svg/weather/wind.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/thedesert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/thedesert.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/ventura.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/ventura.jpg -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/applemusic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/applemusic.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/arcade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/arcade.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/calculator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/calculator.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/calculator.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/calculator.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/calendar.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/calendar.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/calendar.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/discord.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/finder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/finder.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/finder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/finder.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/github.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/github.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/github.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/netflix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/netflix.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/photos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/photos.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/scalable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/scalable.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/spotify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/spotify.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/twitter.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/vscode.png -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/vscode.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/vscode.webp -------------------------------------------------------------------------------- /weather-forecast/src/resources/images/webp/weather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianlucajahn/macOS-react/849af2a3a8e1dc133c6fa3573a1c46cf429b16ea/weather-forecast/src/resources/images/webp/weather.png -------------------------------------------------------------------------------- /weather-forecast/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /weather-forecast/src/types/DropdownItemType.ts: -------------------------------------------------------------------------------- 1 | interface DropdownItemType { 2 | name: string; 3 | available: boolean; 4 | } 5 | 6 | export default DropdownItemType; -------------------------------------------------------------------------------- /weather-forecast/src/types/action.ts: -------------------------------------------------------------------------------- 1 | export type actionType = { 2 | type: string; 3 | }; 4 | -------------------------------------------------------------------------------- /weather-forecast/src/types/intervalType.ts: -------------------------------------------------------------------------------- 1 | interface intervalType { 2 | clouds: { 3 | all: number; 4 | }; 5 | dt: number; 6 | dt_txt: string; 7 | main: { 8 | feels_like: number; 9 | grnd_level: number; 10 | humidity: number; 11 | pressure: number; 12 | sea_level: number; 13 | temp: number; 14 | temp_kf: number; 15 | temp_max: number; 16 | temp_min: number; 17 | }; 18 | pop: number; 19 | sys: any; 20 | visibility: number; 21 | weather: any; 22 | wind: any; 23 | day?: any; 24 | rain?: any; 25 | } 26 | 27 | export default intervalType; 28 | -------------------------------------------------------------------------------- /weather-forecast/src/types/query.ts: -------------------------------------------------------------------------------- 1 | export type queryType = string; 2 | -------------------------------------------------------------------------------- /weather-forecast/src/types/sortedInterval.ts: -------------------------------------------------------------------------------- 1 | interface sortedIntervalType { 2 | date: any; 3 | dateName: string; 4 | dateNum: number; 5 | feelsTemp: number; 6 | hours: string; 7 | humidity: number; 8 | maxTemp: number; 9 | minTemp: number; 10 | rain: number; 11 | temp: number; 12 | time: number; 13 | weather: string; 14 | wind: number; 15 | } 16 | 17 | export default sortedIntervalType; 18 | -------------------------------------------------------------------------------- /weather-forecast/src/types/store.ts: -------------------------------------------------------------------------------- 1 | import sortedIntervalType from "./sortedInterval"; 2 | 3 | interface storeType { 4 | weather: { 5 | current: any; 6 | forecast: any; 7 | }; 8 | query: string; 9 | loading: boolean; 10 | section: string; 11 | dockItem: number | undefined; 12 | date: any; 13 | selected: undefined | sortedIntervalType[]; 14 | failed: boolean; 15 | booting: boolean; 16 | onTop: string; 17 | soundPlayed: boolean; 18 | settings: { 19 | open: boolean; 20 | animations: boolean; 21 | color: string; 22 | notch: boolean; 23 | airdrop: boolean; 24 | wallpaper: { 25 | open: boolean; 26 | src: string; 27 | preview: string; 28 | name: string; 29 | surname: string; 30 | }; 31 | }; 32 | } 33 | 34 | export default storeType; 35 | -------------------------------------------------------------------------------- /weather-forecast/src/types/wallpaperObjectType.ts: -------------------------------------------------------------------------------- 1 | interface wallpaperObjectType { 2 | name: string; 3 | surname: string; 4 | preview: string; 5 | src: string; 6 | } 7 | 8 | export default wallpaperObjectType; -------------------------------------------------------------------------------- /weather-forecast/src/types/weather.ts: -------------------------------------------------------------------------------- 1 | type weatherType = { 2 | current: any; 3 | forecast: any; 4 | }; 5 | 6 | export default weatherType; 7 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/checkDropdown.ts: -------------------------------------------------------------------------------- 1 | const checkDropdown = (e: React.MouseEvent) => { 2 | const target = e.target as HTMLElement; 3 | 4 | if (target.classList.contains("dd")) { 5 | return true; 6 | } else if (target.classList.contains("section")) { 7 | return true; 8 | } else { 9 | return false; 10 | } 11 | }; 12 | 13 | export default checkDropdown; 14 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/checkSettings.ts: -------------------------------------------------------------------------------- 1 | const checkSettings = (e: React.MouseEvent) => { 2 | const target = e.target as HTMLElement; 3 | 4 | if (target.classList.contains("set")) { 5 | return true; 6 | } else { 7 | return false; 8 | } 9 | }; 10 | 11 | export default checkSettings; 12 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/clearStorage.ts: -------------------------------------------------------------------------------- 1 | const clearStorage = () => { 2 | localStorage.clear(); 3 | sessionStorage.clear(); 4 | } 5 | 6 | export default clearStorage; -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/getDate.ts: -------------------------------------------------------------------------------- 1 | const getDate = () => { 2 | const currentDate = new Date(); 3 | 4 | // create date string 5 | const dayNum = currentDate.getDay(); 6 | const monthNum = currentDate.getMonth(); 7 | const dateNum = currentDate.getDate(); 8 | const date = dateNum.toString(); 9 | 10 | let day = ""; 11 | switch (dayNum) { 12 | case 1: 13 | day = "Mon"; 14 | break; 15 | case 2: 16 | day = "Tue"; 17 | break; 18 | case 3: 19 | day = "Wed"; 20 | break; 21 | case 4: 22 | day = "Thu"; 23 | break; 24 | case 5: 25 | day = "Fri"; 26 | break; 27 | case 6: 28 | day = "Sat"; 29 | break; 30 | case 0: 31 | day = "Sun"; 32 | break; 33 | } 34 | 35 | let month = ""; 36 | switch (monthNum) { 37 | case 0: 38 | month = "Jan"; 39 | break; 40 | case 1: 41 | month = "Feb"; 42 | break; 43 | case 2: 44 | month = "Mar"; 45 | break; 46 | case 3: 47 | month = "Apr"; 48 | break; 49 | case 4: 50 | month = "May"; 51 | break; 52 | case 5: 53 | month = "Jun"; 54 | break; 55 | case 6: 56 | month = "Jul"; 57 | break; 58 | case 7: 59 | month = "Aug"; 60 | break; 61 | case 8: 62 | month = "Sep"; 63 | break; 64 | case 9: 65 | month = "Oct"; 66 | break; 67 | case 10: 68 | month = "Nov"; 69 | break; 70 | case 11: 71 | month = "Dec"; 72 | break; 73 | } 74 | 75 | // finish work on date 76 | const dateOutput = `${day} ${month} ${date}`; 77 | 78 | // create time string 79 | let hours: string; 80 | const hoursNum = currentDate.getHours(); 81 | if (hoursNum < 10) { 82 | let hourString = hoursNum.toString(); 83 | hours = `0${hourString}`; 84 | } else { 85 | if (hoursNum > 12) { 86 | hours = (hoursNum - 12).toString(); 87 | } else { 88 | hours = hoursNum.toString(); 89 | } 90 | } 91 | 92 | let minutes: string; 93 | const minutesNum = currentDate.getMinutes(); 94 | if (minutesNum === 0) { 95 | minutes = "00"; 96 | } else if (minutesNum === 1) { 97 | minutes = "01"; 98 | } else if (minutesNum === 2) { 99 | minutes = "02"; 100 | } else if (minutesNum === 3) { 101 | minutes = "03"; 102 | } else if (minutesNum === 4) { 103 | minutes = "04"; 104 | } else if (minutesNum === 5) { 105 | minutes = "05"; 106 | } else if (minutesNum === 6) { 107 | minutes = "06"; 108 | } else if (minutesNum === 7) { 109 | minutes = "07"; 110 | } else if (minutesNum === 8) { 111 | minutes = "08"; 112 | } else if (minutesNum === 9) { 113 | minutes = "09"; 114 | } else { 115 | minutes = minutesNum.toString(); 116 | } 117 | 118 | let daytime = ""; 119 | if (hoursNum > 12) { 120 | daytime = "PM"; 121 | } else { 122 | daytime = "AM"; 123 | } 124 | 125 | // finish work on time (as always (pun intended)) 126 | const timeOutput = `${hours}:${minutes} ${daytime}`; 127 | 128 | // return date and time 129 | return [dateOutput, timeOutput]; 130 | }; 131 | 132 | export default getDate; 133 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/getPosition.ts: -------------------------------------------------------------------------------- 1 | import intervalType from "../../types/intervalType"; 2 | import sortedIntervalType from "../../types/sortedInterval"; 3 | import apiKey from "../keys/key"; 4 | 5 | function getPosition() { 6 | return new Promise((res, rej) => { 7 | navigator.geolocation.getCurrentPosition(res, rej); 8 | }); 9 | } 10 | 11 | export default getPosition; 12 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/identifyDockItem.ts: -------------------------------------------------------------------------------- 1 | const identifyDockItem = (target: any) => { 2 | // return if the dock bar is hovered 3 | if (target.classList.contains("dock")) { 4 | return; 5 | } 6 | 7 | if (!target.classList.contains("dock-item")) { 8 | target = target.parentNode; 9 | } 10 | 11 | const id = parseInt(target.id); 12 | return id; 13 | }; 14 | 15 | export default identifyDockItem; 16 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/inputWork.ts: -------------------------------------------------------------------------------- 1 | import intervalType from "../../types/intervalType"; 2 | import sortedIntervalType from "../../types/sortedInterval"; 3 | import apiKey from "../keys/key"; 4 | 5 | const inputWork = async function (query: string) { 6 | // fetch current weather data 7 | const currentResponse = await fetch( 8 | `https://api.openweathermap.org/data/2.5/weather?q=${query}&appid=${apiKey}&units=metric` 9 | ); 10 | let current: any = 0; 11 | try { 12 | current = await currentResponse.json(); 13 | } catch(err) { 14 | return; 15 | } 16 | 17 | console.log(current); 18 | if (current.cod === "404" || current.cod === "400") { 19 | const newWeather = { 20 | current: undefined, 21 | forecast: undefined 22 | } 23 | return newWeather; 24 | } 25 | 26 | const currentObject = { 27 | country: current.sys.country, 28 | town: current.name, 29 | wind: current.wind.speed, 30 | weather: current.weather[0].main, 31 | temp: Math.round(current.main.temp), 32 | feelsTemp: Math.round(current.main.feels_like), 33 | hours: 34 | new Date(current.dt * 1000).getHours() > 12 35 | ? new Date(current.dt * 1000).getHours() - 12 + "PM" 36 | : new Date(current.dt * 1000).getHours() + "AM", 37 | maxTemp: Math.round(current.main.temp_max), 38 | minTemp: Math.round(current.main.temp_min), 39 | humidity: current.main.humidity, 40 | rain: current.clouds["all"], 41 | }; 42 | 43 | let location = [current.coord.lat.toString(), current.coord.lon.toString()]; 44 | console.log(location); 45 | // fetch 5 day forecast data for selected city and save it in forecastData state 46 | const forecastResponse = await fetch( 47 | `https://api.openweathermap.org/data/2.5/forecast?lat=${location[0]}&lon=${location[1]}&appid=${apiKey}&units=metric` 48 | ); 49 | const forecast = await forecastResponse.json(); 50 | 51 | // get current date to use in comparison in forecast intervals 52 | const date = new Date(); 53 | const allDays = [ 54 | "Sunday", 55 | "Monday", 56 | "Tuesday", 57 | "Wednesday", 58 | "Thursday", 59 | "Friday", 60 | "Saturday", 61 | ]; 62 | 63 | // create (unordered) cleaned up forecastArray with exclusively noteworthy informations 64 | // based on forecast array from the response object 65 | let forecastArray: sortedIntervalType[] = []; 66 | forecast.list.forEach((interval: intervalType, i: number) => { 67 | const newIntervalObject = { 68 | wind: interval.wind.speed, 69 | weather: interval.weather[0].main, 70 | temp: Math.round(interval.main.temp), 71 | feelsTemp: Math.round(interval.main.feels_like), 72 | maxTemp: Math.round(interval.main.temp_max), 73 | minTemp: Math.round(interval.main.temp_min), 74 | humidity: interval.main.humidity, 75 | rain: interval.clouds["all"], 76 | date: new Date(interval.dt * 1000), 77 | hours: 78 | new Date(interval.dt * 1000).getHours() > 12 79 | ? new Date(interval.dt * 1000).getHours() - 12 + "PM" 80 | : new Date(interval.dt * 1000).getHours() + "AM", 81 | dateName: allDays[new Date(interval.dt * 1000).getDay()], 82 | dateNum: new Date(interval.dt * 1000).getDay(), 83 | time: date.getHours(), 84 | }; 85 | forecastArray.push(newIntervalObject); 86 | }); 87 | 88 | // declare variable to save forecast objects in, sorted after days 89 | const daysArray: sortedIntervalType[] = []; 90 | 91 | // populate sorted forecast array with values from cleaned forecast array 92 | let previousDayName = forecastArray[0].dateName; 93 | let todaysIntervals: any = []; 94 | for (const interval of forecastArray) { 95 | if (daysArray.length === 0) { 96 | todaysIntervals = forecastArray.filter( 97 | (element: sortedIntervalType) => element.dateName === previousDayName 98 | ); 99 | daysArray.push(todaysIntervals); 100 | } 101 | 102 | if (interval.dateName !== previousDayName) { 103 | previousDayName = interval.dateName; 104 | todaysIntervals = forecastArray.filter( 105 | (element: sortedIntervalType) => element.dateName === previousDayName 106 | ); 107 | daysArray.push(todaysIntervals); 108 | } 109 | } 110 | 111 | // return newWeather 112 | const newWeather = { 113 | current: currentObject, 114 | forecast: daysArray, 115 | }; 116 | return newWeather; 117 | }; 118 | 119 | export default inputWork; 120 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/nightTimes.ts: -------------------------------------------------------------------------------- 1 | const nightTimes: string[] = [ 2 | "10PM", 3 | "1AM", 4 | "4AM", 5 | "11PM", 6 | "9PM", 7 | "2AM", 8 | "3AM", 9 | "0AM", 10 | ]; 11 | 12 | export default nightTimes; 13 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/openContextMenu.ts: -------------------------------------------------------------------------------- 1 | import React, { MouseEvent } from 'react'; 2 | 3 | const openContextMenu = (e: MouseEvent) => { 4 | e.preventDefault(); 5 | const contextMenu = document.getElementById('context-menu'); 6 | // disable entry animation on contextMenu before it appears 7 | contextMenu!.style.transition = "none"; 8 | contextMenu!.style.opacity = "1"; 9 | 10 | let x = e.nativeEvent.offsetX, y = e.nativeEvent.offsetY; 11 | let winWidth = window.innerWidth; 12 | let winHeight = window.innerHeight; 13 | let cmWidth = contextMenu!.offsetWidth; 14 | let cmHeight = contextMenu!.offsetHeight; 15 | 16 | // if x is greater than "windows width - contextMenu width", set the x value 17 | // to "window width - contextMenu width". Else, set x to the offsetX. Similarly with y. 18 | x = x > winWidth - cmWidth ? winWidth - cmWidth : x; 19 | y = y > winHeight - cmHeight ? winHeight - cmHeight : y; 20 | 21 | contextMenu!.style.left = `${x}px`; 22 | contextMenu!.style.top = `${y}px`; 23 | contextMenu!.style.visibility = "visible"; 24 | } 25 | 26 | export default openContextMenu; -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/openWeatherApp.ts: -------------------------------------------------------------------------------- 1 | const openWeatherApp = (e: any) => { 2 | const weatherApp = document.getElementById("weather-window"); 3 | if (weatherApp?.classList.contains("window-minimized")) { 4 | weatherApp.classList.remove("window-minimized"); 5 | weatherApp.classList.add("window"); 6 | return; 7 | } 8 | 9 | if (weatherApp?.classList.contains("window-closed")) { 10 | weatherApp.classList.remove("window-closed"); 11 | weatherApp.classList.add("window"); 12 | return; 13 | } 14 | }; 15 | 16 | export default openWeatherApp; 17 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/positionWork.ts: -------------------------------------------------------------------------------- 1 | import getPosition from "./getPosition"; 2 | import apiKey from "../keys/key"; 3 | import intervalType from "../../types/intervalType"; 4 | import sortedIntervalType from "../../types/sortedInterval"; 5 | 6 | const positionWork = async function () { 7 | let position: any; 8 | try { 9 | position = await getPosition(); 10 | } catch { 11 | position = { 12 | coords: { 13 | latitude: 51.2217, 14 | longitude: 6.7762, 15 | }, 16 | }; 17 | } 18 | 19 | // save latitude and longitude in variables 20 | const lat = position.coords.latitude; 21 | const lon = position.coords.longitude; 22 | 23 | // fetch current weather data and save it in current const 24 | const currentResponse = await fetch( 25 | `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric` 26 | ); 27 | const current = await currentResponse.json(); 28 | 29 | const currentObject = { 30 | country: current.sys.country, 31 | town: current.name, 32 | wind: current.wind.speed, 33 | weather: current.weather[0].main, 34 | temp: Math.round(current.main.temp), 35 | feelsTemp: Math.round(current.main.feels_like), 36 | maxTemp: Math.round(current.main.temp_max), 37 | minTemp: Math.round(current.main.temp_min), 38 | hours: 39 | new Date(current.dt * 1000).getHours() > 12 40 | ? new Date(current.dt * 1000).getHours() - 12 + "PM" 41 | : new Date(current.dt * 1000).getHours() + "AM", 42 | humidity: current.main.humidity, 43 | rain: current.clouds["all"], 44 | }; 45 | 46 | // fetch 5 day forecast data for user location and save it in forecast const 47 | const forecastResponse = await fetch( 48 | `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric` 49 | ); 50 | const forecast = await forecastResponse.json(); 51 | 52 | // get current date to use in comparison in forecast intervals 53 | const date = new Date(); 54 | const allDays = [ 55 | "Sunday", 56 | "Monday", 57 | "Tuesday", 58 | "Wednesday", 59 | "Thursday", 60 | "Friday", 61 | "Saturday", 62 | ]; 63 | 64 | // create (unordered) cleaned up forecastArray with exclusively noteworthy informations 65 | // based on forecast array from the response object 66 | let forecastArray: sortedIntervalType[] = []; 67 | forecast.list.forEach((interval: intervalType, i: number) => { 68 | const newIntervalObject = { 69 | wind: interval.wind.speed, 70 | weather: interval.weather[0].main, 71 | temp: Math.round(interval.main.temp), 72 | feelsTemp: Math.round(interval.main.feels_like), 73 | maxTemp: Math.round(interval.main.temp_max), 74 | minTemp: Math.round(interval.main.temp_min), 75 | humidity: interval.main.humidity, 76 | rain: interval.clouds["all"], 77 | date: new Date(interval.dt * 1000), 78 | hours: 79 | new Date(interval.dt * 1000).getHours() > 12 80 | ? new Date(interval.dt * 1000).getHours() - 12 + "PM" 81 | : new Date(interval.dt * 1000).getHours() + "AM", 82 | dateName: allDays[new Date(interval.dt * 1000).getDay()], 83 | dateNum: new Date(interval.dt * 1000).getDay(), 84 | time: date.getHours(), 85 | }; 86 | forecastArray.push(newIntervalObject); 87 | }); 88 | 89 | // declare variable to save forecast objects in, sorted after days 90 | const daysArray: sortedIntervalType[] = []; 91 | 92 | // populate sorted forecast array with values from cleaned forecast array 93 | let previousDayName = forecastArray[0].dateName; 94 | let todaysIntervals: any = []; 95 | for (const interval of forecastArray) { 96 | if (daysArray.length === 0) { 97 | todaysIntervals = forecastArray.filter( 98 | (element: sortedIntervalType) => element.dateName === previousDayName 99 | ); 100 | daysArray.push(todaysIntervals); 101 | } 102 | 103 | if (interval.dateName !== previousDayName) { 104 | previousDayName = interval.dateName; 105 | todaysIntervals = forecastArray.filter( 106 | (element: sortedIntervalType) => element.dateName === previousDayName 107 | ); 108 | daysArray.push(todaysIntervals); 109 | } 110 | } 111 | 112 | // return newWeather 113 | const newWeather = { 114 | current: currentObject, 115 | forecast: daysArray, 116 | }; 117 | return newWeather; 118 | }; 119 | 120 | export default positionWork; 121 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/returnColor.ts: -------------------------------------------------------------------------------- 1 | const returnColor = (color: string) => { 2 | switch (color) { 3 | case "orange": 4 | return "#ff9d0a"; 5 | case "green": 6 | return "#2ed157"; 7 | case "babyblue": 8 | return "#66d4ff"; 9 | case "blue": 10 | return "#0a85ff"; 11 | case "purple": 12 | return "#5e5ce6"; 13 | case "violet": 14 | return "#bf5af2"; 15 | case "red": 16 | return "#ff3860"; 17 | default: 18 | return "#0a85ff"; 19 | } 20 | }; 21 | 22 | export default returnColor; 23 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/submitInput.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { store } from "../../App"; 3 | const [state, dispatch] = useContext(store); 4 | 5 | const submitInput = (e: any, query: string) => { 6 | const event = e; 7 | const target = e.currentTarget; 8 | if (event.key === "Enter") { 9 | dispatch({ 10 | type: "query/SUBMIT", 11 | payload: "test", 12 | }); 13 | } 14 | }; 15 | 16 | export default submitInput; 17 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/toggleBorder.ts: -------------------------------------------------------------------------------- 1 | const toggleBorder = (e: React.MouseEvent) => { 2 | const target = e.target as HTMLImageElement; 3 | if (target.classList.contains("image-wrapper-bordered")) { 4 | target.classList.remove("image-wrapper-bordered"); 5 | } else { 6 | target.classList.add("image-wrapper-bordered"); 7 | } 8 | }; 9 | 10 | export default toggleBorder; 11 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/toggleFullscreen.ts: -------------------------------------------------------------------------------- 1 | const toggleFullscreen = () => { 2 | var elem = document.getElementById("page"); 3 | function openFullscreen() { 4 | if (!elem) { 5 | return; 6 | } 7 | if (elem.requestFullscreen) { 8 | elem.requestFullscreen(); 9 | } 10 | } 11 | openFullscreen(); 12 | }; 13 | 14 | export default toggleFullscreen; 15 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/toggleMinimize.ts: -------------------------------------------------------------------------------- 1 | const toggleMinimize = (e: React.MouseEvent) => { 2 | const target = document.getElementById("weather-window"); 3 | if (target?.classList.contains("window")) { 4 | target.classList.remove("window"); 5 | target.classList.add("window-minimized"); 6 | } else if (target?.classList.contains("window-minimized")) { 7 | target.classList.remove("window-minimized"); 8 | target.classList.add("window"); 9 | } 10 | }; 11 | 12 | export default toggleMinimize; 13 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/toggleVisibility.ts: -------------------------------------------------------------------------------- 1 | const toggleVisibility = (e: React.MouseEvent) => { 2 | const target = document.getElementById("weather-window"); 3 | if (target?.classList.contains("window")) { 4 | target.classList.remove("window"); 5 | target.classList.add("window-closed"); 6 | } else if (target?.classList.contains("window-closed")) { 7 | target.classList.remove("window-closed"); 8 | target.classList.add("window"); 9 | } 10 | }; 11 | 12 | export default toggleVisibility; 13 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/toggleWallpaperMin.ts: -------------------------------------------------------------------------------- 1 | const toggleWallpaperMin = (e: React.MouseEvent) => { 2 | const eventTarget = e.target as HTMLDivElement; 3 | console.log(eventTarget); 4 | 5 | const target = document.getElementById("wallpaper-menu"); 6 | if (target?.classList.contains("wallpaper-menu")) { 7 | target.classList.remove("wallpaper-menu"); 8 | target.classList.add("wallpaper-menu-minimized"); 9 | } else if (target?.classList.contains("wallpaper-menu-minimized")) { 10 | target.classList.remove("wallpaper-menu-minimized"); 11 | target.classList.add("wallpaper-menu"); 12 | } 13 | }; 14 | 15 | export default toggleWallpaperMin; -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/toggleWallpaperVis.ts: -------------------------------------------------------------------------------- 1 | const toggleWallpaperVis = (e: React.MouseEvent) => { 2 | const target = document.getElementById("wallpaper-menu"); 3 | const eventTarget = e.target as HTMLDivElement 4 | console.log(eventTarget); 5 | 6 | if (eventTarget.id === "wallpaper-handle") { 7 | return; 8 | } 9 | 10 | if (target?.classList.contains("wallpaper-menu")) { 11 | if (eventTarget.id === "opener") { 12 | return; 13 | } 14 | if (eventTarget.id === "in-context") { 15 | return; 16 | } 17 | target.classList.remove("wallpaper-menu"); 18 | target.classList.add("wallpaper-menu-closed"); 19 | } else if (target?.classList.contains("wallpaper-menu-closed")) { 20 | target.classList.remove("wallpaper-menu-closed"); 21 | target.classList.add("wallpaper-menu"); 22 | } else if (target?.classList.contains("wallpaper-menu-minimized")) { 23 | target.classList.remove("wallpaper-menu-minimized"); 24 | target.classList.add("wallpaper-menu"); 25 | } 26 | }; 27 | 28 | export default toggleWallpaperVis; -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/updateSysColor.ts: -------------------------------------------------------------------------------- 1 | const updateSysColor = (color: string) => { 2 | if (color === "orange") { 3 | document.documentElement.style.setProperty("--user-color", "#ff9d0a"); 4 | } else if (color === "green") { 5 | document.documentElement.style.setProperty("--user-color", "#2ed157"); 6 | } else if (color === "babyblue") { 7 | document.documentElement.style.setProperty("--user-color", "#66d4ff"); 8 | } else if (color === "blue") { 9 | document.documentElement.style.setProperty("--user-color", "#0a85ff"); 10 | } else if (color === "purple") { 11 | document.documentElement.style.setProperty("--user-color", "#5e5ce6"); 12 | } else if (color === "violet") { 13 | document.documentElement.style.setProperty("--user-color", "#bf5af2"); 14 | } else if (color === "red") { 15 | document.documentElement.style.setProperty("--user-color", "#ff3860"); 16 | } 17 | }; 18 | 19 | export default updateSysColor; 20 | -------------------------------------------------------------------------------- /weather-forecast/src/utils/helpers/wallpapers.ts: -------------------------------------------------------------------------------- 1 | const wallpapers = [ 2 | { 3 | surname: "ventura", 4 | name: "Ventura", 5 | preview: "../../resources/images/preview_ventura.jpg", 6 | src: "../../resources/images/ventura.jpg", 7 | }, 8 | { 9 | surname: "monterey", 10 | name: "Monterey", 11 | preview: "../../resources/images/preview_monterey.jpg", 12 | src: "../../resources/images/monterey.jpg", 13 | }, 14 | { 15 | surname: "bigsurgraphic", 16 | name: "Big Sur Graphic", 17 | preview: "../../resources/images/preview_bigsurgraphic.jpg", 18 | src: "../../resources/images/bigsurgraphic.jpg", 19 | }, 20 | { 21 | surname: "bigsur", 22 | name: "Big sur", 23 | preview: "../../resources/images/preview_bigsur.jpg", 24 | src: "../../resources/images/bigsur.jpg", 25 | }, 26 | { 27 | surname: "catalina", 28 | name: "Catalina", 29 | preview: "../../resources/images/preview_catalina.jpg", 30 | src: "../../resources/images/catalina.jpg", 31 | }, 32 | { 33 | surname: "mojave", 34 | name: "Mojave", 35 | preview: "../../resources/images/preview_mojave.jpg", 36 | src: "../../resources/images/mojave.jpg", 37 | }, 38 | { 39 | surname: "thedesert", 40 | name: "The Desert", 41 | preview: "../../resources/images/preview_thedesert.jpg", 42 | src: "../../resources/images/thedesert.jpg", 43 | }, 44 | { 45 | surname: "dome", 46 | name: "Dome", 47 | preview: "../../resources/images/preview_dome.jpg", 48 | src: "../../resources/images/dome.jpg", 49 | }, 50 | { 51 | surname: "peak", 52 | name: "Peak", 53 | preview: "../../resources/images/preview_peak.jpg", 54 | src: "../../resources/images/peak.jpg", 55 | }, 56 | { 57 | surname: "iridescence", 58 | name: "Iridescence", 59 | preview: "../../resources/images/preview_iridescence.jpg", 60 | src: "../../resources/images/iridescence.jpg", 61 | }, 62 | { 63 | surname: "lake", 64 | name: "Lake", 65 | preview: "../../resources/images/preview_lake.jpg", 66 | src: "../../resources/images/lake.jpg", 67 | }, 68 | { 69 | surname: "solargrad", 70 | name: "Solar Grad", 71 | preview: "../../resources/images/preview_solargrad.jpg", 72 | src: "../../resources/images/solargrad.jpg", 73 | }, 74 | ]; 75 | 76 | export default wallpapers; 77 | -------------------------------------------------------------------------------- /weather-forecast/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | --------------------------------------------------------------------------------