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

Implemented new project structure

This aims to improve the contribution experience by separating panels out as much as possible
parent 150a193d
No related branches found
No related tags found
No related merge requests found
[
"config/default.json",
"config/foobar.json"
"config/foobar.json",
"config/default.json"
]
{
"priority": 0,
"id": "default",
"schedule": {
"always": true
},
"layout": {
"type": "xsplit",
"cut": 35,
"left": {
"type": "panel",
"name": "departure",
"config": "oh14"
"panels": [
{
"type": "test",
"position": {
"x": 0,
"y": 0,
"w": 2,
"h": 2
}
},
"right": {
"type": "ysplit",
"cut": 30,
"up": {
"type": "panel",
"name": "clock"
},
"down": {
"type": "panel",
"name": "mensaPlan"
{
"type": "demo",
"position": {
"x": 0,
"y": 0,
"w": 2,
"h": 2
}
}
}
]
}
{
"priority": 1,
"id": "foobar",
"schedule": {
"time": [
"times": [
{
"from": "26.06.2023"
"from": "2023-11-25T16:26",
"to": "2023-11-25T16:27"
}
]
},
"layout": {
"type": "xsplit",
"cut": 35,
"left": {
"type": "panel",
"name": "departure",
"config": "oh14"
},
"right": {
"type": "ysplit",
"cut": 30,
"up": {
"type": "panel",
"name": "clock"
},
"down": {
"type": "panel",
"name": "mensaPlan"
"panels": [
{
"type": "foobar",
"position": {
"x": 0,
"y": 0,
"w": 2,
"h": 2
}
}
}
]
}
{
"type": "object",
"properties": {
"schedule": {
"oneOf": [
{
"type": "object",
"properties": {
"always": {
"type": "boolean"
}
}
},
{
"type": "object",
"properties": {
"time": {
"type": "array",
"items": {
"from": {
"type": "string",
"format": "date-time"
},
"to": {
"type": "string",
"format": "date-time"
}
}
}
}
}
]
}
}
}
import React from 'react';
import './App.css';
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";
import LayoutElement from "./layout/LayoutElement";
import useLayout from "./hooks/useLayout";
import {PanelRenderers} from "./panels/_Panels";
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-950 text-white"} style={{fontFamily: "Inter"}}>
{/*<LayoutElement config={layout} />*/}
<div className={"overflow-hidden w-screen h-screen bg-zinc-950 text-white"} style={{fontFamily: "Inter"}}>
<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}/>
{
layout.panels.map(panel => {
const Renderer = PanelRenderers[panel.type];
return (
<Renderer definition={panel.config} />
)
})
}
</ScreenWrapper>
</div>
);
......
import {useEffect, useState} from "react";
import {useEffect, useRef, useState} from "react";
import {LayoutConfig} from "../types/LayoutConfig";
import {LayoutService} from "../services/LayoutService";
export default function useLayout(): LayoutConfig | null {
const currentId = useRef<string | null>(null);
const [layout, setLayout] = useState<LayoutConfig | null>(null);
useEffect(() => {
LayoutService.init().then(() => {
const refresh = () => {
console.log("Resync")
const activeLayout = LayoutService.getActiveLayout();
if(layout?.id !== activeLayout.id) {
console.log("Determined", activeLayout.id);
if(currentId.current !== activeLayout.id) {
console.log("Switching from", currentId.current, "to", activeLayout.id)
currentId.current = activeLayout.id;
setLayout(activeLayout);
}
};
......
@tailwind base;
@tailwind components;
@tailwind utilities;
.layout-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(max(45px, 45px), 1fr));
grid-template-rows: repeat(auto-fill, minmax(max(45px, 45px), 1fr));
grid-gap: 30px;
}
import React from 'react';
const HSplit = (props: {
top: any,
bottom: any,
split?: number
}) => {
const split = props.split ?? .5;
if(split <= 0 || split >= 1) {
throw new Error("HSplit has an invalid split");
}
return (
<div className={"flex flex-col w-full h-full gap-6"}>
<div style={{height: `${split * 100}%`}}>
{props.top}
</div>
<div className={"flex-1"}>
{props.bottom}
</div>
</div>
);
};
export default HSplit;
import React from 'react';
import {Layout} from "../types/LayoutConfig";
import config from "tailwindcss/defaultConfig";
const LayoutElement = (props: {config: Layout}) => {
// switch (config.type)s
const LayoutElement = (props: {children: any}) => {
// const LayoutElement = (props: {config: Layout}) => {
return (
<div className={"absolute"} style={{gridRowStart: 5, gridRowEnd: "span 2", gridColumnStart: 2, gridColumnEnd: "span 5"}}>
{props.children}
</div>
);
};
export default LayoutElement;
import React from 'react';
const VSplit = (props: {
left: any,
right: any,
split?: number
}) => {
const split = props.split ?? .5;
if(split <= 0 || split >= 1) {
throw new Error("VSplit has an invalid split");
}
return (
<div className={"flex flex-row w-full h-full gap-6"}>
<div style={{width: `${split * 100}%`}}>
{props.left}
</div>
<div className={"flex-1"}>
{props.right}
</div>
</div>
);
};
export default VSplit;
......@@ -5,7 +5,7 @@ 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"}>
<div className={"layout-grid flex-1 px-8 pb-10"}>
{props.children}
</div>
</div>
......
/*
* Hello dear future contributor,
* if you desire to create a panel, this file is for you. Here, you'll need to register your panel both with the
* typings system and with the rendering system.
*/
import React from "react";
import FahrplanPanel from "./Fahrplan/FahrplanPanel";
import {PanelDefinition} from "../types/LayoutConfig";
/*
* First, please claim a unique id for your panel here. Convention is that it is all lowercase, in snake-case to be
* precise. So if you want to call your panel "My awesome Panel", please claim "my-awesome-panel". Add it by adding
* `| "my-awesome-panel"` before the semicolon in the type below this comment.
*/
export type PanelTypes = "fahrplan";
/*
* Next, add your renderer. You'll get the definition that was written in the layout config as a prop. If you'd like to
* provide custom settings, you may add an object with these settings as the generic into the PanelDefinition.
* It will then be available as `definition.config`.
*/
export const PanelRenderers: {[panelType: string]: React.FC<any & {definition: PanelDefinition<any>}>} = {
"fahrplan": FahrplanPanel,
"test": () => (
<h1>Test!</h1>
),
"demo": () => (
<p>Demo!</p>
),
"foobar": () => (
<p>FooBar!</p>
)
};
/*
* That should have been it. Now, have fun implementing your renderer!
*/
import {LayoutConfig} from "../types/LayoutConfig";
const NO_LAYOUT_CONFIG: LayoutConfig = {
id: "",
schedule: {
always: true
},
panels: []
}
export class LayoutService {
static configs: LayoutConfig[] = [];
......@@ -25,22 +33,14 @@ export class LayoutService {
return config.schedule.times.reduce((accu, curr) => {
if(accu) return true;
return (curr.from <= now && curr.to >= now);
return (new Date(curr.from) <= now && new Date(curr.to) >= now);
}, false);
});
console.log(activeConfigs)
/* ToDo: This is not great, as it assumes there is always an active layout. If you don't configure this correctly,
consider yourself warned now and don't blame me */
return activeConfigs.reduce((accu, curr) => {
if(!accu) {
return curr;
}
if(accu.priority < curr.priority) {
return curr;
}
return accu;
})
return activeConfigs.at(0) ?? NO_LAYOUT_CONFIG;
}
}
import {PanelTypes} from "../panels/_Panels";
export type LayoutConfig = {
id: string,
priority: number,
schedule: LayoutSchedule,
layout: Layout
panels: PanelDefinition<any>[]
}
type LayoutSchedule = {
......@@ -10,29 +11,18 @@ type LayoutSchedule = {
} | {
always?: false,
times: {
from: Date,
to: Date
from: string,
to: string
}[]
}
export type Layout = XSplitLayout | YSplitLayout | Panel;
type XSplitLayout = {
type: "xsplit",
cut: number,
left: Layout,
right: Layout
}
type YSplitLayout = {
type: "ysplit",
cut: number,
left: Layout,
right: Layout
}
interface Panel {
type: "panel",
name: string,
config: Object
export type PanelDefinition<T> = {
type: PanelTypes,
position: {
x: number,
y: number,
w: number,
h: number
},
config: T
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment