diff options
Diffstat (limited to 'src/frontend/app/components')
| -rw-r--r-- | src/frontend/app/components/ServiceAlerts.css | 5 | ||||
| -rw-r--r-- | src/frontend/app/components/ServiceAlerts.tsx | 2 | ||||
| -rw-r--r-- | src/frontend/app/components/StopGallery.css | 51 | ||||
| -rw-r--r-- | src/frontend/app/components/StopGallery.tsx | 42 | ||||
| -rw-r--r-- | src/frontend/app/components/StopItem.tsx | 15 | ||||
| -rw-r--r-- | src/frontend/app/components/ThemeColorManager.tsx | 20 | ||||
| -rw-r--r-- | src/frontend/app/components/layout/AppShell.tsx | 2 | ||||
| -rw-r--r-- | src/frontend/app/components/layout/Header.css | 1 |
8 files changed, 91 insertions, 47 deletions
diff --git a/src/frontend/app/components/ServiceAlerts.css b/src/frontend/app/components/ServiceAlerts.css index 7c271f9..c0f67f5 100644 --- a/src/frontend/app/components/ServiceAlerts.css +++ b/src/frontend/app/components/ServiceAlerts.css @@ -1,6 +1,9 @@ /* Service Alerts Container */ .service-alerts-container { - margin-bottom: 1.5rem; + margin: 0; + display: flex; + flex-direction: column; + gap: 0.75rem; } .service-alert { diff --git a/src/frontend/app/components/ServiceAlerts.tsx b/src/frontend/app/components/ServiceAlerts.tsx index eba8a92..a6a1ee8 100644 --- a/src/frontend/app/components/ServiceAlerts.tsx +++ b/src/frontend/app/components/ServiceAlerts.tsx @@ -6,7 +6,7 @@ const ServiceAlerts: React.FC = () => { const { t } = useTranslation(); return ( - <div className="service-alerts-container"> + <div className="service-alerts-container stoplist-section"> <h2 className="page-subtitle">{t("stoplist.service_alerts")}</h2> <div className="service-alert info"> <div className="alert-icon">âšī¸</div> diff --git a/src/frontend/app/components/StopGallery.css b/src/frontend/app/components/StopGallery.css index bc9b955..070a01f 100644 --- a/src/frontend/app/components/StopGallery.css +++ b/src/frontend/app/components/StopGallery.css @@ -1,13 +1,16 @@ /* Gallery Container */ .gallery-container { - margin-bottom: 1.5rem; + margin: 0; + display: flex; + flex-direction: column; + gap: 0.75rem; } .gallery-header { display: flex; align-items: center; justify-content: space-between; - margin-bottom: 0.75rem; + margin-bottom: 0.5rem; } .gallery-counter { @@ -24,6 +27,15 @@ font-weight: 600; } +/* Empty State */ +.gallery-empty-state { + text-align: center; +} + +.gallery-empty-state .message { + font-size: 0.85rem; +} + /* Scroll Container */ .gallery-scroll-container { overflow-x: auto; @@ -32,7 +44,6 @@ scroll-snap-type: x mandatory; scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* IE and Edge */ - padding: 0 1rem; } .gallery-scroll-container::-webkit-scrollbar { @@ -48,14 +59,15 @@ /* Gallery Item */ .gallery-item { - flex: 0 0 100%; + flex: 0 0 90%; + max-width: 320px; scroll-snap-align: start; scroll-snap-stop: always; } .gallery-item-link { display: block; - padding: 1rem; + padding: 0.75rem; background-color: var( --card-background-color, var(--message-background-color) @@ -64,7 +76,7 @@ border-radius: 12px; text-decoration: none; color: var(--text-color); - min-height: 120px; + min-height: 100px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } @@ -72,24 +84,24 @@ display: flex; align-items: center; gap: 0.5rem; - margin-bottom: 0.5rem; + margin-bottom: 0.25rem; } .gallery-item-header .favourite-icon { color: var(--star-color); - font-size: 1rem; + font-size: 0.9rem; } .gallery-item-code { - font-size: 0.85rem; + font-size: 0.8rem; color: var(--subtitle-color); font-weight: 500; } .gallery-item-name { - font-size: 1rem; + font-size: 0.95rem; font-weight: 600; - margin-bottom: 0.75rem; + margin-bottom: 0.5rem; line-height: 1.3; display: -webkit-box; /* Standard property for compatibility */ @@ -97,7 +109,7 @@ -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; - min-height: 2.6em; + min-height: 2.5em; } .gallery-item-lines { @@ -117,23 +129,22 @@ } /* Gallery Indicators */ -.gallery-indicators { +.gallery-dots { display: flex; justify-content: center; - gap: 0.5rem; - margin-top: 1rem; - padding: 0.5rem 0; + gap: 0.35rem; + margin-top: 0.25rem; } -.gallery-indicator { - width: 8px; - height: 8px; +.dot { + width: 6px; + height: 6px; border-radius: 50%; background-color: var(--border-color); transition: background-color 0.2s ease-in-out; } -.gallery-indicator.active { +.dot.active { background-color: var(--button-background-color); } diff --git a/src/frontend/app/components/StopGallery.tsx b/src/frontend/app/components/StopGallery.tsx index 18d0725..500ea20 100644 --- a/src/frontend/app/components/StopGallery.tsx +++ b/src/frontend/app/components/StopGallery.tsx @@ -1,7 +1,7 @@ -import React, { useRef, useState, useEffect } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { type Stop } from "../data/StopDataProvider"; -import StopGalleryItem from "./StopGalleryItem"; import "./StopGallery.css"; +import StopGalleryItem from "./StopGalleryItem"; interface StopGalleryProps { stops: Stop[]; @@ -19,7 +19,9 @@ const StopGallery: React.FC<StopGalleryProps> = ({ useEffect(() => { const element = scrollRef.current; - if (!element) return; + if (!element || stops.length === 0) { + return; + } const handleScroll = () => { const scrollLeft = element.scrollLeft; @@ -34,9 +36,11 @@ const StopGallery: React.FC<StopGalleryProps> = ({ if (stops.length === 0 && emptyMessage) { return ( - <div className="gallery-container"> - <h2 className="page-subtitle">{title}</h2> - <p className="message">{emptyMessage}</p> + <div className="gallery-container stoplist-section"> + <h3 className="page-subtitle">{title}</h3> + <div className="gallery-empty-state"> + <p className="message">{emptyMessage}</p> + </div> </div> ); } @@ -46,29 +50,27 @@ const StopGallery: React.FC<StopGalleryProps> = ({ } return ( - <div className="gallery-container"> + <div className="gallery-container stoplist-section"> <div className="gallery-header"> - <h2 className="page-subtitle">{title}</h2> + <h3 className="page-subtitle">{title}</h3> + <span className="gallery-counter">{stops.length}</span> </div> - <div className="gallery-scroll-container" ref={scrollRef}> + <div ref={scrollRef} className="gallery-scroll-container"> <div className="gallery-track"> {stops.map((stop) => ( <StopGalleryItem key={stop.stopId} stop={stop} /> ))} </div> </div> - - {stops.length > 1 && ( - <div className="gallery-indicators"> - {stops.map((_, index) => ( - <div - key={index} - className={`gallery-indicator ${index === activeIndex ? "active" : ""}`} - /> - ))} - </div> - )} + <div className="gallery-dots"> + {stops.map((_, index) => ( + <span + key={index} + className={`dot ${index === activeIndex ? "active" : ""}`} + ></span> + ))} + </div> </div> ); }; diff --git a/src/frontend/app/components/StopItem.tsx b/src/frontend/app/components/StopItem.tsx index ae51df8..de51576 100644 --- a/src/frontend/app/components/StopItem.tsx +++ b/src/frontend/app/components/StopItem.tsx @@ -1,8 +1,8 @@ import React from "react"; import { Link } from "react-router"; +import { useApp } from "../AppContext"; import StopDataProvider, { type Stop } from "../data/StopDataProvider"; import LineIcon from "./LineIcon"; -import { useApp } from "../AppContext"; interface StopItemProps { stop: Stop; @@ -14,9 +14,16 @@ const StopItem: React.FC<StopItemProps> = ({ stop }) => { return ( <li className="list-item"> <Link className="list-item-link" to={`/estimates/${stop.stopId}`}> - {stop.favourite && <span className="favourite-icon">â
</span>} ( - {stop.stopId}) {StopDataProvider.getDisplayName(region, stop)} - <div className="line-icons"> + <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}> + <span style={{ fontWeight: 600 }}> + {stop.favourite && <span className="favourite-icon">â
</span>} + {StopDataProvider.getDisplayName(region, stop)} + </span> + <span style={{ fontSize: "0.85em", color: "var(--subtitle-color)", marginLeft: "0.5rem" }}> + ({stop.stopId}) + </span> + </div> + <div className="line-icons" style={{ marginTop: "0.25rem" }}> {stop.lines?.map((line) => ( <LineIcon key={line} line={line} region={region} /> ))} diff --git a/src/frontend/app/components/ThemeColorManager.tsx b/src/frontend/app/components/ThemeColorManager.tsx new file mode 100644 index 0000000..c138dc9 --- /dev/null +++ b/src/frontend/app/components/ThemeColorManager.tsx @@ -0,0 +1,20 @@ +import { useEffect } from "react"; +import { useSettings } from "../contexts/SettingsContext"; + +export const ThemeColorManager = () => { + const { resolvedTheme } = useSettings(); + + useEffect(() => { + const color = resolvedTheme === "dark" ? "#121212" : "#ffffff"; + + let meta = document.querySelector('meta[name="theme-color"]'); + if (!meta) { + meta = document.createElement('meta'); + meta.setAttribute('name', 'theme-color'); + document.head.appendChild(meta); + } + meta.setAttribute('content', color); + }, [resolvedTheme]); + + return null; +}; diff --git a/src/frontend/app/components/layout/AppShell.tsx b/src/frontend/app/components/layout/AppShell.tsx index d0c0121..e0559ac 100644 --- a/src/frontend/app/components/layout/AppShell.tsx +++ b/src/frontend/app/components/layout/AppShell.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { Outlet } from "react-router"; import { PageTitleProvider, usePageTitleContext } from "~/contexts/PageTitleContext"; import NavBar from "../NavBar"; +import { ThemeColorManager } from "../ThemeColorManager"; import "./AppShell.css"; import { Drawer } from "./Drawer"; import { Header } from "./Header"; @@ -12,6 +13,7 @@ const AppShellContent: React.FC = () => { return ( <div className="app-shell"> + <ThemeColorManager /> <Header className="app-shell__header" title={title} diff --git a/src/frontend/app/components/layout/Header.css b/src/frontend/app/components/layout/Header.css index c95226f..4ff492e 100644 --- a/src/frontend/app/components/layout/Header.css +++ b/src/frontend/app/components/layout/Header.css @@ -4,7 +4,6 @@ justify-content: space-between; padding: 0.5rem 1rem; background-color: var(--background-color); - border-bottom: 1px solid var(--border-color); height: 60px; box-sizing: border-box; width: 100%; |
