Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Theming and add basic Component lib #37

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified liberica/bun.lockb
Binary file not shown.
5 changes: 3 additions & 2 deletions liberica/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export default [
{
rules: {
"@typescript-eslint/no-confusing-void-expression": "off",
},
},
"@typescript-eslint/restrict-template-expressions": ["warn", { "allowNumber": true }]
}
}
];
14 changes: 7 additions & 7 deletions liberica/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint src --cache",
"lint:clean": "eslint src",
"prettier": "prettier",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.7.3",
"i18next": "^23.12.2",
"axios": "^1.7.4",
"i18next": "^23.12.3",
"i18next-browser-languagedetector": "^8.0.0",
"leaflet": "^1.9.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.0.1",
"react-icons": "^5.2.1",
"react-icons": "^5.3.0",
"react-leaflet": "^4.2.1",
"react-router-dom": "^6.26.0",
"runtypes": "^6.7.0",
Expand All @@ -29,8 +29,8 @@
"@types/leaflet": "^1.9.12",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^8.0.1",
"@typescript-eslint/parser": "^8.0.1",
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
"@vitejs/plugin-react-swc": "^3.7.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.9.0",
Expand All @@ -46,7 +46,7 @@
"prettier-plugin-tailwindcss": "^0.6.6",
"tailwindcss": "^3.4.9",
"typescript": "^5.5.4",
"typescript-eslint": "^8.0.1",
"typescript-eslint": "^8.1.0",
"vite": "^5.4.0",
"vite-tsconfig-paths": "^5.0.1"
},
Expand Down
28 changes: 28 additions & 0 deletions liberica/src/assets/themes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"Rosé Pine Dawn": {
"base": "#faf4ed",
"surface": "#fffaf3",
"muted": "#9893a5",
"primary": "#d7827e",
"secondary": "#286983",

"onBase": "#575279",
"onSurface": "@onBase",
"onMuted": "@onBase",
"onPrimary": "@base",
"onSecondary": "@base"
},
"Rosé Pine": {
"base": "#191724",
"surface": "#1f1d2e",
"muted": "#6e6a86",
"primary": "#ebbcba",
"secondary": "#31748f",

"onBase": "#e0def4",
"onSurface": "@onBase",
"onMuted": "@onBase",
"onPrimary": "@base",
"onSecondary": "@base"
}
}
26 changes: 6 additions & 20 deletions liberica/src/components/InputElements.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Select } from "./lila/select";

export const TextInput = ({
className,
onTextChange,
Expand All @@ -10,7 +12,7 @@ export const TextInput = ({
return (
<input
type="text"
className={`block w-full rounded-md border border-slate-300 bg-white px-3 py-2 placeholder-slate-400 shadow-sm focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500 sm:text-sm ${className ?? ""}`}
className={`border-slate-300 bg-white placeholder-slate-400 focus:border-purple-500 focus:ring-purple-500 block w-full rounded-md border px-3 py-2 shadow-sm focus:outline-none focus:ring-1 sm:text-sm ${className ?? ""}`}
placeholder="Lila Pause"
onChange={(e) => {
const fn = {
Expand All @@ -28,17 +30,15 @@ export const TextInput = ({
};

export function DropDown<T extends string>({
className,
items,
onItemChange,
...props
}: React.SelectHTMLAttributes<HTMLSelectElement> & {
}: React.ComponentProps<"select"> & {
items: T[];
onItemChange?: (item: T) => void;
}) {
return (
<select
className={`block w-full rounded-md border border-slate-300 bg-white px-3 py-2 placeholder-slate-400 shadow-sm focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500 sm:text-sm ${className ?? ""}`}
<Select
onChange={(item) =>
onItemChange?.(items[item.currentTarget.selectedIndex])
}
Expand All @@ -47,20 +47,6 @@ export function DropDown<T extends string>({
{items.map((item) => (
<option key={item}>{item}</option>
))}
</select>
</Select>
);
}

export const Button = ({
className,
...props
}: React.PropsWithChildren<React.ButtonHTMLAttributes<HTMLButtonElement>>) => {
return (
<button
className={`middle none center flex justify-center rounded-lg bg-purple-500 px-6 py-2 font-sans text-sm font-bold text-white shadow-md shadow-pink-500/20 transition-all hover:shadow-lg hover:shadow-pink-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none ${className ?? ""}`}
{...props}
>
{props.children}
</button>
);
};
23 changes: 1 addition & 22 deletions liberica/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,9 @@
import { PropsWithChildren } from "react";
import { Button } from "./InputElements";
import { FaHome } from "react-icons/fa";
import { useNavigate } from "react-router-dom";

export function Navbar(props: PropsWithChildren) {
return (
<div
className="absolute bottom-0 flex w-max gap-3 bg-white p-2"
style={{
position: "fixed",
justifyContent: "space-between",
alignItems: "center",
zIndex: 1000,
}}
>
<div className="fixed bottom-0 z-auto flex max-h-14 w-dvw items-center justify-between gap-3 rounded-t-2xl bg-base p-2">
{props.children}
</div>
);
}

export function HomeButton() {
const navigate = useNavigate();

return (
<Button onClick={() => navigate("/")}>
<FaHome />
</Button>
);
}
Comment on lines -22 to -30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the home button?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am currently experimenting with the Navbar and couldn't find space for it (It also didn't fit the theme). I will put it back sometime later.

2 changes: 1 addition & 1 deletion liberica/src/components/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export function Spinner(props: { color?: string }) {
const color = props.color ?? "white";
return (
<svg
className="mr-3 h-5 w-5 animate-spin text-white"
className="text-white mr-3 h-5 w-5 animate-spin"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
Expand Down
15 changes: 7 additions & 8 deletions liberica/src/components/TeamCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,23 @@ export function TeamCard(props: {
const team = props.team;

const states = {
selected: "outline outline-solid outline-2 outline-slate-300",
default: "",
selected:
"flex w-full items-center rounded-xl cursor-pointer bg-muted/20",
default:
"flex w-full items-center rounded-xl cursor-pointer hover:bg-muted/10",
};

const state = props.selected ? "selected" : "default";

return (
<div
className={`my-1 flex w-full items-center rounded-md transition-all hover:bg-slate-100 ${states[state]}`}
onClick={props.onClick}
>
<div className={states[state]} onClick={props.onClick}>
<div
className="m-2 h-10 w-10 rounded"
className="m-2 h-10 w-10 rounded-xl"
style={{ backgroundColor: team.color }}
/>
<div className="flex flex-col justify-center">
<span className="font-semibold">{team.name}</span>
<span className="justify-end font-normal italic text-slate-400">
<span className="text-slate-400 justify-end font-normal italic">
{team.kind}
</span>
</div>
Expand Down
48 changes: 48 additions & 0 deletions liberica/src/components/lila/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/** eslint-disable react-refresh/only-export-components */ // This element does not have a state, so react-refresh is not needed

import { classes } from "components/lila";

const BASE =
"select-none transition-all font-sans disabled:pointer-events-none active:opacity-[0.85] disabled:opacity-50 disabled:shadow-none ";

const BUTTON_VARIANTS = {
primary:
"bg-primary text-on-primary rounded-xl font-bold hover:bg-primary/90 animate-entry",
secondary:
"bg-secondary text-on-secondary rounded-xl font-bold hover:bg-secondary/90 animate-entry",
muted: "bg-muted/10 text-on-muted rounded-xl font-bold hover:bg-muted/20 animate-entry",
};

const BUTTON_SIZES = {
sm: "py-1 px-4 text-xs",
md: "py-2 px-5 text-xs",
lg: "py-2.5 px-7 text-sm",
"sm-wide": "w-full py-1 text-sm",
"md-wide": "w-full py-2 text-md",
};

export type ButtonVariant = keyof typeof BUTTON_VARIANTS;
export type ButtonSize = keyof typeof BUTTON_SIZES;

export interface ButtonPropsExt {
variant: ButtonVariant;
size?: ButtonSize;
}

export type ButtonProps = Omit<React.ComponentProps<"button">, "className"> &
ButtonPropsExt;

export function Button(props: ButtonProps) {
return (
<button
className={classes(
BASE,
BUTTON_VARIANTS[props.variant],
BUTTON_SIZES[props.size ?? "sm"],
)}
{...props}
>
{props.children}
</button>
);
}
9 changes: 9 additions & 0 deletions liberica/src/components/lila/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Button } from "./button";
import { TextInput } from "./input.tsx";

// eslint-disable-next-line react-refresh/only-export-components
export function classes(...inputs: string[]): string {
return inputs.join(" ");
}

export { Button, TextInput };
16 changes: 16 additions & 0 deletions liberica/src/components/lila/input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type TextInputProps = Omit<
React.ComponentProps<"input">,
"className" | "type"
>;

export function TextInput(props: TextInputProps) {
return (
<input
{...props}
type="text"
className="text-text rounded-xl bg-muted/20 px-6 py-3 outline-none ring-muted focus:ring-2"
>
{props.children}
</input>
);
}
26 changes: 26 additions & 0 deletions liberica/src/components/lila/select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { classes } from "components/lila";

export type SelectProps = Omit<
React.ComponentProps<"select">,
"className" | "size"
> &
SelectPropsExt;
export interface SelectPropsExt {
size?: keyof typeof INPUT_SIZES;
noLogo?: boolean;
}

const BASE =
"text-on-muted rounded-xl bg-muted/20 outline-none ring-muted focus:ring-2";

const INPUT_SIZES = {
lg: "px-6 py-3 text-md",
};

export function Select(props: SelectProps) {
return (
<select className={classes(BASE, INPUT_SIZES[props.size ?? "lg"])}>
{props.children}
</select>
);
}
24 changes: 4 additions & 20 deletions liberica/src/components/map/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
import { createContext, useContext, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { MrXIcon, TrainIcon, DetectiveIcon } from "components/MapIcons";
import { Button } from "components/InputElements";
import { Marker } from "./Marker";
import { GameState, Stop, TeamState, Train } from "lib/bindings";
import { getStops } from "lib/api";
Expand All @@ -28,25 +27,10 @@ const CENTER: [number, number] = [49.0046, 8.403];
const DEFAULT_ZOOM = 15;

export type MapProps = React.PropsWithChildren<{
tileProps?: Partial<TileLayerProps>;
containerProps?: Partial<MapContainerProps>;
tileProps?: Omit<Partial<TileLayerProps>, "className">;
containerProps?: Omit<Partial<MapContainerProps>, "className">;
}>;

function ResetMapViewButton() {
const map = useMap();
const { t } = useTranslation();

return (
<div className="leaflet-top leaflet-center">
<div className="leaflet-control leaflet-bar">
<Button onClick={() => map.setView(CENTER, DEFAULT_ZOOM)}>
{t("ResetMapView")}
</Button>
</div>
</div>
);
}
Comment on lines -35 to -48
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? I find it pretty useful.


function TrainMarker(props: {
train: Train;
onClick?: (train: Train) => void;
Expand Down Expand Up @@ -119,7 +103,7 @@ export function Map(
<MapContainer
center={CENTER}
zoom={DEFAULT_ZOOM}
className="h-max w-max"
className="z-0 h-dvh w-dvw"
{...props.containerProps}
>
<TileLayer
Expand Down Expand Up @@ -173,7 +157,7 @@ export function Map(
</LayerGroup>
</LayersControl.Overlay>
</LayersControl>
<ResetMapViewButton />

{props.children}
</MapContainer>
);
Expand Down
6 changes: 4 additions & 2 deletions liberica/src/i18n/de.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"SelectTeam": "Team auswählen",
"SelectTeam": "Wähle dein Team",
"ResetMapView": "Kartenansicht zurücksetzen",
"CreateTeam": "Team erstellen",
"InvalidName": "ungültiger Name",
Expand All @@ -17,5 +17,7 @@
"time": "{{time, datetime(dateStyle: medium; timeStyle: long)}}",
"FailedParseReplay": "Replay-Datei konnte nicht geparst werden",
"ReplayTooBig": "Replay-Datei ist zu groß",
"Speed": "Geschwindigkeit"
"Speed": "Geschwindigkeit",
"Cancel": "Abbrechen",
"EmbarkPlaceholder": "Zum einsteigen auf Zug klicken"
}
Loading
Loading