First container working
This commit is contained in:
138
src/components/WeatherAndTime.tsx
Normal file
138
src/components/WeatherAndTime.tsx
Normal file
@ -0,0 +1,138 @@
|
||||
import * as React from "react"
|
||||
import type { WeatherInfo } from "../lib/interfaces";
|
||||
import * as styles from "../styles/containers/WeatherAndTime.module.css";
|
||||
|
||||
const WEATHER_REFRESH_INTERVAL = 15 * 60 * 1000;
|
||||
|
||||
function dowToString(dow: number) {
|
||||
switch (dow) {
|
||||
case 0: return "Sonntag";
|
||||
case 1: return "Montag";
|
||||
case 2: return "Dienstag";
|
||||
case 3: return "Mittwoch";
|
||||
case 4: return "Donnerstag";
|
||||
case 5: return "Freitag";
|
||||
case 6: return "Samstag";
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
function monthToString(month: number) {
|
||||
switch (month) {
|
||||
case 0: return "Januar";
|
||||
case 1: return "Februar";
|
||||
case 2: return "März";
|
||||
case 3: return "April";
|
||||
case 4: return "Mail";
|
||||
case 5: return "Juni";
|
||||
case 6: return "Juli";
|
||||
case 7: return "August";
|
||||
case 8: return "September";
|
||||
case 9: return "Oktober";
|
||||
case 10: return "November";
|
||||
case 11: return "Dezember";
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
function getWeatherIcon(icon: string) {
|
||||
const ImportedIcon = require(`../images/weather/${icon}.svg`);
|
||||
return <ImportedIcon />
|
||||
}
|
||||
|
||||
const WeatherAndTimeContainer = ({ apiKey, coords }) => {
|
||||
const [date, setDate] = React.useState(new Date());
|
||||
const [weather, setWeather] = React.useState<WeatherInfo>({
|
||||
currently: {
|
||||
icon: "clear-day",
|
||||
temperature: 0,
|
||||
summary: "No data loaded"
|
||||
},
|
||||
daily: {
|
||||
data: [
|
||||
{ icon: "clear-day", time: 0, temperatureHigh: 0, temperatureLow: 0 },
|
||||
{ icon: "clear-day", time: 0, temperatureHigh: 0, temperatureLow: 0 },
|
||||
{ icon: "clear-day", time: 0, temperatureHigh: 0, temperatureLow: 0 },
|
||||
{ icon: "clear-day", time: 0, temperatureHigh: 0, temperatureLow: 0 },
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const dateInterval = setInterval(() => {
|
||||
const date = new Date()
|
||||
if (date.getHours() < 7) document.documentElement.setAttribute("data-theme", "night");
|
||||
else document.documentElement.setAttribute("data-theme", "day");
|
||||
setDate(date);
|
||||
}, 1000);
|
||||
|
||||
pullWeather()
|
||||
//TODO
|
||||
//const weatherInterval = setInterval(pullWeather, WEATHER_REFRESH_INTERVAL);
|
||||
|
||||
return () => {
|
||||
clearInterval(dateInterval);
|
||||
//clearInterval(weatherInterval);
|
||||
}
|
||||
}, [])
|
||||
|
||||
const pullWeather = () => {
|
||||
fetch(`https://api.pirateweather.net/forecast/${apiKey}/${coords}?exclude=minutely,hourly&lang=de&units=ca`)
|
||||
.then(resp => resp.json())
|
||||
.then(setWeather);
|
||||
}
|
||||
|
||||
const time = `${date.getHours()}:${date.getMinutes().toString().padStart(2, "0")}`;
|
||||
const dateString = `${dowToString(date.getDay())}, der ${date.getDate()}. ${monthToString(date.getMonth())} ${date.getFullYear()}`;
|
||||
|
||||
if (weather.currently.summary === "No data loaded") return (<div className={`container ${styles.container}`}>
|
||||
<span id={styles.clock}>{time}</span>
|
||||
<span id={styles.date}>{dateString}</span>
|
||||
</div>);
|
||||
|
||||
return (<div className={`container ${styles.container}`}>
|
||||
<span id={styles.clock}>{time}</span>
|
||||
<span id={styles.date}>{dateString}</span>
|
||||
<div id={styles.weatherIcon}>{getWeatherIcon(weather.currently.icon)}</div>
|
||||
<span id={styles.temperature}>{weather.currently.temperature.toFixed(1)}°C</span>
|
||||
<span id={styles.currentWeatherInfos}>{weather.currently.summary}</span>
|
||||
<table id={styles.futureWeatherInfos}>
|
||||
<thead>
|
||||
<tr>
|
||||
{(() => {
|
||||
const rslt = []
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const day = new Date((weather.daily.data[i].time * 1000));
|
||||
rslt.push(<th colSpan={2} key={i}>{dowToString(day.getDay())}, {day.getDate()}. {day.getMonth() + 1}.</th>);
|
||||
}
|
||||
return rslt
|
||||
})()}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{(() => {
|
||||
const rslt = []
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const style = i > 0 ? { borderLeft: "1px solid var(--iconColor)" } : {}
|
||||
rslt.push(<td key={`00${i}`} rowSpan={2} style={style}>{getWeatherIcon(weather.daily.data[i].icon)}</td>);
|
||||
rslt.push(<td key={`10${i}`} className={styles.futureWeatherHighTemp}>{weather.daily.data[i].temperatureHigh.toFixed(1)}°C</td>);
|
||||
}
|
||||
return rslt
|
||||
})()}
|
||||
</tr>
|
||||
<tr>
|
||||
{(() => {
|
||||
const rslt = []
|
||||
for (let i = 0; i < 4; i++) {
|
||||
rslt.push(<td key={i} className={styles.futureWeatherLowTemp}>{weather.daily.data[i].temperatureLow.toFixed(1)}°C</td>);
|
||||
}
|
||||
return rslt
|
||||
})()}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>)
|
||||
}
|
||||
|
||||
export default WeatherAndTimeContainer;
|
0
src/images/.gitkeep
Normal file
0
src/images/.gitkeep
Normal file
15
src/lib/interfaces.ts
Normal file
15
src/lib/interfaces.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export interface WeatherInfo {
|
||||
currently: {
|
||||
icon: string;
|
||||
temperature: number;
|
||||
summary: string;
|
||||
},
|
||||
daily: {
|
||||
data: {
|
||||
time: number; // Epoch without ms
|
||||
icon: string;
|
||||
temperatureHigh: number;
|
||||
temperatureLow: number;
|
||||
}[]
|
||||
}
|
||||
}
|
4
src/lib/types.d.ts
vendored
Normal file
4
src/lib/types.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module '*.css' {
|
||||
const content: {[className: string]: string};
|
||||
export = content;
|
||||
}
|
@ -1,7 +1,36 @@
|
||||
import * as React from "react"
|
||||
import secrets from "../../secrets.json"
|
||||
import WeatherAndTimeContainer from "../components/WeatherAndTime"
|
||||
|
||||
function importAll(r) {
|
||||
return r.keys().map(r);
|
||||
}
|
||||
|
||||
const images = importAll(require.context('../images/bg/', false, /\.(png|jpe?g|svg)$/));
|
||||
|
||||
const IndexPage = () => {
|
||||
return (<>Hello World</>)
|
||||
const [currentBg, setCurrentBg] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
// This effect is executed onload
|
||||
updateBackground()
|
||||
const interval = setInterval(() => {
|
||||
updateBackground();
|
||||
}, 30 * 60 * 1000);
|
||||
|
||||
// Return will be executed onunload
|
||||
return () => clearInterval(interval);
|
||||
}, [])
|
||||
|
||||
const updateBackground = () => {
|
||||
let nextBg = Math.floor(Math.random() * images.length)
|
||||
while (nextBg == currentBg) nextBg = Math.floor(Math.random() * images.length)
|
||||
setCurrentBg(nextBg)
|
||||
}
|
||||
|
||||
return (<main style={{ backgroundImage: `url(${images[currentBg].default})` }}>
|
||||
<WeatherAndTimeContainer apiKey={secrets.weather.apiKey} coords={secrets.weather.coords}/>
|
||||
</main>)
|
||||
}
|
||||
|
||||
export default IndexPage
|
||||
|
73
src/styles/containers/WeatherAndTime.module.css
Normal file
73
src/styles/containers/WeatherAndTime.module.css
Normal file
@ -0,0 +1,73 @@
|
||||
.container {
|
||||
grid-area: date-weather;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"time currentIcon currentTemp"
|
||||
"date currentCondition currentCondition"
|
||||
"forecast forecast forecast";
|
||||
grid-template-columns: auto min-content min-content;
|
||||
grid-template-rows: min-content min-content auto;
|
||||
column-gap: 1vw;
|
||||
row-gap: 0;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#clock {
|
||||
grid-area: time;
|
||||
font-size: 14vh;
|
||||
}
|
||||
|
||||
#weatherIcon {
|
||||
grid-area: currentIcon;
|
||||
height: 15vh;
|
||||
}
|
||||
|
||||
.container svg {
|
||||
height: 100%;
|
||||
fill: var(--iconColor);
|
||||
}
|
||||
|
||||
#temperature {
|
||||
grid-area: currentTemp;
|
||||
font-size: 12vh;
|
||||
}
|
||||
|
||||
#date {
|
||||
grid-area: date;
|
||||
font-size: 2.5vh;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#currentWeatherInfos {
|
||||
grid-area: currentCondition;
|
||||
font-size: 2.5vh;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#futureWeatherInfos {
|
||||
border-top: 1px solid var(--iconColor);
|
||||
grid-area: forecast;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#futureWeatherInfos th {
|
||||
text-align: center;
|
||||
font-size: 3vh;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#futureWeatherInfos .futureWeatherHighTemp,
|
||||
#futureWeatherInfos .futureWeatherLowTemp {
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
vertical-align: middle;
|
||||
font-size: 3.5vh;
|
||||
}
|
||||
|
||||
#futureWeatherInfos td {
|
||||
padding-right: 5px;
|
||||
}
|
57
src/styles/global.css
Normal file
57
src/styles/global.css
Normal file
@ -0,0 +1,57 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root, :root[data-theme="day"] {
|
||||
--containerBg: rgba(255,255,255,0.5);
|
||||
--textColor: #000000;
|
||||
color: #000000;
|
||||
--iconColor: #000000;
|
||||
}
|
||||
:root[data-theme="night"] {
|
||||
--containerBg: rgba(20,20,20,0.8);
|
||||
--textColor: #b0b0b0;
|
||||
color: #b0b0b0;
|
||||
--iconColor: #b0b0b0;
|
||||
}
|
||||
|
||||
body, main {
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"date-weather date-weather calendar"
|
||||
"date-weather date-weather calendar"
|
||||
"map dvb news"
|
||||
"map spotify news";
|
||||
grid-template-columns: 28vw auto 28vw;
|
||||
grid-template-rows: 18vh 19vh 19vh 19vh;
|
||||
column-gap: 3vw;
|
||||
row-gap: 5vh;
|
||||
padding: 5vh 3vw;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
max-width: 100%;
|
||||
font-family: Arial;
|
||||
transition: background-image 1s linear;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
:root[data-theme="night"] main {
|
||||
background-image: url("../images/bg/3.jpg") !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
box-shadow: 0 0 1rem 0 rgba(0, 0, 0, .2);
|
||||
border-radius: 10px;
|
||||
background: var(--containerBg);
|
||||
backdrop-filter: blur(5px);
|
||||
padding:10px;
|
||||
overflow:hidden;
|
||||
}
|
Reference in New Issue
Block a user