diff options
| -rw-r--r-- | src/frontend/app/components/SchedulesTable.css | 55 | ||||
| -rw-r--r-- | src/frontend/app/components/StopAlert.css | 50 | ||||
| -rw-r--r-- | src/frontend/app/components/StopAlert.tsx | 9 | ||||
| -rw-r--r-- | src/frontend/app/components/StopItemSkeleton.tsx | 2 | ||||
| -rw-r--r-- | src/frontend/app/components/StopSheetSkeleton.tsx | 4 | ||||
| -rw-r--r-- | src/frontend/app/components/TimetableSkeleton.tsx | 2 | ||||
| -rw-r--r-- | src/frontend/app/root.css | 32 | ||||
| -rw-r--r-- | src/frontend/app/root.tsx | 12 | ||||
| -rw-r--r-- | src/frontend/app/routes/map.tsx | 21 | ||||
| -rw-r--r-- | src/frontend/app/routes/timetable-$id.css | 2 | ||||
| -rw-r--r-- | src/frontend/app/routes/timetable-$id.tsx | 235 | ||||
| -rw-r--r-- | src/frontend/public/maps/spritesheet/sprite.json | 16 | ||||
| -rw-r--r-- | src/frontend/public/maps/spritesheet/sprite.png | bin | 1515 -> 2987 bytes | |||
| -rw-r--r-- | src/frontend/public/maps/spritesheet/sprite@2x.json | 16 | ||||
| -rw-r--r-- | src/frontend/public/maps/spritesheet/sprite@2x.png | bin | 2878 -> 7056 bytes |
15 files changed, 295 insertions, 161 deletions
diff --git a/src/frontend/app/components/SchedulesTable.css b/src/frontend/app/components/SchedulesTable.css index 8980fb4..6d2f201 100644 --- a/src/frontend/app/components/SchedulesTable.css +++ b/src/frontend/app/components/SchedulesTable.css @@ -7,7 +7,7 @@ margin-bottom: 1rem; text-align: left; font-size: 1.1rem; - color: var(--text-primary, #333); + color: var(--text-primary); } .timetable-cards { @@ -18,17 +18,36 @@ } .timetable-card { - background-color: var(--surface-future, #fff); - border: 1px solid var(--card-border, #e0e0e0); + background-color: var(--surface-future); + border: 1px solid var(--card-border); border-radius: 10px; padding: 1.25rem; transition: background-color 0.2s ease, border 0.2s ease; } +/* Next upcoming service: slight emphasis */ +.timetable-card.timetable-next { + background-color: var(--surface-next); + border-color: var(--card-border); + position: relative; +} + +.timetable-card.timetable-next::before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + background: var(--accent-next); +} + .timetable-card.timetable-past { - background-color: var(--surface-past, #f3f3f3); - color: var(--text-secondary, #aaa); - border: 1px solid #e0e0e0; + background-color: var(--surface-past); + color: var(--text-secondary); + border: 1px solid var(--card-border); } .card-header { @@ -46,7 +65,7 @@ flex: 1; text-align: left; margin: 0 1rem; - color: var(--text-primary, #333); + color: var(--text-primary); } .destination-info strong { @@ -54,7 +73,7 @@ } .timetable-card.timetable-past .destination-info { - color: var(--text-secondary, #aaa); + color: var(--text-secondary); } .time-info { @@ -68,11 +87,11 @@ font-weight: bold; font-family: monospace; font-size: 1.1rem; - color: var(--text-primary, #333); + color: var(--text-primary); } .timetable-card.timetable-past .departure-time { - color: var(--text-secondary, #aaa); + color: var(--text-secondary); } .card-body { @@ -81,7 +100,7 @@ .route-streets { font-size: 0.85rem; - color: var(--text-secondary, #666); + color: var(--text-secondary); line-height: 1.8; word-break: break-word; } @@ -89,8 +108,8 @@ .service-id { font-family: monospace; font-size: 0.8rem; - color: var(--text-secondary, #666); - background: var(--service-background, #f0f0f0); + color: var(--text-secondary); + background: var(--service-background); padding: 0.15rem 0.4rem; border-radius: 3px; font-weight: 500; @@ -99,18 +118,18 @@ } .timetable-card.timetable-past .service-id { - color: var(--text-secondary, #bbb); - background: #e8e8e8; + color: var(--text-secondary); + background: var(--service-background-past); } .no-data { text-align: center; - color: var(--text-secondary, #666); + color: var(--text-secondary); font-style: italic; padding: 2rem; - background: var(--card-background, #f8f9fa); + background: var(--card-background); border-radius: 8px; - border: 1px solid var(--card-border, #e0e0e0); + border: 1px solid var(--card-border); } /* Responsive design */ diff --git a/src/frontend/app/components/StopAlert.css b/src/frontend/app/components/StopAlert.css index 0032d09..2ba3baa 100644 --- a/src/frontend/app/components/StopAlert.css +++ b/src/frontend/app/components/StopAlert.css @@ -1,3 +1,25 @@ +/* Alert color variables */ +:root { + --alert-info-bg: rgba(59, 130, 246, 0.1); + --alert-info-border: rgba(59, 130, 246, 0.5); + --alert-info-text: #1e40af; + + --alert-error-bg: rgba(239, 68, 68, 0.1); + --alert-error-border: rgba(239, 68, 68, 0.5); + --alert-error-text: #991b1b; +} + +/* Dark mode overrides use data-mode */ +[data-mode="dark"] { + --alert-info-bg: rgba(59, 130, 246, 0.15); + --alert-info-border: rgba(59, 130, 246, 0.4); + --alert-info-text: #93c5fd; + + --alert-error-bg: rgba(239, 68, 68, 0.15); + --alert-error-border: rgba(239, 68, 68, 0.4); + --alert-error-text: #fca5a5; +} + .stop-alert { display: flex; align-items: flex-start; @@ -9,20 +31,20 @@ } .stop-alert-info { - background-color: rgba(59, 130, 246, 0.1); - border-color: rgba(59, 130, 246, 0.3); - color: #1e40af; + background-color: var(--alert-info-bg); + border-color: var(--alert-info-border); + color: var(--alert-info-text); } .stop-alert-error { - background-color: rgba(239, 68, 68, 0.1); - border-color: rgba(239, 68, 68, 0.3); - color: #991b1b; + background-color: var(--alert-error-bg); + border-color: var(--alert-error-border); + color: var(--alert-error-text); } .stop-alert-compact { padding: 0.5rem; - margin: 0.5rem 0; + margin: 1.5rem 0 0.5rem 0; font-size: 0.875rem; } @@ -73,17 +95,3 @@ font-size: 0.75rem; } -/* Dark mode support */ -@media (prefers-color-scheme: dark) { - .stop-alert-info { - background-color: rgba(59, 130, 246, 0.15); - border-color: rgba(59, 130, 246, 0.4); - color: #93c5fd; - } - - .stop-alert-error { - background-color: rgba(239, 68, 68, 0.15); - border-color: rgba(239, 68, 68, 0.4); - color: #fca5a5; - } -} diff --git a/src/frontend/app/components/StopAlert.tsx b/src/frontend/app/components/StopAlert.tsx index 69ecc22..a96f93e 100644 --- a/src/frontend/app/components/StopAlert.tsx +++ b/src/frontend/app/components/StopAlert.tsx @@ -19,17 +19,10 @@ export const StopAlert: React.FC<StopAlertProps> = ({ stop, compact = false }) = return ( <div className={`stop-alert ${isError ? 'stop-alert-error' : 'stop-alert-info'} ${compact ? 'stop-alert-compact' : ''}`}> - <div className="stop-alert-icon"> - {isError ? <AlertCircle /> : <Info />} - </div> + {isError ? <AlertCircle /> : <Info />} <div className="stop-alert-content"> {stop.title && <div className="stop-alert-title">{stop.title}</div>} {stop.message && <div className="stop-alert-message">{stop.message}</div>} - {stop.alternateCodes && stop.alternateCodes.length > 0 && ( - <div className="stop-alert-alternate-codes"> - Alternative stops: {stop.alternateCodes.join(", ")} - </div> - )} </div> </div> ); diff --git a/src/frontend/app/components/StopItemSkeleton.tsx b/src/frontend/app/components/StopItemSkeleton.tsx index 72f7506..9133393 100644 --- a/src/frontend/app/components/StopItemSkeleton.tsx +++ b/src/frontend/app/components/StopItemSkeleton.tsx @@ -12,7 +12,7 @@ const StopItemSkeleton: React.FC<StopItemSkeletonProps> = ({ stopId }) => { return ( - <SkeletonTheme baseColor="#f0f0f0" highlightColor="#e0e0e0"> + <SkeletonTheme baseColor="var(--skeleton-base)" highlightColor="var(--skeleton-highlight)"> <li className="list-item"> <div className="list-item-link"> <span> diff --git a/src/frontend/app/components/StopSheetSkeleton.tsx b/src/frontend/app/components/StopSheetSkeleton.tsx index 651efa5..6870af2 100644 --- a/src/frontend/app/components/StopSheetSkeleton.tsx +++ b/src/frontend/app/components/StopSheetSkeleton.tsx @@ -13,7 +13,7 @@ export const StopSheetSkeleton: React.FC<StopSheetSkeletonProps> = ({ const { t } = useTranslation(); return ( - <SkeletonTheme baseColor="#f0f0f0" highlightColor="#e0e0e0"> + <SkeletonTheme baseColor="var(--skeleton-base)" highlightColor="var(--skeleton-highlight)"> <div className="stop-sheet-estimates"> <h3 className="stop-sheet-subtitle"> {t("estimates.next_arrivals", "Next arrivals")} @@ -53,7 +53,7 @@ export const StopSheetSkeleton: React.FC<StopSheetSkeletonProps> = ({ </div> <div className="stop-sheet-view-all" style={{ - background: "#f0f0f0", + background: "var(--service-background)", cursor: "not-allowed", pointerEvents: "none" }}> diff --git a/src/frontend/app/components/TimetableSkeleton.tsx b/src/frontend/app/components/TimetableSkeleton.tsx index 01956ee..79c7725 100644 --- a/src/frontend/app/components/TimetableSkeleton.tsx +++ b/src/frontend/app/components/TimetableSkeleton.tsx @@ -13,7 +13,7 @@ export const TimetableSkeleton: React.FC<TimetableSkeletonProps> = ({ const { t } = useTranslation(); return ( - <SkeletonTheme baseColor="#f0f0f0" highlightColor="#e0e0e0"> + <SkeletonTheme baseColor="var(--skeleton-base)" highlightColor="var(--skeleton-highlight)"> <div className="timetable-container"> <div className="timetable-caption"> <Skeleton width="250px" height="1.1rem" /> diff --git a/src/frontend/app/root.css b/src/frontend/app/root.css index 202e6f1..fb955eb 100644 --- a/src/frontend/app/root.css +++ b/src/frontend/app/root.css @@ -10,6 +10,22 @@ --star-color: #ffcc00; --message-background-color: #f8f9fa; + /* Skeletons */ + --skeleton-base: #f0f0f0; + --skeleton-highlight: #e0e0e0; + + /* Timetable component variables */ + --text-primary: var(--text-color); + --text-secondary: #666666; + --surface-future: var(--background-color); + --surface-next: #eef6ff; /* slightly accented surface for next card */ + --surface-past: hsl(0 0% 25% / 1); + --accent-next: #1e88e5; /* accent color for next card left border */ + --card-border: var(--border-color); + --card-background: #f8f9fa; + --service-background: #f0f0f0; + --service-background-past: #e8e8e8; + color-scheme: light; font-family: "Roboto Variable", Roboto, Arial, sans-serif; } @@ -26,6 +42,22 @@ --star-color: #ffcc00; --message-background-color: #333333; + /* Skeletons (dark) */ + --skeleton-base: #2a2a2a; + --skeleton-highlight: #3a3a3a; + + /* Timetable component dark overrides */ + --text-primary: var(--text-color); + --text-secondary: #bbbbbb; + --surface-future: #1e1e1e; + --surface-next: #17212b; + --surface-past: #1a1a1a; + --accent-next: #64b5f6; + --card-border: var(--border-color); + --card-background: #1e1e1e; + --service-background: #222222; + --service-background-past: #1f1f1f; + color-scheme: dark; } diff --git a/src/frontend/app/root.tsx b/src/frontend/app/root.tsx index 4815d2f..9bc69e4 100644 --- a/src/frontend/app/root.tsx +++ b/src/frontend/app/root.tsx @@ -76,6 +76,18 @@ export function Layout({ children }: { children: React.ReactNode }) { <title>Busurbano</title> <Meta /> + {/* Set theme early to avoid flash of wrong theme (especially skeletons) */} + <script + dangerouslySetInnerHTML={{ + __html: `(() => { try { + var saved = localStorage.getItem('theme'); + var system = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; + var resolved = (saved === 'light' || saved === 'dark') ? saved : system; + document.documentElement.setAttribute('data-theme', resolved); + document.documentElement.style.colorScheme = resolved; + } catch (e) {} })();`, + }} + /> <Links /> </head> <body> diff --git a/src/frontend/app/routes/map.tsx b/src/frontend/app/routes/map.tsx index effd29b..f705617 100644 --- a/src/frontend/app/routes/map.tsx +++ b/src/frontend/app/routes/map.tsx @@ -32,7 +32,10 @@ const defaultStyle: StyleSpecification = { export default function StopMap() { const { t } = useTranslation(); const [stops, setStops] = useState< - GeoJsonFeature<Point, { stopId: number; name: string; lines: string[] }>[] + GeoJsonFeature< + Point, + { stopId: number; name: string; lines: string[]; cancelled?: boolean } + >[] >([]); const [selectedStop, setSelectedStop] = useState<Stop | null>(null); const [isSheetOpen, setIsSheetOpen] = useState(false); @@ -57,14 +60,19 @@ export default function StopMap() { StopDataProvider.getStops(region).then((data) => { const features: GeoJsonFeature< Point, - { stopId: number; name: string; lines: string[] } + { stopId: number; name: string; lines: string[]; cancelled?: boolean } >[] = data.map((s) => ({ type: "Feature", geometry: { type: "Point", coordinates: [s.longitude as number, s.latitude as number], }, - properties: { stopId: s.stopId, name: s.name.original, lines: s.lines }, + properties: { + stopId: s.stopId, + name: s.name.original, + lines: s.lines, + cancelled: s.cancelled, + }, })); setStops(features); }); @@ -157,7 +165,12 @@ export default function StopMap() { type="symbol" source="stops-source" layout={{ - "icon-image": `stop-${region}`, + "icon-image": [ + "case", + ["boolean", ["get", "cancelled"], false], + `stop-${region}-cancelled`, + `stop-${region}`, + ], "icon-size": ["interpolate", ["linear"], ["zoom"], 11, 0.7, 18, 1.0], "icon-allow-overlap": true, "icon-ignore-placement": true, diff --git a/src/frontend/app/routes/timetable-$id.css b/src/frontend/app/routes/timetable-$id.css index b4e231a..3815982 100644 --- a/src/frontend/app/routes/timetable-$id.css +++ b/src/frontend/app/routes/timetable-$id.css @@ -38,8 +38,6 @@ .timetable-full-content { margin-top: 1rem; - overflow-y: auto; - max-height: calc(100vh - 250px); position: relative; padding-bottom: 80px; /* Space for FAB */ } diff --git a/src/frontend/app/routes/timetable-$id.tsx b/src/frontend/app/routes/timetable-$id.tsx index c8f0125..df77372 100644 --- a/src/frontend/app/routes/timetable-$id.tsx +++ b/src/frontend/app/routes/timetable-$id.tsx @@ -153,9 +153,6 @@ export default function Timetable() { const [loading, setLoading] = useState(true); const [error, setError] = useState<ErrorInfo | null>(null); const [showPastEntries, setShowPastEntries] = useState(false); - const [showScrollTop, setShowScrollTop] = useState(false); - const [showScrollBottom, setShowScrollBottom] = useState(false); - const [showGoToNow, setShowGoToNow] = useState(false); const nextEntryRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null); const regionConfig = getRegionConfig(region); @@ -242,73 +239,7 @@ export default function Timetable() { setCustomName(StopDataProvider.getCustomName(region, stopIdNum)); }, [params.id, region]); - // Handle scroll events to update FAB visibility - useEffect(() => { - const handleScroll = () => { - if ( - !containerRef.current || - loading || - error || - timetableData.length === 0 - ) { - return; - } - - const container = containerRef.current; - const scrollTop = container.scrollTop; - const scrollHeight = container.scrollHeight; - const clientHeight = container.clientHeight; - const scrollBottom = scrollHeight - scrollTop - clientHeight; - - // Threshold for showing scroll buttons (in pixels) - const threshold = 100; - - // Show scroll top button when scrolled down - setShowScrollTop(scrollTop > threshold); - - // Show scroll bottom button when not at bottom - setShowScrollBottom(scrollBottom > threshold); - - // Check if next entry (current time) is visible - if (nextEntryRef.current) { - const rect = nextEntryRef.current.getBoundingClientRect(); - const containerRect = container.getBoundingClientRect(); - const isNextVisible = - rect.top >= containerRect.top && rect.bottom <= containerRect.bottom; - - setShowGoToNow(!isNextVisible); - } - }; - - const container = containerRef.current; - if (container) { - container.addEventListener("scroll", handleScroll); - // Initial check - handleScroll(); - - return () => { - container.removeEventListener("scroll", handleScroll); - }; - } - }, [loading, error, timetableData]); - - const scrollToTop = () => { - containerRef.current?.scrollTo({ top: 0, behavior: "smooth" }); - }; - - const scrollToBottom = () => { - containerRef.current?.scrollTo({ - top: containerRef.current.scrollHeight, - behavior: "smooth", - }); - }; - - const scrollToNow = () => { - nextEntryRef.current?.scrollIntoView({ - behavior: "smooth", - block: "center", - }); - }; + // Scroll FABs moved to ScrollFabManager component if (loading) { return ( @@ -401,40 +332,13 @@ export default function Timetable() { /> {/* Floating Action Button */} - {(showGoToNow || showScrollTop || showScrollBottom) && ( - <div className="fab-container"> - {showGoToNow && !showScrollTop && !showScrollBottom && ( - <button - className="fab fab-now" - onClick={scrollToNow} - title={t("timetable.goToNow", "Ir a ahora")} - aria-label={t("timetable.goToNow", "Ir a ahora")} - > - <Clock className="fab-icon" /> - </button> - )} - {showScrollTop && ( - <button - className="fab fab-up" - onClick={scrollToTop} - title={t("timetable.scrollUp", "Subir")} - aria-label={t("timetable.scrollUp", "Subir")} - > - <ChevronUp className="fab-icon" /> - </button> - )} - {showScrollBottom && !showScrollTop && ( - <button - className="fab fab-down" - onClick={scrollToBottom} - title={t("timetable.scrollDown", "Bajar")} - aria-label={t("timetable.scrollDown", "Bajar")} - > - <ChevronDown className="fab-icon" /> - </button> - )} - </div> - )} + <ScrollFabManager + containerRef={containerRef} + nextEntryRef={nextEntryRef} + currentTime={currentTime} + data={filteredData} + disabled={loading || !!error || timetableData.length === 0} + /> </div> )} </div> @@ -525,3 +429,126 @@ const TimetableTableWithScroll: React.FC<{ </div> ); }; + +// Component to manage scroll-based FAB visibility globally within timetable +const ScrollFabManager: React.FC<{ + containerRef: React.RefObject<HTMLDivElement | null>; + nextEntryRef: React.RefObject<HTMLDivElement | null>; + currentTime: string; + data: ScheduledTable[]; + disabled?: boolean; +}> = ({ containerRef, nextEntryRef, currentTime, data, disabled = false }) => { + const { t } = useTranslation(); + const [showScrollTop, setShowScrollTop] = useState(false); + const [showScrollBottom, setShowScrollBottom] = useState(false); + const [showGoToNow, setShowGoToNow] = useState(false); + + // Find the actual scrollable ancestor (.main-content) if our container isn't scrollable + const getScrollContainer = () => { + let el: HTMLElement | null = containerRef.current; + while (el) { + const style = getComputedStyle(el); + const hasScroll = el.scrollHeight > el.clientHeight + 8; + const overflowY = style.overflowY; + if (hasScroll && (overflowY === 'auto' || overflowY === 'scroll')) { + return el; + } + el = el.parentElement; + } + return null; + }; + + useEffect(() => { + if (disabled) return; + const scrollEl = getScrollContainer(); + const useWindowScroll = !scrollEl; + + const handleScroll = () => { + const scrollTop = useWindowScroll + ? (window.scrollY || document.documentElement.scrollTop || 0) + : scrollEl!.scrollTop; + const scrollHeight = useWindowScroll + ? document.documentElement.scrollHeight + : scrollEl!.scrollHeight; + const clientHeight = useWindowScroll ? window.innerHeight : scrollEl!.clientHeight; + + const scrollBottom = scrollHeight - scrollTop - clientHeight; + const threshold = 80; // slightly smaller threshold for responsiveness + setShowScrollTop(scrollTop > threshold); + setShowScrollBottom(scrollBottom > threshold); + + if (nextEntryRef.current) { + const rect = nextEntryRef.current.getBoundingClientRect(); + const isNextVisible = rect.top >= 0 && rect.bottom <= window.innerHeight; + setShowGoToNow(!isNextVisible); + } + }; + + const target: any = useWindowScroll ? window : scrollEl!; + target.addEventListener('scroll', handleScroll, { passive: true }); + window.addEventListener('resize', handleScroll); + handleScroll(); + return () => { + target.removeEventListener('scroll', handleScroll); + window.removeEventListener('resize', handleScroll); + }; + }, [containerRef, nextEntryRef, disabled, data, currentTime]); + + const scrollToTop = () => { + const scrollEl = getScrollContainer(); + if (!scrollEl) { + window.scrollTo({ top: 0, behavior: 'smooth' }); + } else { + scrollEl.scrollTo({ top: 0, behavior: 'smooth' }); + } + }; + const scrollToBottom = () => { + const scrollEl = getScrollContainer(); + if (!scrollEl) { + window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' }); + } else { + scrollEl.scrollTo({ top: scrollEl.scrollHeight, behavior: 'smooth' }); + } + }; + const scrollToNow = () => { + nextEntryRef.current?.scrollIntoView({ behavior: "smooth", block: "center" }); + }; + + if (disabled) return null; + if (!(showGoToNow || showScrollTop || showScrollBottom)) return null; + + return ( + <div className="fab-container"> + {showGoToNow && !showScrollTop && !showScrollBottom && ( + <button + className="fab fab-now" + onClick={scrollToNow} + title={t("timetable.goToNow", "Ir a ahora")} + aria-label={t("timetable.goToNow", "Ir a ahora")} + > + <Clock className="fab-icon" /> + </button> + )} + {showScrollTop && ( + <button + className="fab fab-up" + onClick={scrollToTop} + title={t("timetable.scrollUp", "Subir")} + aria-label={t("timetable.scrollUp", "Subir")} + > + <ChevronUp className="fab-icon" /> + </button> + )} + {showScrollBottom && !showScrollTop && ( + <button + className="fab fab-down" + onClick={scrollToBottom} + title={t("timetable.scrollDown", "Bajar")} + aria-label={t("timetable.scrollDown", "Bajar")} + > + <ChevronDown className="fab-icon" /> + </button> + )} + </div> + ); +}; diff --git a/src/frontend/public/maps/spritesheet/sprite.json b/src/frontend/public/maps/spritesheet/sprite.json index f6b564f..636efa5 100644 --- a/src/frontend/public/maps/spritesheet/sprite.json +++ b/src/frontend/public/maps/spritesheet/sprite.json @@ -14,5 +14,21 @@ "width": 32, "height": 32, "pixelRatio": 1 + }, + "stop-vigo-cancelled": { + "id": "stop-vigo-cancelled", + "x": 64, + "y": 0, + "width": 32, + "height": 32, + "pixelRatio": 1 + }, + "stop-santiago-cancelled": { + "id": "stop-santiago-cancelled", + "x": 96, + "y": 0, + "width": 32, + "height": 32, + "pixelRatio": 1 } } diff --git a/src/frontend/public/maps/spritesheet/sprite.png b/src/frontend/public/maps/spritesheet/sprite.png Binary files differindex f9799e4..5c46a13 100644 --- a/src/frontend/public/maps/spritesheet/sprite.png +++ b/src/frontend/public/maps/spritesheet/sprite.png diff --git a/src/frontend/public/maps/spritesheet/sprite@2x.json b/src/frontend/public/maps/spritesheet/sprite@2x.json index b5bfb0b..f4a9adf 100644 --- a/src/frontend/public/maps/spritesheet/sprite@2x.json +++ b/src/frontend/public/maps/spritesheet/sprite@2x.json @@ -14,5 +14,21 @@ "width": 64, "height": 64, "pixelRatio": 2 + }, + "stop-vigo-cancelled": { + "id": "stop-vigo-cancelled", + "x": 128, + "y": 0, + "width": 64, + "height": 64, + "pixelRatio": 2 + }, + "stop-santiago-cancelled": { + "id": "stop-santiago-cancelled", + "x": 192, + "y": 0, + "width": 64, + "height": 64, + "pixelRatio": 2 } } diff --git a/src/frontend/public/maps/spritesheet/sprite@2x.png b/src/frontend/public/maps/spritesheet/sprite@2x.png Binary files differindex e05a242..4101224 100644 --- a/src/frontend/public/maps/spritesheet/sprite@2x.png +++ b/src/frontend/public/maps/spritesheet/sprite@2x.png |
