├── 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 | 
7 | 
8 | 
9 | 
10 | 
11 | 
12 |
13 |
14 |
15 | 
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 | 
31 | 
32 | 
33 | 
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 | You need to enable JavaScript to run this app.
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 |
56 |
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 |
41 | Cancel
42 |
43 | {
46 | onClose();
47 | toggleFullscreen();
48 | }}
49 | ml={3}
50 | >
51 | Enter
52 |
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 |
9 | No cookies!
10 |
15 |
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 |
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 |
{
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 |
53 |
58 |
63 | {state.section === "logo" ?
: null}
64 |
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 |
{
24 | if (window.screenTop && window.screenY) {
25 | document.exitFullscreen();
26 | return;
27 | }
28 | onOpen();
29 | }}
30 | >
31 |
36 |
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 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
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 |
80 |
81 |
82 | {state.weather.current.town}, {state.weather.current.country}
83 |
84 |
85 |
86 |
87 |
88 | {returnCurrentCon(
89 | state.selected
90 | ? state.selected[0].weather
91 | : state.weather.current.weather
92 | )}
93 |
94 |
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 |
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 |
--------------------------------------------------------------------------------