- Implemented StationListLoadingState component for loading messages. - Created MenuLED component to display LED information with loading animations. - Developed MenuMap component for interactive map features, including user location markers and station markers. - Introduced LEDFrame component for consistent styling of LED displays. - Enhanced LED_vision component to support embedded rendering and content readiness. - Added menuStationSelectors for managing nearby and favorite station groups. - Updated Menu component to integrate new loading states and map functionalities. - Added tests for menuStationSelectors to ensure correct grouping and selection logic.
129 lines
3.7 KiB
TypeScript
129 lines
3.7 KiB
TypeScript
import type { OriginalStationList, StationProps } from "./CommonTypes";
|
|
import type { StationSource } from "../types";
|
|
|
|
export type MenuCoordinates = {
|
|
latitude: number;
|
|
longitude: number;
|
|
};
|
|
|
|
type NearbyStationGroupsArgs = {
|
|
coordinates: MenuCoordinates;
|
|
originalStationList: OriginalStationList;
|
|
lineNames: readonly string[];
|
|
touristSpotKey?: string;
|
|
};
|
|
|
|
type MenuStationGroupsArgs = {
|
|
stationSource: StationSource;
|
|
originalStationList: OriginalStationList;
|
|
nearbyPositionStations: StationProps[][];
|
|
favoriteStations: StationProps[][];
|
|
findStationsByName: (query: string) => StationProps[];
|
|
lineWebIds: Readonly<Record<string, string>>;
|
|
stationIds: Readonly<Record<string, string>>;
|
|
};
|
|
|
|
export const MENU_NEARBY_SEARCH_RADIUS = 0.055;
|
|
|
|
const distanceFrom = (
|
|
station: StationProps,
|
|
coordinates: MenuCoordinates,
|
|
): number => {
|
|
const latitudeDistance = Math.abs(station.lat - coordinates.latitude);
|
|
const longitudeDistance = Math.abs(station.lng - coordinates.longitude);
|
|
return Math.sqrt(
|
|
latitudeDistance * latitudeDistance +
|
|
longitudeDistance * longitudeDistance,
|
|
);
|
|
};
|
|
|
|
const sortNearbyStations = (
|
|
stations: StationProps[],
|
|
coordinates: MenuCoordinates,
|
|
): StationProps[] => {
|
|
return stations
|
|
.map((station) => ({
|
|
station,
|
|
distance: distanceFrom(station, coordinates),
|
|
}))
|
|
.filter(({ distance }) => distance < MENU_NEARBY_SEARCH_RADIUS)
|
|
.sort((a, b) => a.distance - b.distance)
|
|
.map(({ station }) => station);
|
|
};
|
|
|
|
export const getNearbyStationGroups = ({
|
|
coordinates,
|
|
originalStationList,
|
|
lineNames,
|
|
touristSpotKey = "観光スポット",
|
|
}: NearbyStationGroupsArgs): StationProps[][] => {
|
|
const nearbyStations = lineNames.flatMap((lineName) =>
|
|
sortNearbyStations(originalStationList[lineName] ?? [], coordinates),
|
|
);
|
|
nearbyStations.push(
|
|
...sortNearbyStations(
|
|
originalStationList[touristSpotKey] ?? [],
|
|
coordinates,
|
|
),
|
|
);
|
|
|
|
const groupsByStationName = new Map<string, StationProps[]>();
|
|
for (const station of nearbyStations) {
|
|
const group = groupsByStationName.get(station.Station_JP);
|
|
if (group) {
|
|
group.push(station);
|
|
} else {
|
|
groupsByStationName.set(station.Station_JP, [station]);
|
|
}
|
|
}
|
|
|
|
return Array.from(groupsByStationName.values()).sort(
|
|
(a, b) => distanceFrom(a[0], coordinates) - distanceFrom(b[0], coordinates),
|
|
);
|
|
};
|
|
|
|
export const getMenuStationGroups = ({
|
|
stationSource,
|
|
originalStationList,
|
|
nearbyPositionStations,
|
|
favoriteStations,
|
|
findStationsByName,
|
|
lineWebIds,
|
|
stationIds,
|
|
}: MenuStationGroupsArgs): StationProps[][] => {
|
|
if (stationSource.type === "position") {
|
|
return nearbyPositionStations.filter((station) => station !== undefined && station !== null);
|
|
}
|
|
|
|
if (stationSource.type === "favorite") {
|
|
return favoriteStations.filter((station) => station !== undefined && station !== null);
|
|
}
|
|
|
|
const { query, lineId } = stationSource;
|
|
if (!query && !lineId) return [];
|
|
|
|
if (!query) {
|
|
const groups: StationProps[][] = [];
|
|
for (const lineName of Object.keys(lineWebIds)) {
|
|
const lineIdForName = stationIds[lineWebIds[lineName]];
|
|
if (lineId !== lineIdForName) continue;
|
|
|
|
for (const station of originalStationList[lineName] ?? []) {
|
|
if (station.StationNumber) groups.push([station]);
|
|
}
|
|
}
|
|
return groups;
|
|
}
|
|
|
|
const groupsByStationName = new Map<string, StationProps[]>();
|
|
for (const station of findStationsByName(query)) {
|
|
const group = groupsByStationName.get(station.Station_JP);
|
|
if (group) {
|
|
group.push(station);
|
|
} else {
|
|
groupsByStationName.set(station.Station_JP, [station]);
|
|
}
|
|
}
|
|
return Array.from(groupsByStationName.values());
|
|
};
|