Skip to content
Snippets Groups Projects
Commit 7994d700 authored by Niklas Schrötler's avatar Niklas Schrötler
Browse files

Implemented a base of components

parent ba6cd5da
No related branches found
No related tags found
No related merge requests found
......@@ -15,6 +15,8 @@
"@types/node": "^16.18.36",
"@types/react": "^18.2.13",
"@types/react-dom": "^18.2.6",
"framer-motion": "^10.12.22",
"javascript-time-ago": "^2.5.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
......@@ -2295,6 +2297,21 @@
"postcss-selector-parser": "^6.0.10"
}
},
"node_modules/@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
"optional": true,
"dependencies": {
"@emotion/memoize": "0.7.4"
}
},
"node_modules/@emotion/memoize": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
"optional": true
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
......@@ -8256,6 +8273,29 @@
"url": "https://www.patreon.com/infusion"
}
},
"node_modules/framer-motion": {
"version": "10.12.22",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.12.22.tgz",
"integrity": "sha512-bBGYPOxvxcfzS7/py9MEqDucmXBkVl2g42HNlXXPieSTSGGkr8L7+MilCnrU6uX3HrNk/tcB++1SkWE8BosHFw==",
"dependencies": {
"tslib": "^2.4.0"
},
"optionalDependencies": {
"@emotion/is-prop-valid": "^0.8.2"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
......@@ -9604,6 +9644,14 @@
"node": ">=8"
}
},
"node_modules/javascript-time-ago": {
"version": "2.5.9",
"resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.9.tgz",
"integrity": "sha512-pQ8mNco/9g9TqWXWWjP0EWl6i/lAQScOyEeXy5AB+f7MfLSdgyV9BJhiOD1zrIac/lrxPYOWNbyl/IW8CW5n0A==",
"dependencies": {
"relative-time-format": "^1.1.6"
}
},
"node_modules/jest": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
......@@ -14530,6 +14578,11 @@
"node": ">= 0.10"
}
},
"node_modules/relative-time-format": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-1.1.6.tgz",
"integrity": "sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ=="
},
"node_modules/renderkid": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
......@@ -18723,6 +18776,21 @@
"integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==",
"requires": {}
},
"@emotion/is-prop-valid": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
"optional": true,
"requires": {
"@emotion/memoize": "0.7.4"
}
},
"@emotion/memoize": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
"optional": true
},
"@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
......@@ -23101,6 +23169,15 @@
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
"integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA=="
},
"framer-motion": {
"version": "10.12.22",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.12.22.tgz",
"integrity": "sha512-bBGYPOxvxcfzS7/py9MEqDucmXBkVl2g42HNlXXPieSTSGGkr8L7+MilCnrU6uX3HrNk/tcB++1SkWE8BosHFw==",
"requires": {
"@emotion/is-prop-valid": "^0.8.2",
"tslib": "^2.4.0"
}
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
......@@ -24034,6 +24111,14 @@
}
}
},
"javascript-time-ago": {
"version": "2.5.9",
"resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.9.tgz",
"integrity": "sha512-pQ8mNco/9g9TqWXWWjP0EWl6i/lAQScOyEeXy5AB+f7MfLSdgyV9BJhiOD1zrIac/lrxPYOWNbyl/IW8CW5n0A==",
"requires": {
"relative-time-format": "^1.1.6"
}
},
"jest": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
......@@ -27446,6 +27531,11 @@
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
"integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="
},
"relative-time-format": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-1.1.6.tgz",
"integrity": "sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ=="
},
"renderkid": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
import React from 'react';
import './App.css';
import LayoutElement from "./layout/LayoutElement";
import useLayout from "./hooks/useLayout";
import VSplit from "./layout/VSplit";
import HSplit from "./layout/HSplit";
import ScreenWrapper from "./meta/ScreenWrapper";
import PanelWrapper from "./meta/PanelWrapper";
import PanelTitle from "./meta/PanelTitle";
import PanelContent from "./meta/PanelContent";
import FahrplanPanel from "./panels/Fahrplan/FahrplanPanel";
function App() {
const layout = useLayout();
// const layout = useLayout();
if(!layout) {
return (
<div>
Loading...
</div>
)
}
// if(!layout) {
// return (
// <div>
// Loading...
// </div>
// )
// }
return (
<div className={"overflow-hidden w-screen h-screen bg-zinc-900 text-white"} style={{fontFamily: "Be Vietnam Pro"}}>
<LayoutElement config={layout} />
<div className={"overflow-hidden w-screen h-screen bg-zinc-950 text-white"} style={{fontFamily: "Inter"}}>
{/*<LayoutElement config={layout} />*/}
<ScreenWrapper>
<VSplit left={(
<HSplit top={(
<FahrplanPanel />
)} bottom={(
<PanelWrapper>
<PanelTitle title={"Als nächstes im CZI"} />
<PanelContent>
<p>Next up</p>
</PanelContent>
</PanelWrapper>
)} split={.9}/>
)} right={(
<HSplit top={(
<PanelWrapper>
<p>News</p>
</PanelWrapper>
)} bottom={(
<HSplit top={(
<PanelWrapper>
<PanelTitle title={"Mensa Öffnungszeiten"} />
<PanelContent>
<p>Mensa Öffnungszeiten</p>
</PanelContent>
</PanelWrapper>
)} bottom={(
<PanelWrapper>
<PanelTitle title={"Mensaplan Hauptmensa"} />
<PanelContent>
<p>Mensa Plan</p>
</PanelContent>
</PanelWrapper>
)} split={.4}/>
)} split={.9}/>
)} split={.35}/>
</ScreenWrapper>
</div>
);
}
......
......@@ -12,7 +12,7 @@ const HSplit = (props: {
}
return (
<div className={"flex flex-col w-full h-full"}>
<div className={"flex flex-col w-full h-full gap-6"}>
<div style={{height: `${split * 100}%`}}>
{props.top}
</div>
......
......@@ -3,7 +3,7 @@ import {Layout} from "../types/LayoutConfig";
import config from "tailwindcss/defaultConfig";
const LayoutElement = (props: {config: Layout}) => {
switch (config.type)
// switch (config.type)s
};
export default LayoutElement;
......@@ -12,7 +12,7 @@ const VSplit = (props: {
}
return (
<div className={"flex flex-row w-full h-full"}>
<div className={"flex flex-row w-full h-full gap-6"}>
<div style={{width: `${split * 100}%`}}>
{props.left}
</div>
......
import React from 'react';
const PanelContent = (props: {padding?: boolean, children: any}) => {
return (
<div className={"flex-1 " + ((props.padding ?? true) ? "px-5 pb-4" : "")}>
{props.children}
</div>
);
};
export default PanelContent;
import React from 'react';
const PanelTitle = (props: {title: string}) => {
return (
<div className={"px-5 py-4 text-zinc-400"}>
<h2>{props.title}</h2>
</div>
);
};
export default PanelTitle;
......@@ -3,7 +3,7 @@ import React from 'react';
const PanelWrapper = (props: {children: any, className?: string}) => {
// ToDo: The className thing is not pretty. Re-do
return (
<div className={`w-full h-full ${props.className ?? ""}`}>
<div className={`w-full h-full bg-zinc-900 rounded-2xl overflow-hidden flex flex-col ${props.className ?? ""}`}>
{props.children}
</div>
);
......
import React, {useEffect, useState} from 'react';
const MONTH_NAMES = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];
const WEEKDAY_NAMES = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"];
const ScreenBar = () => {
const [time, setTime] = useState<string>("");
const [date, setDate] = useState<string>("")
useEffect(() => {
let update = () => {
const d = new Date();
const hh = d.getHours().toString().padStart(2, "0");
const mm = d.getMinutes().toString().padStart(2, "0");
const yyyy = d.getFullYear().toString();
const monthname = MONTH_NAMES[d.getMonth()];
const dd = d.getDate()
const weekday = WEEKDAY_NAMES[d.getDay()];
setTime(`${hh}:${mm}`);
setDate(`${weekday}, ${dd}. ${monthname} ${yyyy}`);
}
setInterval(update, 1000);
update();
}, [])
return (
<div className={"flex flex-row justify-between px-8 py-6"}>
<p>{date}</p>
<p>{time}</p>
</div>
);
};
export default ScreenBar;
import React from 'react';
import ScreenBar from "./ScreenBar";
const ScreenWrapper = (props: {children: any}) => {
return (
<div className={"h-full w-full flex flex-col"} style={{backgroundImage: "/logo512.png"}}>
<ScreenBar />
<div className={"flex-1 px-6 pb-6"}>
{props.children}
</div>
</div>
);
};
export default ScreenWrapper;
import React from 'react';
import PanelWrapper from "../../meta/PanelWrapper";
import PlanElement from "./components/PlanElement";
const Fahrplan = () => {
return (
<PanelWrapper className={"bg-blue-900"}>
<PlanElement trainIdentifier={"S1"} trainHeading={"Dortmund Hbf"} stops={[
{
name: "Essen",
time: "10:00",
delay: 20
},
{
name: "Nicht Essen",
time: "10:00",
delay: 20
}
]} />
<PlanElement trainIdentifier={"S1"} trainHeading={"Dortmund Hbf"} stops={[
{
name: "Essen",
time: "10:00",
delay: 20
},
{
name: "Nicht Essen",
time: "10:00",
delay: 20
}
]} />
</PanelWrapper>
);
};
export default Fahrplan;
import React from 'react';
import PanelWrapper from "../../meta/PanelWrapper";
import PlanElement from "./components/PlanElement";
import PanelTitle from "../../meta/PanelTitle";
import PanelContent from "../../meta/PanelContent";
const FahrplanPanel = () => {
return (
<PanelWrapper>
<PanelTitle title={"ÖPNV Monitor"}/>
<PanelContent>
<div className={"flex flex-col gap-3"}>
<PlanElement trainIdentifier={"S1"} trainHeading={"Dortmund Hbf"} stops={[
{
name: "Essen",
time: "10:00",
delay: 20
},
{
name: "Nicht Essen",
time: "10:00",
delay: 20
}
]}/>
<PlanElement trainIdentifier={"S1"} trainHeading={"Dortmund Hbf"} stops={[
{
name: "Essen",
time: "10:00",
delay: 20
},
{
name: "Nicht Essen",
time: "10:00",
delay: 20
}
]}/>
</div>
</PanelContent>
</PanelWrapper>
);
};
export default FahrplanPanel;
import { motion } from 'framer-motion';
import React from 'react';
import ProgressIndicator from "./ProgressIndicator";
const PlanElement = (props: {
trainIdentifier: string,
......@@ -10,32 +12,21 @@ const PlanElement = (props: {
}[]
}) => {
return (
<div className={"grid grid-cols-2"}>
<div className={"bg-white text-blue-900 px-4 py-0.5"}>
<div>
<div className={"flex flex-row gap-4 items-center font-semibold"}>
<div className={"flex justify-center items-center bg-green-700 text-lg h-8 w-16 leading-none"}>
{props.trainIdentifier}
</div>
<div className={"px-2 py-0.5 w-full"}>
<h3 className={"text-xl"}>
{props.trainHeading}
</h3>
</div>
{props.stops.map(stop => (
<>
<div>
{stop.time}
{(stop.delay && stop.delay > 0) && (
<span>
+ {stop.delay}
</span>
)}
</div>
<div>
{stop.name}
</div>
</>
))}
<motion.ol role="list" className="overflow-hidden" aria-hidden="true">
<ProgressIndicator first={true} id={"a"} name={"Meitnerweg"} arrival={new Date("Mon Jul 17 2023 23:56:56 GMT+0200 (Central European Summer Time)")}/>
<ProgressIndicator id={"b"} name={"Dortmund Universität S"} arrival={new Date("Mon Jul 17 2023 23:57:07 GMT+0200 (Central European Summer Time)")}/>
</motion.ol>
</div>
);
};
......
import TimeAgo from 'javascript-time-ago'
import de from 'javascript-time-ago/locale/de'
import {useEffect, useState} from "react";
export default function ProgressIndicator(props: {first?: boolean, id: string, name: string, arrival: Date}) {
const [timeUntil, setTimeUntil] = useState<string>("");
useEffect(() => {
// Setup TimeAgo
TimeAgo.addDefaultLocale(de);
const timeAgo = new TimeAgo('de-DE')
const update = () => {
setTimeUntil(timeAgo.format(new Date(), {future: true}));
}
setInterval(update, 1000);
update();
}, []);
return (
<li className={`relative ${!(props.first ?? false) ? "-mt-3.5" : "-mt-1"} text-sm text-zinc-300`} key={props.id}>
<div className={"flex flex-row gap-4"}>
<div className={"w-16 flex flex-row justify-center"}>
<div className="w-[3px] h-6 -mb-5 bg-zinc-400" aria-hidden="true" />
</div>
<div className={"flex-1"}></div>
</div>
<div className="relative flex flex-row items-center gap-4">
<div className={"w-16 flex flex-row justify-center"}>
<span className="h-9 flex items-center" aria-hidden="true">
<span className="relative z-10 w-[18px] h-[18px] flex items-center justify-center bg-zinc-400 rounded-full">
<span className="h-[12px] w-[12px] bg-zinc-900 rounded-full" />
</span>
</span>
</div>
<div className={"flex-1"}>
{props.name}
</div>
<div>
{timeUntil}
</div>
</div>
</li>
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment