From 64eb936bd941020979a073b38930d22bf3d83d72 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Thu, 23 May 2024 13:55:28 +0200 Subject: [PATCH 01/19] Theme setup --- liberica/package.json | 2 +- liberica/src/assets/themes.json | 11 +++++ liberica/src/lib/theme.ts | 19 ++++++++ liberica/tailwind.config.js | 79 ++++++++++++++++++++++++++++++--- 4 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 liberica/src/assets/themes.json create mode 100644 liberica/src/lib/theme.ts diff --git a/liberica/package.json b/liberica/package.json index 3700a94..08078ce 100644 --- a/liberica/package.json +++ b/liberica/package.json @@ -7,7 +7,7 @@ "dev": "vite", "build": "tsc && vite build", "lint": "eslint src --cache", - "lint:clean": "eslint src", + "prettier": "prettier", "preview": "vite preview" }, "dependencies": { diff --git a/liberica/src/assets/themes.json b/liberica/src/assets/themes.json new file mode 100644 index 0000000..709b1a5 --- /dev/null +++ b/liberica/src/assets/themes.json @@ -0,0 +1,11 @@ +{ + "Lila Pause Muted": { + "text-bright":"#150d19", + "text-dark": "#f6f2f8", + "surface": "#f6f2f8", + "primary": "#925ea5", + "secondary": "#c397d1", + "accent": "#bf80a4" + }, + +} diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts new file mode 100644 index 0000000..ed76907 --- /dev/null +++ b/liberica/src/lib/theme.ts @@ -0,0 +1,19 @@ +import THEMES_JSON from "assets/themes.json" + +export const RAW_THEMES: Record = THEMES_JSON; + +export type Theme = { + 'text-bright': string, + 'text-dark': string, + 'surface': string, + 'primary': string, + 'secondary': string, + 'accent': string +} + +export function applyTheme(theme: Theme) { + const style = window.getComputedStyle(document.documentElement); + for (const key of Object.keys(theme)) { + style.setProperty(`--${key}`, theme[key as keyof Theme]); + } +} diff --git a/liberica/tailwind.config.js b/liberica/tailwind.config.js index 614c86b..7b418d3 100644 --- a/liberica/tailwind.config.js +++ b/liberica/tailwind.config.js @@ -1,8 +1,77 @@ /** @type {import('tailwindcss').Config} */ export default { - content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], - theme: { - extend: {}, - }, - plugins: [], + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: { + colors: { + 'text': { + 50: 'var(--text-50)', + 100: 'var(--text-100)', + 200: 'var(--text-200)', + 300: 'var(--text-300)', + 400: 'var(--text-400)', + 500: 'var(--text-500)', + 600: 'var(--text-600)', + 700: 'var(--text-700)', + 800: 'var(--text-800)', + 900: 'var(--text-900)', + 950: 'var(--text-950)', + }, + 'background': { + 50: 'var(--background-50)', + 100: 'var(--background-100)', + 200: 'var(--background-200)', + 300: 'var(--background-300)', + 400: 'var(--background-400)', + 500: 'var(--background-500)', + 600: 'var(--background-600)', + 700: 'var(--background-700)', + 800: 'var(--background-800)', + 900: 'var(--background-900)', + 950: 'var(--background-950)', + }, + 'primary': { + 50: 'var(--primary-50)', + 100: 'var(--primary-100)', + 200: 'var(--primary-200)', + 300: 'var(--primary-300)', + 400: 'var(--primary-400)', + 500: 'var(--primary-500)', + 600: 'var(--primary-600)', + 700: 'var(--primary-700)', + 800: 'var(--primary-800)', + 900: 'var(--primary-900)', + 950: 'var(--primary-950)', + }, + 'secondary': { + 50: 'var(--secondary-50)', + 100: 'var(--secondary-100)', + 200: 'var(--secondary-200)', + 300: 'var(--secondary-300)', + 400: 'var(--secondary-400)', + 500: 'var(--secondary-500)', + 600: 'var(--secondary-600)', + 700: 'var(--secondary-700)', + 800: 'var(--secondary-800)', + 900: 'var(--secondary-900)', + 950: 'var(--secondary-950)', + }, + 'accent': { + 50: 'var(--accent-50)', + 100: 'var(--accent-100)', + 200: 'var(--accent-200)', + 300: 'var(--accent-300)', + 400: 'var(--accent-400)', + 500: 'var(--accent-500)', + 600: 'var(--accent-600)', + 700: 'var(--accent-700)', + 800: 'var(--accent-800)', + 900: 'var(--accent-900)', + 950: 'var(--accent-950)', + }, + }, + + }, + }, + plugins: [], }; From 7798ae310cbbedd5d079c830ed10ca476ac2f3ab Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Fri, 24 May 2024 04:06:50 +0200 Subject: [PATCH 02/19] Generate shades using value.js --- liberica/bun.lockb | Bin 179145 -> 165673 bytes liberica/package.json | 3 +- liberica/src/assets/themes.json | 8 +-- liberica/src/components/Navbar.tsx | 2 +- liberica/src/lib/theme.ts | 28 ++++++---- liberica/src/main.tsx | 3 + liberica/src/page/Admin.tsx | 5 +- liberica/tailwind.config.js | 86 +++++++---------------------- 8 files changed, 51 insertions(+), 84 deletions(-) diff --git a/liberica/bun.lockb b/liberica/bun.lockb index 850461ce3880a79de2161eac052588b695b0fbe4..36b518df459e1471eee88cc48cee5c5014169039 100755 GIT binary patch delta 50358 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

bmiW^i&ZHwYRw)P>*Mr@zYb@K zv{QiFyM}@&8q$@Gj!Vcu*vxokLP|zLY}_DqMy4`0E-pUV)0IQwvNN=4#H6MS zN*JtEr>Cp4L*uiO)hWZ{Q)7@`Mgu&8nV!wv2H^gGNCI=_J`woyJc4<0A&@!n!X7N0 zo2Q}^G>gEWxH#6Fe~d>{ed@}p@%{-clFvzI?fB_9)CLqSD5a10Vve&uwB@G+y6@eYb)|F)T@tQdpA8 ztO_2*F>l7}^@6QuW-?d4xd~Hnhj`@Dr626N+>fR3(fyIgay5dCi)NMvVx>}ftawBp z=E!$Pv%Wkr8fN$F%fk34HA~`EBUu~n-IKNA**#b&U)rDf@F#s)48Px>1@nhJk+hhT zx)*EAQ+u+}eCPnyl=(C$*f@ao5qNAjcF8Y*d=(yOP0EUmQzm5$Pfd?i4o_F7(G!zO zv`)+r(UhdPVR0!L@tJW6DUw00y0f;tW_RYx7yZCS@Y3$Mi|QVZ!cFbYa8sFT@98F1 z6)!N`(wMmyEbhg|3Vcal)wOIC=#lmkeh{ z1)D~)1%g($4_CbkejLqG870t|KOc)!aEYCpXxI&{tFE5IGKIi`@i}Y=GmA~g$iz@h zZXmuZB#|E&jM9m{#&kS0YAHV49Y__BsE!$ukrt;OB98own5^{l>_MsNaf8!Svr=Nw zQxcM5(-1~II8K?Ek(#0$l%ATbMDl#gQs$`orYcjMU%nA1Q>o&r6z0+J>l_f_Ymt&3 zz4j#&GZF@;sFRd&BND_s9dyT(QH3LAfY^WLB{73AsU)Hy@MzMJ5>vQ5i;MTteO*V7 zb`@_`_=$UT3&=MqJxlaoVu!HT9>pQ0FZ)WWJ|2Y$(MEfh3u^7I66+qZd7FHxTWG)R zqg?b53d$9lkfBz`WHi7_QL++3(}oPjl0%%R)#-!926OvLNGlR;fDEh=R4TFSXeuRy zFk*sA@y?l8f0^kCDT9^66Jj&tQ__+fXH+;2r4_r6Z$*(ZCNV=BEKRA#To5YV&Y?h( zfP#=S5w$5UNb}24sCXmI_scPYie<6+B7uQl_LU9A{=oCQF+~fljR;W59>QsKhTCtZBLp`ecP?I@YNK??J9sE{;*RycxBq8|Gl z^JRwx^KIc)MCWw}`~1g|m`12j4Zc?>@fIbuP8_CLbw&cFKx!X8JApa6RuYjtl?3vl z&Z&ZxU)hX1X_A9E83lh9#Gm~Dui9uli-kj3I-Yr&W~QrS;*`niv^4Y#o-_f@I${E& z7gvhDX#WIErZpxo4+GIha2(rEfI9s61bA+7vJ=BtO=PzESQ;1jPGqx93SQ4-c~vF# znAKRdY+A$&O@x4g-&e5+1us~~x{1rk0tSwK zFa!^`v0i+00W+?S!o|QNVf4}&MZtmXEKSg-K_-lZ58b!}i?0DYn0F;nK&2yNUcG?T zt0c1GX$8!wl1NO(kQWs&w@Pv|UjKJy#2t4syRYP9c4C#0x)XI!*_?OVQGZ1{aU-F! yTv5nebfUI}ECQ2F=~$GO7I}PsAzLR2-1wATSR#mP?xI~R$v~XW`Opwy{{IIUy|5wx delta 50302 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 ZITauK8Szde34NEPIZ6{p{POF({{jHFBSQcH diff --git a/liberica/package.json b/liberica/package.json index 08078ce..9b61748 100644 --- a/liberica/package.json +++ b/liberica/package.json @@ -22,7 +22,8 @@ "react-leaflet": "^4.2.1", "react-router-dom": "^6.26.0", "runtypes": "^6.7.0", - "use-interval": "^1.4.0" + "use-interval": "^1.4.0", + "values.js": "^2.1.1" }, "devDependencies": { "@eslint/js": "^9.9.0", diff --git a/liberica/src/assets/themes.json b/liberica/src/assets/themes.json index 709b1a5..82a883a 100644 --- a/liberica/src/assets/themes.json +++ b/liberica/src/assets/themes.json @@ -1,11 +1,9 @@ { - "Lila Pause Muted": { - "text-bright":"#150d19", - "text-dark": "#f6f2f8", + "Lila Pause": { + "text":"#150d19", "surface": "#f6f2f8", "primary": "#925ea5", "secondary": "#c397d1", "accent": "#bf80a4" - }, - + } } diff --git a/liberica/src/components/Navbar.tsx b/liberica/src/components/Navbar.tsx index 447e271..5227761 100644 --- a/liberica/src/components/Navbar.tsx +++ b/liberica/src/components/Navbar.tsx @@ -6,7 +6,7 @@ import { useNavigate } from "react-router-dom"; export function Navbar(props: PropsWithChildren) { return (

= THEMES_JSON; +export const THEMES: Record = THEMES_JSON; -export type Theme = { - 'text-bright': string, - 'text-dark': string, - 'surface': string, - 'primary': string, - 'secondary': string, - 'accent': string +export interface Theme { + text: string; + surface: string; + primary: string; + secondary: string; + accent: string; } + + export function applyTheme(theme: Theme) { - const style = window.getComputedStyle(document.documentElement); + const style = document.documentElement.style; for (const key of Object.keys(theme)) { - style.setProperty(`--${key}`, theme[key as keyof Theme]); + const shades = new Values(theme[key as keyof Theme], "base").all(22); + for (const [i, shade] of shades.entries()) { + style.setProperty(`--${key}-${i * 100 + 100}`, '#' + shade.hex); + } } } + diff --git a/liberica/src/main.tsx b/liberica/src/main.tsx index d931656..4ff2786 100644 --- a/liberica/src/main.tsx +++ b/liberica/src/main.tsx @@ -13,6 +13,7 @@ import "style/main.css"; import en_translation from "i18n/en.json"; import de_translation from "i18n/de.json"; +import { THEMES, applyTheme } from "lib/theme"; i18n.use(LanguageDetector) .use(initReactI18next) @@ -32,6 +33,8 @@ i18n.use(LanguageDetector) e instanceof Error && console.error("i18n init error", e.message), ); +applyTheme(THEMES["Lila Pause"]) + const rootElement = document.getElementById("root") as HTMLElement; ReactDOM.createRoot(rootElement).render( diff --git a/liberica/src/page/Admin.tsx b/liberica/src/page/Admin.tsx index 4520f09..2acd28f 100644 --- a/liberica/src/page/Admin.tsx +++ b/liberica/src/page/Admin.tsx @@ -3,5 +3,8 @@ import { useTranslation } from "react-i18next"; export function Admin() { const { t } = useTranslation(); - return <>{t("AdminPage")}; + return <> +
+
+ ; } diff --git a/liberica/tailwind.config.js b/liberica/tailwind.config.js index 7b418d3..d9f2e13 100644 --- a/liberica/tailwind.config.js +++ b/liberica/tailwind.config.js @@ -1,3 +1,16 @@ +function gen(name) { + const shades = [...new Array(9)] + .map((_, i) => i) + .map(i => 100 * i + 100) + .map(i => ({ [i]: `var(--${name}-${i})` })) + .reduce((prev, curr) => ({ ...prev, ...curr })); + + return { + ...shades, + DEFAULT: `var(--${name}-500)` + }; +} + /** @type {import('tailwindcss').Config} */ export default { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], @@ -5,73 +18,16 @@ export default { extend: { colors: { 'text': { - 50: 'var(--text-50)', - 100: 'var(--text-100)', - 200: 'var(--text-200)', - 300: 'var(--text-300)', - 400: 'var(--text-400)', - 500: 'var(--text-500)', - 600: 'var(--text-600)', - 700: 'var(--text-700)', - 800: 'var(--text-800)', - 900: 'var(--text-900)', - 950: 'var(--text-950)', - }, - 'background': { - 50: 'var(--background-50)', - 100: 'var(--background-100)', - 200: 'var(--background-200)', - 300: 'var(--background-300)', - 400: 'var(--background-400)', - 500: 'var(--background-500)', - 600: 'var(--background-600)', - 700: 'var(--background-700)', - 800: 'var(--background-800)', - 900: 'var(--background-900)', - 950: 'var(--background-950)', - }, - 'primary': { - 50: 'var(--primary-50)', - 100: 'var(--primary-100)', - 200: 'var(--primary-200)', - 300: 'var(--primary-300)', - 400: 'var(--primary-400)', - 500: 'var(--primary-500)', - 600: 'var(--primary-600)', - 700: 'var(--primary-700)', - 800: 'var(--primary-800)', - 900: 'var(--primary-900)', - 950: 'var(--primary-950)', - }, - 'secondary': { - 50: 'var(--secondary-50)', - 100: 'var(--secondary-100)', - 200: 'var(--secondary-200)', - 300: 'var(--secondary-300)', - 400: 'var(--secondary-400)', - 500: 'var(--secondary-500)', - 600: 'var(--secondary-600)', - 700: 'var(--secondary-700)', - 800: 'var(--secondary-800)', - 900: 'var(--secondary-900)', - 950: 'var(--secondary-950)', - }, - 'accent': { - 50: 'var(--accent-50)', - 100: 'var(--accent-100)', - 200: 'var(--accent-200)', - 300: 'var(--accent-300)', - 400: 'var(--accent-400)', - 500: 'var(--accent-500)', - 600: 'var(--accent-600)', - 700: 'var(--accent-700)', - 800: 'var(--accent-800)', - 900: 'var(--accent-900)', - 950: 'var(--accent-950)', + light: gen('text'), + dark: gen('text-dark'), + DEFAULT: gen('text') }, + 'surface': gen('surface'), + 'primary': gen('primary'), + 'secondary': gen('secondary'), + 'accent': gen('accent'), }, - - }, + } }, plugins: [], }; From de7770995ff9ac7bd94eca3db2469e4ffde23d6e Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Fri, 24 May 2024 04:11:35 +0200 Subject: [PATCH 03/19] Please the linter --- liberica/src/lib/theme.ts | 8 +++----- liberica/src/main.tsx | 2 +- liberica/src/page/Admin.tsx | 6 +----- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts index 9feb5ad..5865566 100644 --- a/liberica/src/lib/theme.ts +++ b/liberica/src/lib/theme.ts @@ -1,5 +1,5 @@ import THEMES_JSON from "assets/themes.json"; -import Values from "values.js" +import Values from "values.js"; export const THEMES: Record = THEMES_JSON; @@ -11,15 +11,13 @@ export interface Theme { accent: string; } - - export function applyTheme(theme: Theme) { const style = document.documentElement.style; for (const key of Object.keys(theme)) { const shades = new Values(theme[key as keyof Theme], "base").all(22); for (const [i, shade] of shades.entries()) { - style.setProperty(`--${key}-${i * 100 + 100}`, '#' + shade.hex); + const name = (i * 100 + 100).toString(); + style.setProperty(`--${key}-${name}`, "#" + shade.hex); } } } - diff --git a/liberica/src/main.tsx b/liberica/src/main.tsx index 4ff2786..086025b 100644 --- a/liberica/src/main.tsx +++ b/liberica/src/main.tsx @@ -33,7 +33,7 @@ i18n.use(LanguageDetector) e instanceof Error && console.error("i18n init error", e.message), ); -applyTheme(THEMES["Lila Pause"]) +applyTheme(THEMES["Lila Pause"]); const rootElement = document.getElementById("root") as HTMLElement; ReactDOM.createRoot(rootElement).render( diff --git a/liberica/src/page/Admin.tsx b/liberica/src/page/Admin.tsx index 2acd28f..cf82658 100644 --- a/liberica/src/page/Admin.tsx +++ b/liberica/src/page/Admin.tsx @@ -2,9 +2,5 @@ import { useTranslation } from "react-i18next"; export function Admin() { const { t } = useTranslation(); - - return <> -
-
- ; + return <>{t("AdminPage")}; } From 040a258f70083db65b8effc26b19cdd51a558674 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Mon, 3 Jun 2024 01:38:44 +0200 Subject: [PATCH 04/19] Theming tailwind integration --- liberica/bun.lockb | Bin 165673 -> 163384 bytes liberica/package.json | 3 +- liberica/src/assets/themes.json | 14 +++-- liberica/src/components/InputElements.tsx | 11 ++-- liberica/src/components/Spinner.tsx | 2 +- liberica/src/components/TeamCard.tsx | 4 +- liberica/src/components/lila/button.tsx | 43 ++++++++++++++ liberica/src/i18n/en.json | 2 +- liberica/src/lib/colors.ts | 66 ++++++++++++++++++++++ liberica/src/lib/theme.ts | 17 +++--- liberica/src/main.tsx | 4 +- liberica/src/page/Admin.tsx | 5 +- liberica/src/page/CreateTeam.tsx | 4 +- liberica/src/page/Debug.tsx | 57 +++++++++++++++++++ liberica/src/page/Game.tsx | 4 +- liberica/src/page/Home.tsx | 4 +- liberica/tailwind.config.js | 36 ++++-------- 17 files changed, 212 insertions(+), 64 deletions(-) create mode 100644 liberica/src/components/lila/button.tsx create mode 100644 liberica/src/lib/colors.ts create mode 100644 liberica/src/page/Debug.tsx diff --git a/liberica/bun.lockb b/liberica/bun.lockb index 36b518df459e1471eee88cc48cee5c5014169039..1e4a3243a6ce3eb7fc32b57c381a6e22a5bfd503 100755 GIT binary patch delta 30651 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 delta 31955 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 diff --git a/liberica/package.json b/liberica/package.json index 9b61748..08078ce 100644 --- a/liberica/package.json +++ b/liberica/package.json @@ -22,8 +22,7 @@ "react-leaflet": "^4.2.1", "react-router-dom": "^6.26.0", "runtypes": "^6.7.0", - "use-interval": "^1.4.0", - "values.js": "^2.1.1" + "use-interval": "^1.4.0" }, "devDependencies": { "@eslint/js": "^9.9.0", diff --git a/liberica/src/assets/themes.json b/liberica/src/assets/themes.json index 82a883a..8e6b7cd 100644 --- a/liberica/src/assets/themes.json +++ b/liberica/src/assets/themes.json @@ -1,9 +1,11 @@ { - "Lila Pause": { - "text":"#150d19", - "surface": "#f6f2f8", - "primary": "#925ea5", - "secondary": "#c397d1", - "accent": "#bf80a4" + "Rosé Pine": { + "base": "#faf4ed", + "surface": "#fffaf3", + "muted": "#9893a5", + "text": "#575279", + "primary": "#d7827e", + "secondary": "#286983", + "accent": "#ea9d34" } } diff --git a/liberica/src/components/InputElements.tsx b/liberica/src/components/InputElements.tsx index eb028ac..1128c47 100644 --- a/liberica/src/components/InputElements.tsx +++ b/liberica/src/components/InputElements.tsx @@ -10,7 +10,7 @@ export const TextInput = ({ return ( { const fn = { @@ -38,7 +38,7 @@ export function DropDown({ }) { return ( setText(e.target.value || "Test")} + > + + + + + + + + + + + + {variants + .flatMap((variant) => + sizes.map((size) => [variant, size]), + ) + .map(([variant, size]) => ( + + + + + + ))} + +
VariantSizeElement
{variant}{size} + + {text} + +
+
+ ); +} diff --git a/liberica/src/page/Game.tsx b/liberica/src/page/Game.tsx index 31e157a..ca1a4c2 100644 --- a/liberica/src/page/Game.tsx +++ b/liberica/src/page/Game.tsx @@ -106,10 +106,10 @@ export function Game() { const LandingPage = (
- + {t("ConnectionLost")} - {t("Reconnect")} + {t("Reconnect")}
); diff --git a/liberica/src/page/Home.tsx b/liberica/src/page/Home.tsx index e4de2e3..b201cd3 100644 --- a/liberica/src/page/Home.tsx +++ b/liberica/src/page/Home.tsx @@ -30,7 +30,7 @@ export function Home() { return (
-
+

{t("SelectTeam")}

{teams.map((team, index) => ( @@ -50,7 +50,7 @@ export function Home() { {t("JoinTeam")} {t("CreateTeam")} diff --git a/liberica/tailwind.config.js b/liberica/tailwind.config.js index d9f2e13..1e6f551 100644 --- a/liberica/tailwind.config.js +++ b/liberica/tailwind.config.js @@ -1,33 +1,17 @@ -function gen(name) { - const shades = [...new Array(9)] - .map((_, i) => i) - .map(i => 100 * i + 100) - .map(i => ({ [i]: `var(--${name}-${i})` })) - .reduce((prev, curr) => ({ ...prev, ...curr })); - - return { - ...shades, - DEFAULT: `var(--${name}-500)` - }; -} - /** @type {import('tailwindcss').Config} */ export default { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { - extend: { - colors: { - 'text': { - light: gen('text'), - dark: gen('text-dark'), - DEFAULT: gen('text') - }, - 'surface': gen('surface'), - 'primary': gen('primary'), - 'secondary': gen('secondary'), - 'accent': gen('accent'), - }, - } + colors: { + "surface": "hsl(var(--color-surface) / )", + "overlay": "hsl(var(--color-overlay) / )", + "muted": "hsl(var(--color-muted) / )", + "text": "hsl(var(--color-text) / )", + "contrast": "hsl(var(--color-surface) / )", + "primary": "hsl(var(--color-primary) / )", + "secondary": "hsl(var(--color-secondary) / )", + "accent": "hsl(var(--color-accent) / )" + }, }, plugins: [], }; From 65b8d539d472fe635e646549d4fdce2da0705ded Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Mon, 3 Jun 2024 02:00:45 +0200 Subject: [PATCH 05/19] Rebase main --- liberica/src/components/lila/button.tsx | 14 +++++++------- liberica/src/lib/theme.ts | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/liberica/src/components/lila/button.tsx b/liberica/src/components/lila/button.tsx index acf9bfa..4943abf 100644 --- a/liberica/src/components/lila/button.tsx +++ b/liberica/src/components/lila/button.tsx @@ -1,19 +1,19 @@ -const BUTTON_BASE = +const BASE = "select-none transition-all font-sans disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none "; export const BUTTON_VARIANTS = { - filled: BUTTON_BASE + "bg-secondary text-contrast rounded-xl font-bold", + filled: BASE + "bg-secondary text-contrast rounded-xl font-bold", tonal: - BUTTON_BASE + + BASE + "bg-muted/10 text-contrast rounded-xl font-bold hover:bg-muted/20 animate-entry text-text", - text: BUTTON_BASE + " py-0 px-0 font-bold uppercase text-primary", + text: BASE + " py-0 px-0 font-bold uppercase text-primary", }; 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", - wide: "w-full py-1 text-xs", + "sm-wide": "w-full py-1 text-xs", }; export type ButtonVariant = keyof typeof BUTTON_VARIANTS; @@ -31,9 +31,9 @@ export function BaseButton(props: ButtonProps) { return ( + {t("CreateTeam")} From 1d05d300de56e32ddfed983073abe62b639bd579 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Sat, 8 Jun 2024 14:55:39 +0200 Subject: [PATCH 07/19] Allow color linking for themes --- liberica/src/assets/themes.json | 31 ++++++++++++++----- liberica/src/components/InputElements.tsx | 21 +++---------- liberica/src/components/Navbar.tsx | 14 ++------- liberica/src/components/TeamCard.tsx | 13 ++++---- liberica/src/components/lila/button.tsx | 29 ++++++++++-------- liberica/src/components/lila/index.tsx | 8 +++++ liberica/src/components/lila/input.tsx | 16 ++++++++++ liberica/src/components/lila/select.tsx | 9 ++++++ liberica/src/components/map/Map.tsx | 10 ++++-- liberica/src/i18n/de.json | 5 +-- liberica/src/i18n/en.json | 7 +++-- liberica/src/lib/colors.ts | 2 +- liberica/src/lib/theme.ts | 34 ++++++++++++++++----- liberica/src/lib/util.ts | 3 ++ liberica/src/page/CreateTeam.tsx | 24 ++++++++++++--- liberica/src/page/Debug.tsx | 22 +++++++++----- liberica/src/page/Game.tsx | 17 +++++++---- liberica/src/page/Replay.tsx | 3 +- liberica/src/page/SelectTeam.tsx | 37 ++++++++++++----------- liberica/src/style/main.css | 23 +++----------- liberica/tailwind.config.js | 20 +++++++----- 21 files changed, 215 insertions(+), 133 deletions(-) create mode 100644 liberica/src/components/lila/index.tsx create mode 100644 liberica/src/components/lila/input.tsx create mode 100644 liberica/src/components/lila/select.tsx diff --git a/liberica/src/assets/themes.json b/liberica/src/assets/themes.json index 8e6b7cd..2b5bcee 100644 --- a/liberica/src/assets/themes.json +++ b/liberica/src/assets/themes.json @@ -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" } } diff --git a/liberica/src/components/InputElements.tsx b/liberica/src/components/InputElements.tsx index 1128c47..04a417e 100644 --- a/liberica/src/components/InputElements.tsx +++ b/liberica/src/components/InputElements.tsx @@ -1,3 +1,5 @@ +import { Select } from "./lila/select"; + export const TextInput = ({ className, onTextChange, @@ -28,17 +30,15 @@ export const TextInput = ({ }; export function DropDown({ - className, items, onItemChange, ...props -}: React.SelectHTMLAttributes & { +}: React.ComponentProps<"select"> & { items: T[]; onItemChange?: (item: T) => void; }) { return ( - + ); } - -export const Button = (props: React.ComponentProps<"button">) => { - return ( - - ); -}; diff --git a/liberica/src/components/Navbar.tsx b/liberica/src/components/Navbar.tsx index 5227761..c3debf8 100644 --- a/liberica/src/components/Navbar.tsx +++ b/liberica/src/components/Navbar.tsx @@ -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 ( -
+
{props.children}
); @@ -23,7 +15,7 @@ export function HomeButton() { const navigate = useNavigate(); return ( - ); diff --git a/liberica/src/components/TeamCard.tsx b/liberica/src/components/TeamCard.tsx index 347a18b..d8b7ff6 100644 --- a/liberica/src/components/TeamCard.tsx +++ b/liberica/src/components/TeamCard.tsx @@ -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 ( -
+
diff --git a/liberica/src/components/lila/button.tsx b/liberica/src/components/lila/button.tsx index 4943abf..f25b06e 100644 --- a/liberica/src/components/lila/button.tsx +++ b/liberica/src/components/lila/button.tsx @@ -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; @@ -27,14 +30,14 @@ export interface ButtonPropsExt { export type ButtonProps = Omit, "className"> & ButtonPropsExt; -export function BaseButton(props: ButtonProps) { +export function Button(props: ButtonProps) { return (
@@ -119,7 +123,7 @@ export function Map( = 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); } } diff --git a/liberica/src/lib/util.ts b/liberica/src/lib/util.ts index 029da71..c23eb51 100644 --- a/liberica/src/lib/util.ts +++ b/liberica/src/lib/util.ts @@ -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()); diff --git a/liberica/src/page/CreateTeam.tsx b/liberica/src/page/CreateTeam.tsx index 4bc4e41..2e9163f 100644 --- a/liberica/src/page/CreateTeam.tsx +++ b/liberica/src/page/CreateTeam.tsx @@ -1,4 +1,6 @@ -import { Button, DropDown, TextInput } from "components/InputElements"; +import { DropDown } from "components/InputElements"; +import { Button } from "components/lila/button"; +import { TextInput } from "components/lila/input"; import { defaultErrorHandler, postCreateTeam } from "lib/api"; import { TeamKind } from "lib/bindings"; import { FormEvent, useState } from "react"; @@ -39,12 +41,15 @@ export function CreateTeam() { style={{ backgroundColor: color }} >

{t("CreateTeam")}

- + setName(e.target.value)} + placeholder="Lila pause" + /> onItemChange={setKind} @@ -62,13 +67,24 @@ export function CreateTeam() { ))}
- +
); diff --git a/liberica/src/page/Debug.tsx b/liberica/src/page/Debug.tsx index 712551b..f7a4fb0 100644 --- a/liberica/src/page/Debug.tsx +++ b/liberica/src/page/Debug.tsx @@ -1,10 +1,12 @@ import { BUTTON_SIZES, BUTTON_VARIANTS, - BaseButton, + Button, ButtonSize, ButtonVariant, } from "components/lila/button"; +import { TextInput } from "components/lila/input"; +import { THEMES, applyTheme } from "lib/theme"; import { useState } from "react"; export function Debug() { @@ -14,13 +16,17 @@ export function Debug() { const [text, setText] = useState("Test"); return ( -
- + setText(e.target.value || "Test")} - > + > + + @@ -41,12 +47,12 @@ export function Debug() { ))} diff --git a/liberica/src/page/Game.tsx b/liberica/src/page/Game.tsx index ca1a4c2..70e6546 100644 --- a/liberica/src/page/Game.tsx +++ b/liberica/src/page/Game.tsx @@ -5,7 +5,7 @@ import { WebSocketApi } from "lib/websockets"; import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; import { HomeButton, Navbar } from "components/Navbar"; -import { Button } from "components/InputElements"; +import { Button } from "components/lila/button"; import { useTranslation } from "react-i18next"; export function Game() { @@ -67,7 +67,7 @@ export function Game() { }, [ws]); const Game = ( -
+
)} - @@ -104,12 +109,12 @@ export function Game() { ); const LandingPage = ( -
+
- + {t("ConnectionLost")} - {t("Reconnect")} + {t("Reconnect")}
); diff --git a/liberica/src/page/Replay.tsx b/liberica/src/page/Replay.tsx index d00255c..3ab8358 100644 --- a/liberica/src/page/Replay.tsx +++ b/liberica/src/page/Replay.tsx @@ -1,7 +1,7 @@ import { useRef, useState } from "react"; import { useInterval } from "use-interval"; import { GameStateContext, Map } from "components/map/Map"; -import { Button } from "components/InputElements"; +import { Button } from "components/lila"; import { HomeButton, Navbar } from "components/Navbar"; import { GameState } from "lib/bindings"; import { clamp } from "lib/util"; @@ -203,6 +203,7 @@ export function Replay() {
+ + {t("CreateTeam")} + +
); diff --git a/liberica/src/style/main.css b/liberica/src/style/main.css index a090dbb..0f6bcbe 100644 --- a/liberica/src/style/main.css +++ b/liberica/src/style/main.css @@ -1,25 +1,12 @@ @tailwind base; +@layer base { + html { + @apply text-on-base; + } +} @tailwind components; @tailwind utilities; -body,html { - margin: 0; - padding: 0 -} - -.h-max { - height: 100vh; -} - -.w-max { - width: 100vw; -} - -.flex-center { - justify-content: center; - align-items: center; -} - .leaflet-center { position: absolute; left: 50%; diff --git a/liberica/tailwind.config.js b/liberica/tailwind.config.js index 1e6f551..8bf6e6b 100644 --- a/liberica/tailwind.config.js +++ b/liberica/tailwind.config.js @@ -3,14 +3,18 @@ export default { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { colors: { - "surface": "hsl(var(--color-surface) / )", - "overlay": "hsl(var(--color-overlay) / )", - "muted": "hsl(var(--color-muted) / )", - "text": "hsl(var(--color-text) / )", - "contrast": "hsl(var(--color-surface) / )", - "primary": "hsl(var(--color-primary) / )", - "secondary": "hsl(var(--color-secondary) / )", - "accent": "hsl(var(--color-accent) / )" + base: "hsl(var(--color-base) / )", + surface: "hsl(var(--color-surface) / )", + muted: "hsl(var(--color-muted) / )", + primary: "hsl(var(--color-primary) / )", + secondary: "hsl(var(--color-secondary) / )", + on: { + base: "hsl(var(--color-on-base) / )", + surface: "hsl(var(--color-on-surface) / )", + muted: "hsl(var(--color-on-muted) / )", + primary: "hsl(var(--color-on-primary) / )", + secondary: "hsl(var(--color-on-secondary) / )", + } }, }, plugins: [], From 33ec9632fe75b640df3d97e4dd351405d89bf6ca Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Sat, 8 Jun 2024 15:09:15 +0200 Subject: [PATCH 08/19] Fix bg-white --- liberica/src/page/Admin.tsx | 5 ++++- liberica/src/page/SelectTeam.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/liberica/src/page/Admin.tsx b/liberica/src/page/Admin.tsx index a536060..23b8597 100644 --- a/liberica/src/page/Admin.tsx +++ b/liberica/src/page/Admin.tsx @@ -1,3 +1,6 @@ +import { useTranslation } from "react-i18next"; + export function Admin() { - return
Not yet implemented
; + const { t } = useTranslation(); + return
{t("AdminPage")}
; } diff --git a/liberica/src/page/SelectTeam.tsx b/liberica/src/page/SelectTeam.tsx index ad5f97c..7e821ed 100644 --- a/liberica/src/page/SelectTeam.tsx +++ b/liberica/src/page/SelectTeam.tsx @@ -28,7 +28,7 @@ export function SelectTeam() { return (
-
+
{t("SelectTeam")}
{teams.map((team, index) => ( From c65b75fb40bbd5d856b2108ff13f8f46c3a46924 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Wed, 12 Jun 2024 23:55:10 +0200 Subject: [PATCH 09/19] Apply suggestions --- liberica/src/lib/colors.ts | 6 +++--- liberica/src/lib/theme.ts | 10 +++++----- liberica/src/lib/util.ts | 2 +- liberica/src/page/CreateTeam.tsx | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/liberica/src/lib/colors.ts b/liberica/src/lib/colors.ts index e1be43f..980bfba 100644 --- a/liberica/src/lib/colors.ts +++ b/liberica/src/lib/colors.ts @@ -10,7 +10,7 @@ export interface HSL { l: number; } -export function HexToRGB(hex: string): RGB { +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) { @@ -27,8 +27,8 @@ export function HexToRGB(hex: string): RGB { return { r, g, b }; } -export function HexToHSL(hex: string): HSL { - const { r, g, b } = HexToRGB(hex); +export function hexToHSL(hex: string): HSL { + const { r, g, b } = hexToRGB(hex); const max = Math.max(r, g, b); const min = Math.min(r, g, b); diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts index 062b681..02edb19 100644 --- a/liberica/src/lib/theme.ts +++ b/liberica/src/lib/theme.ts @@ -1,6 +1,6 @@ import THEMES_JSON from "assets/themes.json"; -import { HexToHSL } from "lib/colors"; -import { fromCamelToKebabCase } from "lib/util"; +import { hexToHSL } from "lib/colors"; +import { camelToKebabCase } from "lib/util"; export const THEMES: Record = THEMES_JSON; @@ -33,10 +33,10 @@ export function applyTheme(theme: Theme) { console.log(name, "links to " + link + " --> " + color); } - const { h, s, l } = HexToHSL(color); + const { h, s, l } = hexToHSL(color); + // This has to be like this, to please the linter const hsl = `${h.toString()} ${s.toString()}% ${l.toString()}%`; - console.log(name, fromCamelToKebabCase(name)); - style.setProperty("--color-" + fromCamelToKebabCase(name), hsl); + style.setProperty("--color-" + camelToKebabCase(name), hsl); } } diff --git a/liberica/src/lib/util.ts b/liberica/src/lib/util.ts index c23eb51..f718d2b 100644 --- a/liberica/src/lib/util.ts +++ b/liberica/src/lib/util.ts @@ -22,5 +22,5 @@ export const clamp = (x: number, min: number, max: number) => { return Math.min(Math.max(x, min), max); }; -export const fromCamelToKebabCase = (input: string) => +export const camelToKebabCase = (input: string) => input.replace(/[A-Z]/g, (letter) => "-" + letter.toLowerCase()); diff --git a/liberica/src/page/CreateTeam.tsx b/liberica/src/page/CreateTeam.tsx index 2e9163f..a4728d5 100644 --- a/liberica/src/page/CreateTeam.tsx +++ b/liberica/src/page/CreateTeam.tsx @@ -48,7 +48,7 @@ export function CreateTeam() { setName(e.target.value)} - placeholder="Lila pause" + placeholder="Lila Pause" /> From 1ae2d2d9533eb6d7ce5e4c9bc352e56397632236 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Thu, 13 Jun 2024 00:43:01 +0200 Subject: [PATCH 10/19] Allow restrict-template-expressions for numbers --- liberica/eslint.config.js | 5 +++-- liberica/src/lib/theme.ts | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/liberica/eslint.config.js b/liberica/eslint.config.js index 339bd5d..f9ce8c0 100644 --- a/liberica/eslint.config.js +++ b/liberica/eslint.config.js @@ -76,6 +76,7 @@ export default [ { rules: { "@typescript-eslint/no-confusing-void-expression": "off", - }, - }, + "@typescript-eslint/restrict-template-expressions": ["warn", { "allowNumber": true }] + } + } ]; diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts index 02edb19..93c5a17 100644 --- a/liberica/src/lib/theme.ts +++ b/liberica/src/lib/theme.ts @@ -34,8 +34,7 @@ export function applyTheme(theme: Theme) { } const { h, s, l } = hexToHSL(color); - // This has to be like this, to please the linter - const hsl = `${h.toString()} ${s.toString()}% ${l.toString()}%`; + const hsl = `${h} ${s}% ${l}%`; style.setProperty("--color-" + camelToKebabCase(name), hsl); } From ef9f754a8df8ee6c81a1209fe6b5ee071df4d7c5 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Thu, 13 Jun 2024 01:14:09 +0200 Subject: [PATCH 11/19] Make themes persistent --- liberica/src/lib/theme.ts | 36 +++++++++++++++++++++++++++++------- liberica/src/main.tsx | 8 ++++++-- liberica/src/page/Debug.tsx | 7 ++++--- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts index 93c5a17..004a7c4 100644 --- a/liberica/src/lib/theme.ts +++ b/liberica/src/lib/theme.ts @@ -2,7 +2,9 @@ import THEMES_JSON from "assets/themes.json"; import { hexToHSL } from "lib/colors"; import { camelToKebabCase } from "lib/util"; -export const THEMES: Record = THEMES_JSON; +export type ThemeName = keyof typeof THEMES_JSON; +export const THEMES: Record = THEMES_JSON; +export const THEME_NAMES = Object.keys(THEMES) as ThemeName[]; export interface Theme { base: string; @@ -18,19 +20,35 @@ export interface Theme { onMuted: string; } -export function applyTheme(theme: Theme) { +const LOCAL_STORAGE_THEME_KEY = "theme"; + +export function saveTheme(themeName?: ThemeName) { + if (!themeName) { + localStorage.removeItem(LOCAL_STORAGE_THEME_KEY); + return; + } + + localStorage.setItem(LOCAL_STORAGE_THEME_KEY, themeName); +} + +export function loadTheme(): ThemeName | null { + return localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as ThemeName | null; +} + +export function applyTheme(themeName: ThemeName, persistent = false) { + console.assert(THEME_NAMES.includes(themeName), "Set Theme does not exist"); + const style = document.documentElement.style; + const theme = THEMES[themeName]; + + type ThemeEntry = [keyof Theme, string]; - for (const [name, val] of Object.entries(theme) as [ - keyof Theme, - string, - ][]) { + for (const [name, val] of Object.entries(theme) as ThemeEntry[]) { 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); @@ -38,4 +56,8 @@ export function applyTheme(theme: Theme) { style.setProperty("--color-" + camelToKebabCase(name), hsl); } + + if (persistent) { + saveTheme(themeName); + } } diff --git a/liberica/src/main.tsx b/liberica/src/main.tsx index 29d138a..d14b99c 100644 --- a/liberica/src/main.tsx +++ b/liberica/src/main.tsx @@ -14,7 +14,7 @@ import "style/main.css"; import en_translation from "i18n/en.json"; import de_translation from "i18n/de.json"; -import { THEMES, applyTheme } from "lib/theme"; +import { THEMES, ThemeName, applyTheme, loadTheme } from "lib/theme"; i18n.use(LanguageDetector) .use(initReactI18next) @@ -34,7 +34,11 @@ i18n.use(LanguageDetector) e instanceof Error && console.error("i18n init error", e.message), ); -applyTheme(Object.values(THEMES)[0]); +const defaultTheme = Object.keys(THEMES)[0] as ThemeName; +const setTheme = loadTheme(); +console.log("Default Theme:", defaultTheme); +console.log("Set Theme:", defaultTheme); +applyTheme(setTheme ?? defaultTheme); const rootElement = document.getElementById("root") as HTMLElement; ReactDOM.createRoot(rootElement).render( diff --git a/liberica/src/page/Debug.tsx b/liberica/src/page/Debug.tsx index f7a4fb0..26cb357 100644 --- a/liberica/src/page/Debug.tsx +++ b/liberica/src/page/Debug.tsx @@ -6,7 +6,7 @@ import { ButtonVariant, } from "components/lila/button"; import { TextInput } from "components/lila/input"; -import { THEMES, applyTheme } from "lib/theme"; +import { THEMES, ThemeName, applyTheme } from "lib/theme"; import { useState } from "react"; export function Debug() { @@ -14,7 +14,6 @@ export function Debug() { const variants = Object.keys(BUTTON_VARIANTS) as ButtonVariant[]; const [text, setText] = useState("Test"); - return (
setText(e.target.value || "Test")} > - applyTheme(e.target.value as ThemeName, true)} + > {Object.keys(THEMES).map((theme) => ( ))} From a995a3222701dc76d7e2adfcdf37cfc89d567796 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Thu, 13 Jun 2024 01:16:44 +0200 Subject: [PATCH 12/19] Remove debug statements --- liberica/src/lib/theme.ts | 4 +--- liberica/src/main.tsx | 6 +----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts index 004a7c4..1e11e26 100644 --- a/liberica/src/lib/theme.ts +++ b/liberica/src/lib/theme.ts @@ -57,7 +57,5 @@ export function applyTheme(themeName: ThemeName, persistent = false) { style.setProperty("--color-" + camelToKebabCase(name), hsl); } - if (persistent) { - saveTheme(themeName); - } + if (persistent) saveTheme(themeName); } diff --git a/liberica/src/main.tsx b/liberica/src/main.tsx index d14b99c..a98e36f 100644 --- a/liberica/src/main.tsx +++ b/liberica/src/main.tsx @@ -34,11 +34,7 @@ i18n.use(LanguageDetector) e instanceof Error && console.error("i18n init error", e.message), ); -const defaultTheme = Object.keys(THEMES)[0] as ThemeName; -const setTheme = loadTheme(); -console.log("Default Theme:", defaultTheme); -console.log("Set Theme:", defaultTheme); -applyTheme(setTheme ?? defaultTheme); +applyTheme(loadTheme() ?? Object.keys(THEMES)[0] as ThemeName); const rootElement = document.getElementById("root") as HTMLElement; ReactDOM.createRoot(rootElement).render( From 4ed8cd3929501433074ef7e0a1572799732c0dda Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Thu, 13 Jun 2024 01:19:37 +0200 Subject: [PATCH 13/19] Please the linter --- liberica/src/main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liberica/src/main.tsx b/liberica/src/main.tsx index a98e36f..cf9d43a 100644 --- a/liberica/src/main.tsx +++ b/liberica/src/main.tsx @@ -34,7 +34,7 @@ i18n.use(LanguageDetector) e instanceof Error && console.error("i18n init error", e.message), ); -applyTheme(loadTheme() ?? Object.keys(THEMES)[0] as ThemeName); +applyTheme(loadTheme() ?? (Object.keys(THEMES)[0] as ThemeName)); const rootElement = document.getElementById("root") as HTMLElement; ReactDOM.createRoot(rootElement).render( From b25b470a39754657e5867577cdb770b288fbf0cb Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Fri, 14 Jun 2024 00:30:17 +0200 Subject: [PATCH 14/19] Use shadow realm Web APIs to sync Themes between tabs --- liberica/src/components/Navbar.tsx | 15 +------- liberica/src/components/map/Map.tsx | 26 ++----------- liberica/src/i18n/de.json | 3 +- liberica/src/i18n/en.json | 3 +- liberica/src/lib/theme.ts | 34 ++++++++++++++-- liberica/src/page/Debug.tsx | 7 +++- liberica/src/page/Game.tsx | 60 +++++++++++++++++------------ liberica/src/page/Replay.tsx | 4 +- 8 files changed, 82 insertions(+), 70 deletions(-) diff --git a/liberica/src/components/Navbar.tsx b/liberica/src/components/Navbar.tsx index c3debf8..03b15a6 100644 --- a/liberica/src/components/Navbar.tsx +++ b/liberica/src/components/Navbar.tsx @@ -1,22 +1,9 @@ import { PropsWithChildren } from "react"; -import { FaHome } from "react-icons/fa"; -import { useNavigate } from "react-router-dom"; -import { Button } from "components/lila/button"; export function Navbar(props: PropsWithChildren) { return ( -
+
{props.children}
); } - -export function HomeButton() { - const navigate = useNavigate(); - - return ( - - ); -} diff --git a/liberica/src/components/map/Map.tsx b/liberica/src/components/map/Map.tsx index 74074a8..557857e 100644 --- a/liberica/src/components/map/Map.tsx +++ b/liberica/src/components/map/Map.tsx @@ -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/lila"; import { Marker } from "./Marker"; import { GameState, Stop, TeamState, Train } from "lib/bindings"; import { getStops } from "lib/api"; @@ -28,29 +27,10 @@ const CENTER: [number, number] = [49.0046, 8.403]; const DEFAULT_ZOOM = 15; export type MapProps = React.PropsWithChildren<{ - tileProps?: Partial; - containerProps?: Partial; + tileProps?: Omit, "className">; + containerProps?: Omit, "className">; }>; -function ResetMapViewButton() { - const map = useMap(); - const { t } = useTranslation(); - - return ( -
-
- -
-
- ); -} - function TrainMarker(props: { train: Train; onClick?: (train: Train) => void; @@ -177,7 +157,7 @@ export function Map( - + {props.children} ); diff --git a/liberica/src/i18n/de.json b/liberica/src/i18n/de.json index 3f5e6ba..ea02db6 100644 --- a/liberica/src/i18n/de.json +++ b/liberica/src/i18n/de.json @@ -18,5 +18,6 @@ "FailedParseReplay": "Replay-Datei konnte nicht geparst werden", "ReplayTooBig": "Replay-Datei ist zu groß", "Speed": "Geschwindigkeit", - "Cancel": "Abbrechen" + "Cancel": "Abbrechen", + "EmbarkPlaceholder": "Zum einsteigen auf Zug klicken" } diff --git a/liberica/src/i18n/en.json b/liberica/src/i18n/en.json index d6b004e..f7c7bb8 100644 --- a/liberica/src/i18n/en.json +++ b/liberica/src/i18n/en.json @@ -18,5 +18,6 @@ "FailedParseReplay": "failed to parse replay file", "ReplayTooBig": "replay file is too big", "Speed": "Speed", - "Cancel": "Cancel" + "Cancel": "Cancel", + "EmbarkPlaceholder": "Click on train to embark" } diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts index 1e11e26..c5042f2 100644 --- a/liberica/src/lib/theme.ts +++ b/liberica/src/lib/theme.ts @@ -2,10 +2,25 @@ import THEMES_JSON from "assets/themes.json"; import { hexToHSL } from "lib/colors"; import { camelToKebabCase } from "lib/util"; +const LOCAL_STORAGE_THEME_KEY = "theme"; +const BROADCAST_CHANNEL_NAME = "theme"; + export type ThemeName = keyof typeof THEMES_JSON; + export const THEMES: Record = THEMES_JSON; export const THEME_NAMES = Object.keys(THEMES) as ThemeName[]; +export const BROADCAST_CHANNEL = new BroadcastChannel(BROADCAST_CHANNEL_NAME); + +BROADCAST_CHANNEL.onmessage = (msg: { data: ThemeName }) => { + console.debug( + `Received Boradcast Message on channel ${BROADCAST_CHANNEL_NAME}: `, + msg.data, + ); + if (!THEME_NAMES.includes(msg.data)) return; + applyTheme(msg.data, { persistent: false, broadcast: false }); +}; + export interface Theme { base: string; surface: string; @@ -20,8 +35,6 @@ export interface Theme { onMuted: string; } -const LOCAL_STORAGE_THEME_KEY = "theme"; - export function saveTheme(themeName?: ThemeName) { if (!themeName) { localStorage.removeItem(LOCAL_STORAGE_THEME_KEY); @@ -35,8 +48,22 @@ export function loadTheme(): ThemeName | null { return localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as ThemeName | null; } -export function applyTheme(themeName: ThemeName, persistent = false) { +export interface ApplyThemeOptions { + persistent: boolean; + broadcast: boolean; +} + +const applyThemeOptionsDefaults: ApplyThemeOptions = { + persistent: false, + broadcast: false, +}; + +export function applyTheme(themeName: ThemeName, options: ApplyThemeOptions) { console.assert(THEME_NAMES.includes(themeName), "Set Theme does not exist"); + const { persistent, broadcast } = { + ...applyThemeOptionsDefaults, + ...options, + }; const style = document.documentElement.style; const theme = THEMES[themeName]; @@ -58,4 +85,5 @@ export function applyTheme(themeName: ThemeName, persistent = false) { } if (persistent) saveTheme(themeName); + if (broadcast) BROADCAST_CHANNEL.postMessage(themeName); } diff --git a/liberica/src/page/Debug.tsx b/liberica/src/page/Debug.tsx index 26cb357..28fb73c 100644 --- a/liberica/src/page/Debug.tsx +++ b/liberica/src/page/Debug.tsx @@ -22,7 +22,12 @@ export function Debug() { > { From 32bd72243e7279fccbd5f7fb9e01b99f069b1051 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Mon, 17 Jun 2024 04:32:37 +0200 Subject: [PATCH 15/19] Hacky drawer navbar, might revert later --- liberica/src/components/Navbar.tsx | 57 +++++++++++++++++++++++++++++ liberica/src/lib/theme.ts | 11 +++--- liberica/src/page/Game.tsx | 58 ++++++++++++++++-------------- liberica/src/style/main.css | 1 + liberica/tailwind.config.js | 2 ++ 5 files changed, 98 insertions(+), 31 deletions(-) diff --git a/liberica/src/components/Navbar.tsx b/liberica/src/components/Navbar.tsx index 03b15a6..885a141 100644 --- a/liberica/src/components/Navbar.tsx +++ b/liberica/src/components/Navbar.tsx @@ -1,3 +1,5 @@ +import { motion, useSpring } from "framer-motion"; +import React, { useRef } from "react"; import { PropsWithChildren } from "react"; export function Navbar(props: PropsWithChildren) { @@ -7,3 +9,58 @@ export function Navbar(props: PropsWithChildren) {
); } + +export function NavbarHeader(props: PropsWithChildren) { + return <>{props.children}; +} + +export function NavbarBody(props: PropsWithChildren) { + return <>{props.children}; +} + +export function NavbarDrawer(props: PropsWithChildren) { + const y = useSpring(0, { bounce: 0, duration: window.innerHeight / 3 }); // 854 / 4 = 285ms + const headerRef = useRef(null); + const bodyRef = useRef(null); + + const body = React.Children.map(props.children, (child) => { + if (!React.isValidElement(child)) return; + if (child.type !== NavbarBody) return; + return child; + }); + + const header = React.Children.map(props.children, (child) => { + if (!React.isValidElement(child)) return; + if (child.type !== NavbarHeader) return; + return child; + }); + + const bodyHeight = bodyRef.current?.getBoundingClientRect().height ?? 0; + const headerHeight = headerRef.current?.getBoundingClientRect().height ?? 0; + + return ( + { + y.stop(); + if (info.velocity.y < -10) { + y.set(-bodyHeight); + return; + } + y.set(0); + }} + > +
+
+
+
+
{header}
+
+
+ {body} +
+
+ ); +} diff --git a/liberica/src/lib/theme.ts b/liberica/src/lib/theme.ts index c5042f2..b15f8b7 100644 --- a/liberica/src/lib/theme.ts +++ b/liberica/src/lib/theme.ts @@ -49,16 +49,19 @@ export function loadTheme(): ThemeName | null { } export interface ApplyThemeOptions { - persistent: boolean; - broadcast: boolean; + persistent?: boolean; + broadcast?: boolean; } -const applyThemeOptionsDefaults: ApplyThemeOptions = { +const applyThemeOptionsDefaults: Required = { persistent: false, broadcast: false, }; -export function applyTheme(themeName: ThemeName, options: ApplyThemeOptions) { +export function applyTheme( + themeName: ThemeName, + options: ApplyThemeOptions = {}, +) { console.assert(THEME_NAMES.includes(themeName), "Set Theme does not exist"); const { persistent, broadcast } = { ...applyThemeOptionsDefaults, diff --git a/liberica/src/page/Game.tsx b/liberica/src/page/Game.tsx index d48911e..3388a0c 100644 --- a/liberica/src/page/Game.tsx +++ b/liberica/src/page/Game.tsx @@ -4,7 +4,7 @@ import { GameState, Team, Train } from "lib/bindings"; import { WebSocketApi } from "lib/websockets"; import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; -import { Navbar } from "components/Navbar"; +import { NavbarBody, NavbarDrawer, NavbarHeader } from "components/Navbar"; import { Button } from "components/lila/button"; import { useTranslation } from "react-i18next"; @@ -66,20 +66,6 @@ export function Game() { } }, [ws]); - const widgetEmbarkedTrain = ( -
- {embarkedTrain ? ( - - {embarkedTrain.line_name} {embarkedTrain.direction} - - ) : ( - - {t("EmbarkPlaceholder")} - - )} -
- ); - const Game = (
@@ -100,18 +86,36 @@ export function Game() { onTrainClick={embark} /> - - {widgetEmbarkedTrain} - - - + + +
+ {embarkedTrain ? ( + + {embarkedTrain.line_name}{" "} + {embarkedTrain.direction} + + ) : ( + + {t("EmbarkPlaceholder")} + + )} +
+ + +
+ +
+ lol +
+
+
); diff --git a/liberica/src/style/main.css b/liberica/src/style/main.css index 0f6bcbe..b3ac6ee 100644 --- a/liberica/src/style/main.css +++ b/liberica/src/style/main.css @@ -2,6 +2,7 @@ @layer base { html { @apply text-on-base; + overscroll-behavior: none; } } @tailwind components; diff --git a/liberica/tailwind.config.js b/liberica/tailwind.config.js index 8bf6e6b..c4320d1 100644 --- a/liberica/tailwind.config.js +++ b/liberica/tailwind.config.js @@ -3,6 +3,8 @@ export default { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { colors: { + red: "red", // For debugging + base: "hsl(var(--color-base) / )", surface: "hsl(var(--color-surface) / )", muted: "hsl(var(--color-muted) / )", From d90f9fc6a9a954b67166a401be8f85649e255da6 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Thu, 20 Jun 2024 22:13:48 +0200 Subject: [PATCH 16/19] Revert drawer navbar Will add modify the navbar in another PR --- liberica/src/components/Navbar.tsx | 57 ------------------------------ liberica/src/page/Game.tsx | 55 +++++++++++++--------------- 2 files changed, 24 insertions(+), 88 deletions(-) diff --git a/liberica/src/components/Navbar.tsx b/liberica/src/components/Navbar.tsx index 885a141..03b15a6 100644 --- a/liberica/src/components/Navbar.tsx +++ b/liberica/src/components/Navbar.tsx @@ -1,5 +1,3 @@ -import { motion, useSpring } from "framer-motion"; -import React, { useRef } from "react"; import { PropsWithChildren } from "react"; export function Navbar(props: PropsWithChildren) { @@ -9,58 +7,3 @@ export function Navbar(props: PropsWithChildren) {
); } - -export function NavbarHeader(props: PropsWithChildren) { - return <>{props.children}; -} - -export function NavbarBody(props: PropsWithChildren) { - return <>{props.children}; -} - -export function NavbarDrawer(props: PropsWithChildren) { - const y = useSpring(0, { bounce: 0, duration: window.innerHeight / 3 }); // 854 / 4 = 285ms - const headerRef = useRef(null); - const bodyRef = useRef(null); - - const body = React.Children.map(props.children, (child) => { - if (!React.isValidElement(child)) return; - if (child.type !== NavbarBody) return; - return child; - }); - - const header = React.Children.map(props.children, (child) => { - if (!React.isValidElement(child)) return; - if (child.type !== NavbarHeader) return; - return child; - }); - - const bodyHeight = bodyRef.current?.getBoundingClientRect().height ?? 0; - const headerHeight = headerRef.current?.getBoundingClientRect().height ?? 0; - - return ( - { - y.stop(); - if (info.velocity.y < -10) { - y.set(-bodyHeight); - return; - } - y.set(0); - }} - > -
-
-
-
-
{header}
-
-
- {body} -
-
- ); -} diff --git a/liberica/src/page/Game.tsx b/liberica/src/page/Game.tsx index 3388a0c..b2c8e45 100644 --- a/liberica/src/page/Game.tsx +++ b/liberica/src/page/Game.tsx @@ -4,7 +4,7 @@ import { GameState, Team, Train } from "lib/bindings"; import { WebSocketApi } from "lib/websockets"; import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; -import { NavbarBody, NavbarDrawer, NavbarHeader } from "components/Navbar"; +import { Navbar } from "components/Navbar"; import { Button } from "components/lila/button"; import { useTranslation } from "react-i18next"; @@ -86,36 +86,29 @@ export function Game() { onTrainClick={embark} /> - - -
- {embarkedTrain ? ( - - {embarkedTrain.line_name}{" "} - {embarkedTrain.direction} - - ) : ( - - {t("EmbarkPlaceholder")} - - )} -
- - -
- -
- lol -
-
-
+ +
+ {embarkedTrain ? ( + + {embarkedTrain.line_name}{" "} + {embarkedTrain.direction} + + ) : ( + + {t("EmbarkPlaceholder")} + + )} +
+ + +
); From c01e698350f405719b6183ff01f09b267cc55a4f Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Thu, 20 Jun 2024 23:33:30 +0200 Subject: [PATCH 17/19] Setup select element --- liberica/src/components/lila/select.tsx | 21 +++++++++++++++++++-- liberica/src/page/Debug.tsx | 8 +++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/liberica/src/components/lila/select.tsx b/liberica/src/components/lila/select.tsx index 0a7c01a..e9d3e26 100644 --- a/liberica/src/components/lila/select.tsx +++ b/liberica/src/components/lila/select.tsx @@ -1,8 +1,25 @@ -export type SelectProps = Omit, "className">; +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; +} + +export const BASE = + "text-on-muted rounded-xl bg-muted/20 outline-none ring-muted focus:ring-2"; + +export const INPUT_SIZES = { + lg: "px-6 py-3 text-md", +}; export function Select(props: SelectProps) { return ( - {props.children} ); diff --git a/liberica/src/page/Debug.tsx b/liberica/src/page/Debug.tsx index 28fb73c..dbd634c 100644 --- a/liberica/src/page/Debug.tsx +++ b/liberica/src/page/Debug.tsx @@ -6,6 +6,7 @@ import { ButtonVariant, } from "components/lila/button"; import { TextInput } from "components/lila/input"; +import { Select } from "components/lila/select"; import { THEMES, ThemeName, applyTheme } from "lib/theme"; import { useState } from "react"; @@ -19,9 +20,10 @@ export function Debug() { setText(e.target.value || "Test")} - > + /> - +
{variant} {size} - {text} - +
From 57d6f7f6262277357f0119e6b61de481177df104 Mon Sep 17 00:00:00 2001 From: Aaron Geiger Date: Tue, 13 Aug 2024 22:43:09 +0200 Subject: [PATCH 18/19] Update deps --- liberica/bun.lockb | Bin 163384 -> 163384 bytes liberica/package.json | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/liberica/bun.lockb b/liberica/bun.lockb index 1e4a3243a6ce3eb7fc32b57c381a6e22a5bfd503..fa04994f41d6322391dc438a4d2192e7bb6e6258 100755 GIT binary patch delta 947 zcmYk*Ur1AN6bJC%Ou<>%MlH70gJ6oN8XeB%KiP)|1-FYXMC(H6dz{H6U75meQ43F^;kYL;>O%z8zFIOy;8$ zfn-Ef?cc8dIq!E7T`bE=hxB-Rc zSjGG)k3D5|nNb6^K>L6h>O_&svcluY}^}f|yH5x1t0= z)Z9$)@4C4f}{yRompajo}0EIl>sS1M!6gQx5n}i${B{I;ZYF`Hk^{;wC{!ck;2=}n|H~H`yYe$Bv02Pkq1aA0^5pQLDdR~)Svpc3l*@o}J0k{b zd#px_pd74ZSPAE=>n0y&Y;pqtY3#dq4cd}ZuY24yy5JtD8nGqMU*^r>!ug#l_<{B vcD|{e=KLvMy(fI&`?747PVY5@r0rfDdg1L@=L-D`wVQfn8y-b^WR;=6Nct_C delta 945 zcmYMyT}YE*6bJCLQcx?~2u1BiFh%@$*t;(`3|CQ>Ath#xK9Huo+F&}_-cl3VBqVQ~ z7NJyzl96F$CQ(x>!6LInQZGtsU1(Z~n1qywef*!dRo6fM=bYy`$F~~lTMhNxQ;xC0|xj7-b4v<0Gs)b(E< zkE$3;`73JDL1H1#M@Hvjatav4$ka?A%7a8hiYLuI2UFd~ng3J@LNiZ{>ZZ4D?u-MX zWodl4S_mKETWYr_XVzcA8>yL&Y1E?2ZYz_Bdh-LnacL*24LDYvvfq zBJgS*kO+vy6XzaixI1eB88DrR=wTqVnyX%}QHc!@J=5^H$03^#np#ZoC#5;wLeiL4 zk9so@{-S(oq3Y1Dh$j#oGcXO&PsnD-%K4?1Bg)xb5I!~fl-M1GR=bPb?6HsS&?Jt7 z1iie8a)(PQa=GZJb@au2c1R7xh>%X8a;GRsHqrU6K%Lu@+*=R97O)43R{Uwyv~4(r9sWOjC+-u_(87D#Oy8RHudT5zxpkT8r(X zM6U*NGd%+fr{r=bKDJY|7U2}A@#CBK(Tz_paN&n4P(7-JqFQxS`ikvrUhzM1Qxj+d z$bH4PR=d5dixakrP&Gb-KdPq+lZE%GbrhbWOrzh9d= zy;!b-;1Y0L Date: Tue, 13 Aug 2024 23:04:45 +0200 Subject: [PATCH 19/19] Update react-refresh for lila --- liberica/src/components/lila/button.tsx | 6 ++++-- liberica/src/components/lila/index.tsx | 1 + liberica/src/components/lila/select.tsx | 4 ++-- liberica/src/i18n/en.json | 5 ++++- liberica/src/page/Debug.tsx | 9 ++++++--- liberica/src/page/Game.tsx | 2 +- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/liberica/src/components/lila/button.tsx b/liberica/src/components/lila/button.tsx index f25b06e..907d9c0 100644 --- a/liberica/src/components/lila/button.tsx +++ b/liberica/src/components/lila/button.tsx @@ -1,9 +1,11 @@ +/** 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 "; -export const BUTTON_VARIANTS = { +const BUTTON_VARIANTS = { primary: "bg-primary text-on-primary rounded-xl font-bold hover:bg-primary/90 animate-entry", secondary: @@ -11,7 +13,7 @@ export const BUTTON_VARIANTS = { muted: "bg-muted/10 text-on-muted rounded-xl font-bold hover:bg-muted/20 animate-entry", }; -export const BUTTON_SIZES = { +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", diff --git a/liberica/src/components/lila/index.tsx b/liberica/src/components/lila/index.tsx index d6cbf76..e72c738 100644 --- a/liberica/src/components/lila/index.tsx +++ b/liberica/src/components/lila/index.tsx @@ -1,6 +1,7 @@ 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(" "); } diff --git a/liberica/src/components/lila/select.tsx b/liberica/src/components/lila/select.tsx index e9d3e26..0bccb0a 100644 --- a/liberica/src/components/lila/select.tsx +++ b/liberica/src/components/lila/select.tsx @@ -10,10 +10,10 @@ export interface SelectPropsExt { noLogo?: boolean; } -export const BASE = +const BASE = "text-on-muted rounded-xl bg-muted/20 outline-none ring-muted focus:ring-2"; -export const INPUT_SIZES = { +const INPUT_SIZES = { lg: "px-6 py-3 text-md", }; diff --git a/liberica/src/i18n/en.json b/liberica/src/i18n/en.json index f7c7bb8..7aa2087 100644 --- a/liberica/src/i18n/en.json +++ b/liberica/src/i18n/en.json @@ -19,5 +19,8 @@ "ReplayTooBig": "replay file is too big", "Speed": "Speed", "Cancel": "Cancel", - "EmbarkPlaceholder": "Click on train to embark" + "EmbarkPlaceholder": "Click on train to embark", + "DebugVariant": "Variant", + "DebugSize": "Size", + "DebugElement": "Element" } diff --git a/liberica/src/page/Debug.tsx b/liberica/src/page/Debug.tsx index dbd634c..513a83e 100644 --- a/liberica/src/page/Debug.tsx +++ b/liberica/src/page/Debug.tsx @@ -9,11 +9,14 @@ import { TextInput } from "components/lila/input"; import { Select } from "components/lila/select"; import { THEMES, ThemeName, applyTheme } from "lib/theme"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; export function Debug() { const sizes = Object.keys(BUTTON_SIZES) as ButtonSize[]; const variants = Object.keys(BUTTON_VARIANTS) as ButtonVariant[]; + const { t } = useTranslation(); + const [text, setText] = useState("Test"); return (
@@ -39,9 +42,9 @@ export function Debug() {
- - - + + + diff --git a/liberica/src/page/Game.tsx b/liberica/src/page/Game.tsx index b2c8e45..b5f5877 100644 --- a/liberica/src/page/Game.tsx +++ b/liberica/src/page/Game.tsx @@ -87,7 +87,7 @@ export function Game() { /> -
+
{embarkedTrain ? ( {embarkedTrain.line_name}{" "}
VariantSizeElement{t("DebugVariant")}{t("DebugSize")}{t("DebugElement")}