functional version

This commit is contained in:
ak 2023-06-22 21:37:26 -07:00
parent 629e8c8a1e
commit 1c7f97ccb3
15 changed files with 21965 additions and 0 deletions

11
.babelrc Normal file
View file

@ -0,0 +1,11 @@
{
"plugins": ["@babel/syntax-dynamic-import"],
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
]
]
}

13
.eslintrc.js Normal file
View 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
View file

@ -0,0 +1 @@
node_modules

1
.prettierrc.json Normal file
View file

@ -0,0 +1 @@
{}

View file

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

File diff suppressed because one or more lines are too long

1
dist/main.js.LICENSE.txt vendored Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

33
package.json Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

19
src/style.css Normal file
View 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
View 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;
};