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
|
||||
|
||||
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