Skip to content

Commit

Permalink
Allow color linking for themes
Browse files Browse the repository at this point in the history
  • Loading branch information
Comeza committed Jun 8, 2024
1 parent e3e6980 commit 7e34c3f
Show file tree
Hide file tree
Showing 21 changed files with 215 additions and 133 deletions.
31 changes: 24 additions & 7 deletions liberica/src/assets/themes.json
Original file line number Diff line number Diff line change
@@ -1,11 +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": "#faf4ed",
"surface": "#fffaf3",
"muted": "#9893a5",
"text": "#575279",
"primary": "#d7827e",
"secondary": "#286983",
"accent": "#ea9d34"
"base": "#191724",
"surface": "#1f1d2e",
"muted": "#6e6a86",
"primary": "#ebbcba",
"secondary": "#31748f",

"onBase": "#e0def4",
"onSurface": "@onBase",
"onMuted": "@onBase",
"onPrimary": "@base",
"onSecondary": "@base"
}
}
21 changes: 5 additions & 16 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 Down Expand Up @@ -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={`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 ?? ""}`}
<Select
onChange={(item) =>
onItemChange?.(items[item.currentTarget.selectedIndex])
}
Expand All @@ -47,17 +47,6 @@ export function DropDown<T extends string>({
{items.map((item) => (
<option key={item}>{item}</option>
))}
</select>
</Select>
);
}

export const Button = (props: React.ComponentProps<"button">) => {
return (
<button
className="hover: select-none rounded-lg bg-primary px-4 py-2 font-sans font-bold capitalize text-contrast transition-all"
{...props}
>
{props.children}
</button>
);
};
14 changes: 3 additions & 11 deletions liberica/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import { PropsWithChildren } from "react";
import { Button } from "./InputElements";
import { FaHome } from "react-icons/fa";
import { useNavigate } from "react-router-dom";
import { Button } from "components/lila/button";

export function Navbar(props: PropsWithChildren) {
return (
<div
className="absolute bottom-0 flex w-max gap-3 bg-surface p-2"
style={{
position: "fixed",
justifyContent: "space-between",
alignItems: "center",
zIndex: 1000,
}}
>
<div className="fixed bottom-0 z-auto flex w-dvw items-center justify-between gap-3 bg-base p-2">
{props.children}
</div>
);
Expand All @@ -23,7 +15,7 @@ export function HomeButton() {
const navigate = useNavigate();

return (
<Button onClick={() => navigate("/")}>
<Button onClick={() => navigate("/")} variant="primary" size="lg">
<FaHome />
</Button>
);
Expand Down
13 changes: 6 additions & 7 deletions liberica/src/components/TeamCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@ 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={`hover:bg-slate-100 my-1 flex w-full items-center rounded-md transition-all ${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">
Expand Down
29 changes: 16 additions & 13 deletions liberica/src/components/lila/button.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { classes } from "components/lila";

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

export const BUTTON_VARIANTS = {
filled: BASE + "bg-secondary text-contrast rounded-xl font-bold",
tonal:
BASE +
"bg-muted/10 text-contrast rounded-xl font-bold hover:bg-muted/20 animate-entry text-text",
text: BASE + " py-0 px-0 font-bold uppercase text-primary",
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",
};

export 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-xs",
"sm-wide": "w-full py-1 text-sm",
"md-wide": "w-full py-2 text-md",
};

export type ButtonVariant = keyof typeof BUTTON_VARIANTS;
Expand All @@ -27,14 +30,14 @@ export interface ButtonPropsExt {
export type ButtonProps = Omit<React.ComponentProps<"button">, "className"> &
ButtonPropsExt;

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

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>
);
}
9 changes: 9 additions & 0 deletions liberica/src/components/lila/select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type SelectProps = Omit<React.ComponentProps<"select">, "className">;

export function Select(props: SelectProps) {
return (
<select className="text-text rounded-xl bg-muted/20 px-6 py-3">
{props.children}
</select>
);
}
10 changes: 7 additions & 3 deletions liberica/src/components/map/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ 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 { Button } from "components/lila";
import { Marker } from "./Marker";
import { GameState, Stop, TeamState, Train } from "lib/bindings";
import { getStops } from "lib/api";
Expand All @@ -39,7 +39,11 @@ function ResetMapViewButton() {
return (
<div className="leaflet-top leaflet-center">
<div className="leaflet-control leaflet-bar">
<Button onClick={() => map.setView(CENTER, DEFAULT_ZOOM)}>
<Button
onClick={() => map.setView(CENTER, DEFAULT_ZOOM)}
variant={"primary"}
size="lg"
>
{t("ResetMapView")}
</Button>
</div>
Expand Down Expand Up @@ -119,7 +123,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
5 changes: 3 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,6 @@
"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"
}
7 changes: 4 additions & 3 deletions liberica/src/i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"SelectTeam": "Select team",
"SelectTeam": "Select your team",
"ResetMapView": "Reset Map View",
"CreateTeam": "Create",
"CreateTeam": "Create team",
"InvalidName": "invalid name",
"NameAlreadyExists": "name already exists",
"Detective": "Detective",
Expand All @@ -17,5 +17,6 @@
"time": "{{time, datetime(dateStyle: medium; timeStyle: long)}}",
"FailedParseReplay": "failed to parse replay file",
"ReplayTooBig": "replay file is too big",
"Speed": "Speed"
"Speed": "Speed",
"Cancel": "Cancel"
}
2 changes: 1 addition & 1 deletion liberica/src/lib/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function HexToRGB(hex: string): RGB {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);

if (!result) {
throw new Error("Could not parse Hex Color");
throw new Error("Could not parse Hex Color " + hex);
}

const rHex = parseInt(result[1], 16);
Expand Down
34 changes: 26 additions & 8 deletions liberica/src/lib/theme.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
import THEMES_JSON from "assets/themes.json";
import { HexToHSL } from "lib/colors";
import { fromCamelToKebabCase } from "lib/util";

export const THEMES: Record<string, Theme> = THEMES_JSON;

export interface Theme {
base: string;
surface: string;
text: string;
muted: string;
primary: string;
secondary: string;
accent: string;

onBase: string;
onSurface: string;
onPrimary: string;
onSecondary: string;
onMuted: string;
}

export function applyTheme(theme: Theme) {
const style = document.documentElement.style;
for (const [name, color] of Object.entries(theme)) {
const { h, s, l } = HexToHSL(color as string);
style.setProperty(
`--color-${name}`,
`${h.toString()} ${s.toString()}% ${l.toString()}%`,
);

for (const [name, val] of Object.entries(theme) as [
keyof Theme,
string,
][]) {
let color = val;

if (val.startsWith("@")) {
const link = val.substring(1) as keyof Theme;
color = theme[link];
console.log(name, "links to " + link + " --> " + color);
}

const { h, s, l } = HexToHSL(color);
const hsl = `${h.toString()} ${s.toString()}% ${l.toString()}%`;

console.log(name, fromCamelToKebabCase(name));
style.setProperty("--color-" + fromCamelToKebabCase(name), hsl);
}
}
3 changes: 3 additions & 0 deletions liberica/src/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ export const getContrastingTextColor = (bg: string) => {
export const clamp = (x: number, min: number, max: number) => {
return Math.min(Math.max(x, min), max);
};

export const fromCamelToKebabCase = (input: string) =>
input.replace(/[A-Z]/g, (letter) => "-" + letter.toLowerCase());
Loading

0 comments on commit 7e34c3f

Please sign in to comment.