functional version
This commit is contained in:
parent
629e8c8a1e
commit
1c7f97ccb3
15 changed files with 21965 additions and 0 deletions
11
.babelrc
Normal file
11
.babelrc
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"plugins": ["@babel/syntax-dynamic-import"],
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
"modules": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
13
.eslintrc.js
Normal file
13
.eslintrc.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
module.exports = {
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint-config-prettier",
|
||||||
|
"prettier"
|
||||||
|
]
|
||||||
|
}
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
||||||
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
# js-weatherapp
|
# js-weatherapp
|
||||||
|
|
||||||
|
Simple weather app written in ES6, made with npm, webpack, linted and prettiered
|
||||||
96
dist/main.js
vendored
Normal file
96
dist/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/main.js.LICENSE.txt
vendored
Normal file
1
dist/main.js.LICENSE.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
||||||
27
index.html
Normal file
27
index.html
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.0/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||||
|
<link rel="stylesheet" href="../src/style.css">
|
||||||
|
<title>Weather App</title>
|
||||||
|
</head>
|
||||||
|
<body class="d-flex h-100 text-center bg-dark">
|
||||||
|
<div class="cover-container d-flex w-100 h-100 px-3 flex-column">
|
||||||
|
<header class="d-flex flex-wrap justify-content-center py-3 align-items-center">
|
||||||
|
<button class="btn btn-primary btn-lg" type="button" id="newButton">
|
||||||
|
Enter Location
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<center>
|
||||||
|
<main class="col-sm-4 py-3"></main>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
<script type="module" src="/dist/main.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.0/js/bootstrap.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
21527
package-lock.json
generated
Normal file
21527
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
33
package.json
Normal file
33
package.json
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "my-webpack-project",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "My webpack project",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "webpack --mode=development",
|
||||||
|
"watch": "webpack --watch"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://_gitea@git.alexanderk.net:3022/ak/js-weatherapp.git"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.22.5",
|
||||||
|
"@babel/preset-env": "^7.22.5",
|
||||||
|
"@webpack-cli/generators": "^3.0.7",
|
||||||
|
"babel-loader": "^9.1.2",
|
||||||
|
"css-loader": "^6.7.3",
|
||||||
|
"eslint": "^8.43.0",
|
||||||
|
"eslint-config-prettier": "^8.8.0",
|
||||||
|
"mini-css-extract-plugin": "^2.7.6",
|
||||||
|
"prettier": "^2.8.8",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"webpack": "^5.88.0",
|
||||||
|
"webpack-cli": "^5.1.4",
|
||||||
|
"webpack-dev-server": "^4.15.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
179
src/UI.js
Normal file
179
src/UI.js
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
const BODY = document.querySelector("main");
|
||||||
|
|
||||||
|
// close button functionality
|
||||||
|
const closeWindow = (headDiv) => {
|
||||||
|
headDiv.parentElement.removeChild(headDiv);
|
||||||
|
};
|
||||||
|
|
||||||
|
// handy way of making new elements
|
||||||
|
const makeElement = (passedClass, passedType = "div") => {
|
||||||
|
const newElement = document.createElement(passedType);
|
||||||
|
newElement.setAttribute("class", passedClass);
|
||||||
|
return newElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function formSubmitted(location) {
|
||||||
|
// checks location entered
|
||||||
|
if (location != "") {
|
||||||
|
// close window
|
||||||
|
closeWindow(BODY.firstChild);
|
||||||
|
// add loader
|
||||||
|
loader(true);
|
||||||
|
// get weather data
|
||||||
|
fetch(
|
||||||
|
`https://api.weatherapi.com/v1/current.json?key=e34322c69ded4ab083604615232306&q=${location}`,
|
||||||
|
{ mode: "cors" }
|
||||||
|
)
|
||||||
|
// once done, remove loader
|
||||||
|
.then(function (response) {
|
||||||
|
loader(false);
|
||||||
|
makeWindow();
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
// then pass to output fields
|
||||||
|
.then(function (response) {
|
||||||
|
populateOutputWindow(response);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// no location entered
|
||||||
|
alert("Please enter a location!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates input fields
|
||||||
|
const populateInputWindow = () => {
|
||||||
|
// container
|
||||||
|
const inputContainer = makeElement("mb-3");
|
||||||
|
formWindow.appendChild(inputContainer);
|
||||||
|
// descriptor
|
||||||
|
const inputDescriptor = makeElement("card-title", "h6");
|
||||||
|
inputDescriptor.textContent =
|
||||||
|
"Please enter your location to get current weather:";
|
||||||
|
inputContainer.appendChild(inputDescriptor);
|
||||||
|
// input field
|
||||||
|
const userLocation = makeElement("form-control", "input");
|
||||||
|
userLocation.type = "text";
|
||||||
|
inputContainer.appendChild(userLocation);
|
||||||
|
//submit button
|
||||||
|
const submitButton = makeElement("btn btn-primary", "button");
|
||||||
|
submitButton.type = "submit";
|
||||||
|
submitButton.textContent = "Submit";
|
||||||
|
formWindow.appendChild(submitButton);
|
||||||
|
// prevent default
|
||||||
|
submitButton.onclick = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
formSubmitted(userLocation.value);
|
||||||
|
};
|
||||||
|
// get all Enter keystrokes in input field and treat them as a click
|
||||||
|
submitButton.onkeydown = (event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
formSubmitted(userLocation.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// creates output fields with weather info
|
||||||
|
const populateOutputWindow = (weatherData) => {
|
||||||
|
// main output container
|
||||||
|
const outputContainer = makeElement("mb-3 d-flex");
|
||||||
|
formWindow.appendChild(outputContainer);
|
||||||
|
// weather icon
|
||||||
|
const weatherIcon = makeElement("weatherIcon", "img");
|
||||||
|
let currentIconURL = weatherData.current.condition.icon;
|
||||||
|
// replace the weather icon link in db with 128x128
|
||||||
|
currentIconURL = currentIconURL.replace(
|
||||||
|
new RegExp("(?<=/)[0-9]+(?=x)", "gi"),
|
||||||
|
"128"
|
||||||
|
);
|
||||||
|
console.log(currentIconURL);
|
||||||
|
currentIconURL = currentIconURL.replace(
|
||||||
|
new RegExp("(?<=x)[0-9]+(?=/)", "gi"),
|
||||||
|
"128"
|
||||||
|
);
|
||||||
|
console.log(currentIconURL);
|
||||||
|
// display icon
|
||||||
|
weatherIcon.src = currentIconURL;
|
||||||
|
outputContainer.appendChild(weatherIcon);
|
||||||
|
// information container
|
||||||
|
const infoContainer = makeElement("d-flex flex-column");
|
||||||
|
outputContainer.appendChild(infoContainer);
|
||||||
|
// temperature
|
||||||
|
const userTemp = makeElement("card-title mb-0", "h1");
|
||||||
|
const currentTemp = Math.round(weatherData.current.temp_f);
|
||||||
|
userTemp.textContent = `${currentTemp}°F`;
|
||||||
|
infoContainer.appendChild(userTemp);
|
||||||
|
// city
|
||||||
|
const userCity = makeElement("card-text", "h3");
|
||||||
|
userCity.textContent = weatherData.location.name;
|
||||||
|
infoContainer.appendChild(userCity);
|
||||||
|
// wind
|
||||||
|
const userWind = makeElement("card-text text-muted", "h6");
|
||||||
|
const currentWind = Math.round(weatherData.current.wind_mph);
|
||||||
|
userWind.textContent = `Wind Speed: ${currentWind} mph`;
|
||||||
|
infoContainer.appendChild(userWind);
|
||||||
|
// precipitation
|
||||||
|
const userHumidity = makeElement("card-text text-muted", "h6");
|
||||||
|
userHumidity.textContent = `${weatherData.current.humidity}% Humidity`;
|
||||||
|
infoContainer.appendChild(userHumidity);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loader = (toggle) => {
|
||||||
|
// make loading animation img
|
||||||
|
const loadingAnimation = makeElement("loadingAnimation", "img");
|
||||||
|
loadingAnimation.src = "/src/loader.gif";
|
||||||
|
// turn on
|
||||||
|
if (toggle) {
|
||||||
|
BODY.appendChild(loadingAnimation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// turn off
|
||||||
|
else {
|
||||||
|
BODY.removeChild(BODY.firstChild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeWindow = (type = null) => {
|
||||||
|
// *destroy any existing window*
|
||||||
|
if (BODY.firstChild) {
|
||||||
|
closeWindow(BODY.firstChild);
|
||||||
|
}
|
||||||
|
// *make window itself*
|
||||||
|
// 1. window body
|
||||||
|
const formWindow = makeElement("card-body", "form");
|
||||||
|
formWindow.id = "formWindow";
|
||||||
|
BODY.appendChild(makeElement("card")).appendChild(formWindow);
|
||||||
|
|
||||||
|
// 2. close button div
|
||||||
|
const closeButton = makeElement("mb-3 d-flex");
|
||||||
|
formWindow.appendChild(closeButton);
|
||||||
|
|
||||||
|
// the close button is SVG
|
||||||
|
// 3. container for SVG - 24x24
|
||||||
|
const closeButtonSVGContainer = document.createElementNS(
|
||||||
|
"http://www.w3.org/2000/svg",
|
||||||
|
"svg"
|
||||||
|
);
|
||||||
|
closeButtonSVGContainer.style = "width: 24px; height: 24px";
|
||||||
|
closeButtonSVGContainer.setAttribute("viewbox", "0 0 24 24");
|
||||||
|
closeButtonSVGContainer.id = "closeButton";
|
||||||
|
closeButton.appendChild(closeButtonSVGContainer);
|
||||||
|
|
||||||
|
// 4. the actual SVG path
|
||||||
|
const closeButtonSVGPath = document.createElementNS(
|
||||||
|
"http://www.w3.org/2000/svg",
|
||||||
|
"path"
|
||||||
|
);
|
||||||
|
closeButtonSVGPath.setAttribute(
|
||||||
|
"d",
|
||||||
|
"M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
|
||||||
|
);
|
||||||
|
closeButtonSVGPath.fill = "#6c757d";
|
||||||
|
closeButtonSVGContainer.appendChild(closeButtonSVGPath);
|
||||||
|
// add event listeners to close the window if button is clicked
|
||||||
|
closeButtonSVGContainer.onclick = () => closeWindow(formWindow.parentElement);
|
||||||
|
|
||||||
|
// create input fields
|
||||||
|
if (type === "input") {
|
||||||
|
populateInputWindow();
|
||||||
|
}
|
||||||
|
};
|
||||||
5
src/index.js
Normal file
5
src/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { makeWindow } from "./UI.js";
|
||||||
|
|
||||||
|
const NEWBUTTON = document.getElementById("newButton");
|
||||||
|
|
||||||
|
NEWBUTTON.onclick = () => makeWindow("input");
|
||||||
BIN
src/loader.gif
Normal file
BIN
src/loader.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
19
src/style.css
Normal file
19
src/style.css
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#formWindow > .d-flex:first-child {
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#formWindow > .d-flex:nth-child(2) {
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
#closeButton {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weatherIcon {
|
||||||
|
max-width: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#formWindow > .d-flex:nth-child(2) {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
51
webpack.config.js
Normal file
51
webpack.config.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Generated using webpack-cli https://github.com/webpack/webpack-cli
|
||||||
|
|
||||||
|
const path = require("path");
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
|
||||||
|
const isProduction = process.env.NODE_ENV == "production";
|
||||||
|
|
||||||
|
const stylesHandler = isProduction
|
||||||
|
? MiniCssExtractPlugin.loader
|
||||||
|
: "style-loader";
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
entry: "./src/index.js",
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, "dist"),
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// Add your plugins here
|
||||||
|
// Learn more about plugins from https://webpack.js.org/configuration/plugins/
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(js|jsx)$/i,
|
||||||
|
loader: "babel-loader",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/i,
|
||||||
|
use: [stylesHandler, "css-loader"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
|
||||||
|
type: "asset",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Add your rules for custom modules here
|
||||||
|
// Learn more about loaders from https://webpack.js.org/loaders/
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = () => {
|
||||||
|
if (isProduction) {
|
||||||
|
config.mode = "production";
|
||||||
|
|
||||||
|
config.plugins.push(new MiniCssExtractPlugin());
|
||||||
|
} else {
|
||||||
|
config.mode = "development";
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
};
|
||||||
Loading…
Add table
Reference in a new issue