Skip to content

Commit

Permalink
Merge branch 'master' into ssr
Browse files Browse the repository at this point in the history
  • Loading branch information
zbycz authored Oct 8, 2024
2 parents fa48be8 + 600cd3f commit 2df2864
Show file tree
Hide file tree
Showing 23 changed files with 478 additions and 147 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"canvas": "^2.11.2",
"canvg": "^4.0.2",
"date-fns": "^3.6.0",
"dice-coefficient": "^2.1.1",
"image-size": "^1.1.1",
"isomorphic-unfetch": "^4.0.2",
"isomorphic-xml2js": "^0.1.3",
Expand Down
5 changes: 3 additions & 2 deletions src/components/FeaturePanel/FeaturePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ClimbingRouteGrade } from './ClimbingRouteGrade';
import { Box } from '@mui/material';
import { ClimbingGuideInfo } from './Climbing/ClimbingGuideInfo';
import { ClimbingStructuredData } from './Climbing/ClimbingStructuredData';
import { isPublictransportRoute } from '../../utils';

const Flex = styled.div`
flex: 1;
Expand Down Expand Up @@ -80,11 +81,11 @@ export const FeaturePanel = ({ headingRef }: FeaturePanelProps) => {
<PanelSidePadding>
{!isClimbingCrag && <PropertiesComponent />}
<RouteDistributionInPanel />
<MemberFeatures />
{!isPublictransportRoute(feature) && <MemberFeatures />}
{advanced && <Members />}
{isClimbingCrag && <PropertiesComponent />}

<PublicTransport tags={tags} />
<PublicTransport />
<Runways />

<FeatureOpenPlaceGuideLink />
Expand Down
100 changes: 10 additions & 90 deletions src/components/FeaturePanel/PublicTransport/PublicTransport.tsx
Original file line number Diff line number Diff line change
@@ -1,99 +1,19 @@
import groupBy from 'lodash/groupBy';
import { useQuery } from 'react-query';
import React from 'react';
import { Typography } from '@mui/material';
import { LineInformation, requestLines } from './requestRoutes';
import { PublicTransportCategory } from './PublicTransportWrapper';
import { FeatureTags } from '../../../services/types';
import { DotLoader } from '../../helpers';
import { sortByReference } from './helpers';
import { PublicTransportInner } from './routes/Routes';
import { PublicTransportRoute } from './route/PublicTransportRoute';
import { useFeatureContext } from '../../utils/FeatureContext';
import { getOverpassSource } from '../../../services/mapStorage';
import { EMPTY_GEOJSON_SOURCE } from '../../Map/consts';
import { isPublictransportStop, isPublictransportRoute } from '../../../utils';

interface PublicTransportProps {
tags: FeatureTags;
}

const categories = [
'tourism',
'subway',
'commuter',
'regional',
'long_distance',
'high_speed',
'night',
'car',
'car_shuttle',
'bus',
'unknown',
];

const PublicTransportDisplay = ({ routes }) => {
const grouped = groupBy(routes, ({ service }) => {
const base = service?.split(';')[0];
return categories.includes(base) ? base : 'unknown';
});
const entries = Object.entries(grouped) as [string, LineInformation[]][];
const sorted = sortByReference(entries, categories, ([category]) => category);

return (
<>
{sorted.map(([category, lines]) => (
<PublicTransportCategory
key={category}
category={category}
lines={lines}
showHeading={entries.length > 1}
/>
))}
</>
);
};

const PublicTransportInner = () => {
export const PublicTransport = () => {
const { feature } = useFeatureContext();
const { id, type } = feature.osmMeta;

const { data, status } = useQuery([id, type], () =>
requestLines(type, Number(id)),
);

React.useEffect(() => {
if (!data) {
return;
}

const source = getOverpassSource();
source?.setData(data.geoJson);

return () => {
source?.setData({ type: 'FeatureCollection', features: [] });
};
}, [data]);

return (
<div>
{(status === 'loading' || status === 'idle') && <DotLoader />}
{status === 'success' && <PublicTransportDisplay routes={data.routes} />}
{status === 'error' && (
<Typography color="secondary" paragraph>
Error
</Typography>
)}
</div>
);
};

export const PublicTransport: React.FC<PublicTransportProps> = ({ tags }) => {
const isPublicTransport =
Object.keys(tags).includes('public_transport') ||
tags.railway === 'station' ||
tags.railway === 'halt';
if (isPublictransportStop(feature)) {
return <PublicTransportInner />;
}

if (!isPublicTransport) {
return null;
if (isPublictransportRoute(feature)) {
return <PublicTransportRoute />;
}

return <PublicTransportInner />;
return null;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useFeatureContext } from '../../../utils/FeatureContext';
import { Feature } from '../../../../services/types';
import React from 'react';
import { t } from '../../../../services/intl';
import { getUrlOsmId } from '../../../../services/helpers';
import { Stops } from './Stops';

export const PublicTransportRoute = () => {
const { feature } = useFeatureContext();

if (!feature.memberFeatures) return null;
if (!feature.members) return null;

const stopIds = feature.members
.filter(({ role }) => role === 'stop')
.map(({ ref, type }) => getUrlOsmId({ id: ref, type }));
const stops = feature.memberFeatures.filter(({ osmMeta }) =>
stopIds.includes(getUrlOsmId(osmMeta)),
);

if (stops.length === 0) {
return null;
}

return <StopList stops={stops} />;
};

const StopList = ({ stops }: { stops: Feature[] }) => {
const [minimized, setMinimized] = React.useState(stops.length > 7);
const getStops = React.useCallback(
() => (minimized ? [stops[0], stops[stops.length - 1]] : stops),
[minimized, stops],
);
const [renderedStops, setRenderedStops] = React.useState(getStops());

React.useEffect(() => {
setRenderedStops(getStops());
}, [minimized, getStops]);

return (
<>
<h3>{t('publictransport.route')}</h3>
<Stops
stops={renderedStops}
stopCount={stops.length}
onExpand={() => {
setMinimized(false);
}}
onCollapse={() => {
setMinimized(true);
}}
/>
</>
);
};
46 changes: 46 additions & 0 deletions src/components/FeaturePanel/PublicTransport/route/Stop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import TripOriginIcon from '@mui/icons-material/TripOrigin';

export type StopSection = 'start' | 'middle' | 'end';

type Props = {
color: string;
isFirst: boolean;
isLast: boolean;
showCircle?: boolean;
};

export const Stop = ({ color, isFirst, isLast, showCircle = true }: Props) => (
<div
style={{
width: '1rem',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}
>
<div
style={{
width: '4px',
height: '100%',
background: isFirst ? 'transparent' : color,
}}
/>
{showCircle && (
<TripOriginIcon
style={{
color: color,
width: '100%',
margin: '-6px',
}}
/>
)}
<div
style={{
width: '4px',
height: '100%',
background: isLast ? 'transparent' : color,
}}
/>
</div>
);
120 changes: 120 additions & 0 deletions src/components/FeaturePanel/PublicTransport/route/Stops.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import Link from 'next/link';
import { Feature } from '../../../../services/types';
import { StationItem, StationsList } from './helpers';
import { IconButton, Typography } from '@mui/material';
import { t } from '../../../../services/intl';
import { getUrlOsmId } from '../../../../services/helpers';
import TurnLeftIcon from '@mui/icons-material/TurnLeft';

type ShowMoreLessButtonProps = {
type: 'collapse' | 'expand';
stopCount: number;
onClick?: (e: unknown) => void;
};

const ShowMoreLessButton = ({
type,
onClick,
stopCount,
}: ShowMoreLessButtonProps) => (
<Typography variant="body2" color="textSecondary">
<IconButton aria-label={type} onClick={onClick}>
<TurnLeftIcon
style={{
transform:
type === 'collapse' ? 'rotate(90deg) scaleY(-1)' : 'rotate(-90deg)',
}}
/>
</IconButton>
{type === 'collapse'
? t('publictransport.visible_stops', {
amount: stopCount - 2,
})
: t('publictransport.hidden_stops', {
amount: stopCount - 2,
})}
</Typography>
);

const StationInner = ({
stop,
stopCount,
onExpand,
onCollapse,
}: {
stop: Feature | 'collapse' | 'expand';
stopCount: number;
onExpand: () => void;
onCollapse: () => void;
}) => {
if (stop === 'expand') {
return (
<Typography variant="body2" color="textSecondary">
<IconButton
aria-label="expand"
onClick={() => {
onExpand();
}}
>
<TurnLeftIcon style={{ transform: 'rotate(-90deg)' }} />
</IconButton>
{t('publictransport.hidden_stops', {
amount: stopCount - 2,
})}
</Typography>
);
}
if (stop === 'collapse') {
return (
<Typography variant="body2" color="textSecondary">
<IconButton
aria-label="expand"
onClick={() => {
onCollapse();
}}
></IconButton>
{t('publictransport.visible_stops', {
amount: stopCount - 2,
})}
</Typography>
);
}

return <Link href={getUrlOsmId(stop.osmMeta)}>{stop.tags.name}</Link>;
};

type Props = {
stops: Feature[];
stopCount: number;
onExpand: () => void;
onCollapse: () => void;
};

export const Stops = ({ stops, stopCount, onExpand, onCollapse }: Props) => {
const hasFullLength = stops.length === stopCount;
return (
<StationsList>
{stops.map((stop, i) => (
<>
<StationItem isFirst={i === 0} isLast={i === stops.length - 1}>
<StationInner
stop={stop}
stopCount={stopCount}
onExpand={onExpand}
onCollapse={onCollapse}
/>
</StationItem>
{i === 0 && (
<StationItem showCircle={false}>
<ShowMoreLessButton
type={hasFullLength ? 'collapse' : 'expand'}
stopCount={stopCount}
onClick={hasFullLength ? onCollapse : onExpand}
/>
</StationItem>
)}
</>
))}
</StationsList>
);
};
Loading

0 comments on commit 2df2864

Please sign in to comment.