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:
@@ -0,0 +1,10 @@
|
||||
@use "../../styles/fonts" as fonts;
|
||||
@use "../../styles/colors" as colors;
|
||||
|
||||
.duration {
|
||||
@include fonts.font-overflow;
|
||||
|
||||
&--clarifying {
|
||||
color: colors.$orange;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
import type { FC } from "react";
|
||||
import "./FlightListSkeleton.scss";
|
||||
|
||||
export interface FlightListSkeletonProps {
|
||||
/** Number of skeleton rows to display (default: 5) */
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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,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) */
|
||||
|
||||
Reference in New Issue
Block a user