Port flight display component styles (station, time-group, status, duration)

Ported Angular SCSS for station, time-group, flight-status, duration,
flight-card, flight-list, and flight-list-skeleton to React equivalents.
Aligned class names in JSX with Angular BEM conventions and added SCSS
imports to all flight display components.
This commit is contained in:
2026-04-15 19:05:50 +03:00
parent 5d512e146e
commit 4701396a0f
14 changed files with 413 additions and 5 deletions
+10
View File
@@ -0,0 +1,10 @@
@use "../../styles/fonts" as fonts;
@use "../../styles/colors" as colors;
.duration {
@include fonts.font-overflow;
&--clarifying {
color: colors.$orange;
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
import type { FC } from "react";
import { formatDuration } from "@/shared/utils/datetime/index.js";
import "./DurationDisplay.scss";
export interface DurationDisplayProps {
/** Flight duration in total minutes */
@@ -16,6 +17,6 @@ export const DurationDisplay: FC<DurationDisplayProps> = ({
locale = "en",
}) => {
return (
<span className="duration-display">{formatDuration(minutes, locale)}</span>
<span className="duration">{formatDuration(minutes, locale)}</span>
);
};
+68
View File
@@ -0,0 +1,68 @@
@use "../../styles/variables" as vars;
@use "../../styles/colors" as colors;
@use "../../styles/fonts" as fonts;
@use "../../styles/screen" as screen;
.flight-card {
display: flex;
align-items: center;
padding: vars.$space-xl 0;
margin: 0 vars.$space-xl;
justify-content: space-between;
& + & {
border-top: 1px dashed colors.$border;
}
&__number {
width: vars.$width-flight-number;
font-weight: fonts.$font-medium;
}
&__route {
display: flex;
flex: 1;
align-items: center;
justify-content: space-between;
}
&__departure,
&__arrival {
display: flex;
align-items: center;
gap: vars.$space-m;
width: vars.$width-dep-arr;
}
&__duration {
width: vars.$width-flight-time;
text-align: center;
}
&__status {
width: vars.$status-width;
text-align: right;
}
@include screen.mobile {
flex-direction: column;
align-items: flex-start;
gap: vars.$space-m;
&__route {
flex-direction: column;
gap: vars.$space-m;
width: 100%;
}
&__departure,
&__arrival {
width: 100%;
}
&__status {
width: 100%;
text-align: left;
}
}
}
+1
View File
@@ -4,6 +4,7 @@ import { StationDisplay } from "./StationDisplay.js";
import { TimeGroup } from "./TimeGroup.js";
import { FlightStatus } from "./FlightStatus.js";
import { DurationDisplay } from "./DurationDisplay.js";
import "./FlightCard.scss";
export interface FlightCardProps {
flight: ISimpleFlight;
+14
View File
@@ -0,0 +1,14 @@
@use "../../styles/colors" as colors;
@use "../../styles/fonts" as fonts;
.flight-list {
&--empty {
padding: 40px 20px;
text-align: center;
}
&__empty-message {
color: colors.$light-gray;
font-size: fonts.$font-size-l;
}
}
+1
View File
@@ -2,6 +2,7 @@ import type { FC } from "react";
import type { ISimpleFlight } from "@/features/online-board/types.js";
import { FlightCard } from "./FlightCard.js";
import { FlightListSkeleton } from "./FlightListSkeleton.js";
import "./FlightList.scss";
export interface FlightListProps {
/** Array of flights to display */
+52
View File
@@ -0,0 +1,52 @@
@use "../../styles/variables" as vars;
@use "../../styles/colors" as colors;
@keyframes skeleton-pulse {
0% {
opacity: 0.6;
}
50% {
opacity: 1;
}
100% {
opacity: 0.6;
}
}
.flight-list-skeleton {
&__row {
display: flex;
align-items: center;
padding: vars.$space-xl;
gap: vars.$space-m;
&:not(:last-child) {
border-bottom: 1px dashed colors.$border;
}
}
&__cell {
height: 16px;
background-color: colors.$blue-light2;
border-radius: vars.$border-radius;
animation: skeleton-pulse 1.5s ease-in-out infinite;
&--number {
width: vars.$width-flight-number;
}
&--station {
width: 80px;
}
&--time {
width: 50px;
}
&--status {
width: vars.$status-width;
}
}
}
+1
View File
@@ -1,4 +1,5 @@
import type { FC } from "react";
import "./FlightListSkeleton.scss";
export interface FlightListSkeletonProps {
/** Number of skeleton rows to display (default: 5) */
+37
View File
@@ -0,0 +1,37 @@
@use "../../styles/variables" as vars;
@use "../../styles/colors" as colors;
.flight-status {
&__content {
display: flex;
align-items: center;
& > *:not(:last-child) {
margin-right: vars.$space-s2;
}
}
&__indicator {
width: vars.$status-indicator-size;
height: vars.$status-indicator-size;
border-radius: 50%;
display: inline-block;
background-color: colors.$light-gray;
border: 1px solid colors.$light-gray;
&--Finished,
&--arrived,
&--landed,
&--cancelled {
background-color: colors.$red;
border-color: colors.$red;
}
&--InProgress,
&--in-flight,
&--departed {
background-color: colors.$green;
border-color: colors.$green;
}
}
}
+1
View File
@@ -1,5 +1,6 @@
import type { FC } from "react";
import type { FlightStatus as FlightStatusType } from "@/features/online-board/types.js";
import "./FlightStatus.scss";
export interface FlightStatusProps {
status: FlightStatusType;
+49
View File
@@ -0,0 +1,49 @@
@use "../../styles/screen" as screen;
.station {
display: flex;
flex-direction: column;
font-size: 0;
&__city {
max-width: 100%;
}
&__old-city {
text-decoration: line-through;
}
&__terminal {
@include screen.smTablet {
max-height: initial;
}
}
&--right {
align-items: flex-end;
.station__terminal {
text-align: right;
}
}
&--mobile-right {
@include screen.mobile {
align-items: flex-end;
.station__terminal {
text-align: right;
}
}
}
&--mobile-left {
@include screen.gt-mobile {
align-items: flex-end;
}
}
&--center {
align-items: center;
}
}
+5 -4
View File
@@ -1,5 +1,6 @@
import type { FC } from "react";
import { useCityName } from "@/shared/hooks/useDictionaries.js";
import "./StationDisplay.scss";
export interface StationDisplayProps {
/** IATA airport code, e.g. "SVO" */
@@ -23,12 +24,12 @@ export const StationDisplay: FC<StationDisplayProps> = ({
const resolvedCity = cityName ?? useCityName(airportCode);
return (
<div className="station-display">
<span className="station-display__code">{airportCode}</span>
<div className="station">
<span className="station__code">{airportCode}</span>
{airportName ? (
<span className="station-display__name">{airportName}</span>
<span className="station__name">{airportName}</span>
) : null}
<span className="station-display__city">{resolvedCity}</span>
<span className="station__city">{resolvedCity}</span>
</div>
);
};
+171
View File
@@ -0,0 +1,171 @@
@use "../../styles/fonts" as fonts;
@use "../../styles/colors" as colors;
@use "../../styles/variables" as vars;
@use "../../styles/screen" as screen;
.time-group {
display: inline-flex;
&__times {
display: flex;
flex-direction: column;
align-items: flex-start;
}
&__actual {
display: flex;
align-items: flex-start;
}
&__utc {
margin-top: 2px;
margin-left: vars.$space-s;
}
&__day-change {
margin-top: vars.$space-s;
margin-left: vars.$space-s;
}
&__old-time,
&__scheduled--delayed {
text-decoration: line-through;
}
&__specifying {
display: flex;
align-items: center;
}
// Size variants
&--medium {
.time-group__day-change {
margin-top: 3px;
}
}
&--small,
&--extra-small {
.time-group__utc {
margin-top: 0;
font-size: fonts.$font-size-xs;
line-height: 13px;
}
.time-group__day-change {
margin-top: 1px;
}
}
// Positioning variants
&--right {
justify-content: flex-end;
.time-group__day-change {
order: 1;
margin-right: vars.$space-s;
margin-left: 0;
}
.time-group__times {
order: 2;
align-items: flex-end;
}
}
&--day-change-left {
.time-group__day-change {
order: 1;
margin-right: vars.$space-s;
margin-left: 0;
}
.time-group__times {
order: 2;
}
}
&--day-change-mobile-left {
@include screen.mobile {
.time-group__day-change {
order: 1;
margin-right: vars.$space-s;
margin-left: 0;
}
.time-group__times {
order: 2;
}
}
}
&--day-change-mobile-right {
@include screen.gt-mobile {
.time-group__day-change {
order: 1;
margin-right: vars.$space-s;
margin-left: 0;
}
.time-group__times {
order: 2;
}
}
}
&--mobile-right {
@include screen.mobile {
.time-group__day-change {
order: 1;
margin-right: vars.$space-s;
margin-left: 0;
}
.time-group__times {
order: 2;
align-items: flex-end;
}
}
}
&--mobile-left {
@include screen.gt-mobile {
.time-group__day-change {
order: 1;
margin-right: vars.$space-s;
margin-left: 0;
}
.time-group__times {
order: 2;
align-items: flex-end;
}
}
}
&--day-change-absolute {
position: relative;
.time-group__day-change {
position: absolute;
top: 0;
left: 100%;
}
}
&--mobile-right.time-group--day-change-absolute {
@include screen.mobile {
.time-group__day-change {
left: auto;
right: 100%;
}
}
}
&--right.time-group--day-change-absolute {
.time-group__day-change {
left: auto;
right: 100%;
}
}
}
+1
View File
@@ -1,5 +1,6 @@
import type { FC } from "react";
import { formatTime } from "@/shared/utils/datetime/index.js";
import "./TimeGroup.scss";
export interface TimeGroupProps {
/** Scheduled time (ISO 8601 string) */