Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LayerSwitcher: add secondLine for some layers #963

Merged
merged 1 commit into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/Directions/Result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const MobileResult = ({
<strong>{distance}</strong> • <strong>{time}</strong> • ↑{ascent}
<TooltipButton
tooltip={<PoweredBy result={result} />}
color="secondary"
sx={{ color: 'secondary', fontSize: '16px' }}
/>
</div>
<Stack direction="row" justifyContent="space-between">
Expand Down
15 changes: 15 additions & 0 deletions src/components/LayerSwitcher/AddCustomLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
CircularProgress,
TextField,
Autocomplete,
Typography,
Box,
} from '@mui/material';
import {
Category as CategoryType,
Expand Down Expand Up @@ -312,6 +314,19 @@ export const AddCustomDialog: React.FC<AddDialogProps> = ({
</DialogTitle>

<DialogContent>
<Box mb={2}>
<Typography variant="body1">
Layers are sourced from the{' '}
<a
href="https://github.com/osmlab/editor-layer-index"
target="_blank"
>
editor-layer-index
</a>{' '}
list.
</Typography>
</Box>

<LayerDataInput
onSelect={(newLayer) => {
if (!newLayer) {
Expand Down
8 changes: 6 additions & 2 deletions src/components/LayerSwitcher/BaseLayers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import React from 'react';
import { ListItemButton, ListItemText, Tooltip } from '@mui/material';
import WarningIcon from '@mui/icons-material/Warning';
import { TooltipButton } from '../utils/TooltipButton';

const OutsideOfView = () => (
<Tooltip title="Not visible currently" arrow placement="top-end">
Expand All @@ -26,7 +27,7 @@ const getIsOutsideOfView = (bboxes: Bbox[], view: View) =>

const BaseLayerItem = ({ layer }: { layer: Layer }) => {
const { view, activeLayers, setActiveLayers } = useMapStateContext();
const { key, name, type, url, Icon, bboxes } = layer;
const { key, name, type, url, Icon, bboxes, secondLine } = layer;

const handleClick = () => {
setActiveLayers((prev) => [key, ...prev.slice(1)]);
Expand All @@ -38,7 +39,10 @@ const BaseLayerItem = ({ layer }: { layer: Layer }) => {
<>
<ListItemButton key={key} selected={selected} onClick={handleClick}>
<LayerIcon Icon={Icon} />
<ListItemText primary={name} />
<ListItemText
primary={name}
secondary={selected && secondLine ? secondLine : undefined}
/>
{isOutsideOfView && <OutsideOfView />}
{type === 'user' && <RemoveUserLayerAction url={url} />}
</ListItemButton>
Expand Down
14 changes: 11 additions & 3 deletions src/components/LayerSwitcher/Overlays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ import { nl2br } from '../utils/nl2br';
const getLocalTime = (lastRefresh: string) =>
lastRefresh ? new Date(lastRefresh).toLocaleString(intl.lang) : null;

const HOST = process.env.NEXT_PUBLIC_CLIMBING_TILES_LOCAL
? '/'
: 'https://openclimbing.org/';

const fetchClimbingStats = () =>
fetchJson<ClimbingStatsResponse>('/api/climbing-tiles/stats');
fetchJson<ClimbingStatsResponse>(`${HOST}api/climbing-tiles/stats`);

const ClimbingSecondary = () => {
const { data, error, isFetching } = useQuery([], () => fetchClimbingStats());
Expand Down Expand Up @@ -59,7 +63,10 @@ const ClimbingSecondary = () => {
return (
<>
{getLocalTime(osmDataTimestamp).replace(/:\d+( [APM]+)?$/, '$1')}
<TooltipButton fontSize={14} tooltip={tooltip} />
<TooltipButton
sx={{ fontSize: '14px', margin: '-8px -3px -6px -3px' }}
tooltip={tooltip}
/>
</>
);
};
Expand All @@ -77,7 +84,8 @@ const OverlayItem = ({ layer }: { layer: Layer }) => {
e.stopPropagation();
};
const selected = activeLayers.includes(key);
const secondary = key === 'climbing' ? <ClimbingSecondary /> : undefined;
const secondary =
key === 'climbing' && selected ? <ClimbingSecondary /> : undefined;

return (
<ListItemButton onClick={handleClick} key={key}>
Expand Down
10 changes: 3 additions & 7 deletions src/components/LayerSwitcher/osmappLayers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,12 @@ const czBbox: Bbox = [
export const osmappLayers: Layers = {
basic: {
name: `${t('layers.basic')} Maptiler`,
description: 'maptiler.com',
type: 'basemap',
Icon: ExploreIcon,
attribution: ['maptiler', 'osm'],
},
basicOfr: {
name: `${t('layers.basic')} OpenFreeMap (beta)`,
description: '',
type: 'basemap',
Icon: ExploreIcon,
attribution: [
Expand All @@ -62,7 +60,6 @@ export const osmappLayers: Layers = {
},
makinaAfrica: {
name: t('layers.makina_africa'),
description: 'OpenPlaceGuide.org',
type: 'basemap',
Icon: ExploreIcon,
attribution: [
Expand All @@ -73,15 +70,14 @@ export const osmappLayers: Layers = {
},
outdoor: {
name: t('layers.outdoor'),
description: 'Maptiler.com',
type: 'basemap',
Icon: FilterHdrIcon,
attribution: ['maptiler', 'osm'],
// https://api.maptiler.com/tiles/outdoor/tiles.json?key=7dlhLl3hiXQ1gsth0kGu .planettime="1703030400000",
},
s1: { type: 'spacer' },
carto: {
name: t('layers.carto'),
description: 'Default OSM.org style',
type: 'basemap',
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
Icon: MapIcon,
Expand Down Expand Up @@ -117,7 +113,7 @@ export const osmappLayers: Layers = {
// },
bike: {
name: t('layers.bike'),
description: 'Thunderforest.com',
secondLine: 'Thunderforest.com',
type: 'basemap',
url: `https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}${retina}.png?apikey=${process.env.NEXT_PUBLIC_API_KEY_THUNDERFOREST}`,
Icon: DirectionsBikeIcon,
Expand All @@ -128,7 +124,7 @@ export const osmappLayers: Layers = {
},
transport: {
name: t('layers.transport'),
description: 'Thunderforest.com',
secondLine: 'Thunderforest.com',
type: 'basemap',
url: `https://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}${retina}.png?apikey=${process.env.NEXT_PUBLIC_API_KEY_THUNDERFOREST}`,
darkUrl: `https://{s}.tile.thunderforest.com/transport-dark/{z}/{x}/{y}${retina}.png?apikey=${process.env.NEXT_PUBLIC_API_KEY_THUNDERFOREST}`,
Expand Down
8 changes: 4 additions & 4 deletions src/components/utils/MapStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ export type LayerIcon = React.ComponentType<{ fontSize: 'small' }>;
// [b.getWest(), b.getNorth(), b.getEast(), b.getSouth()]
export type Bbox = [number, number, number, number];

export interface Layer {
export type Layer = {
type: 'basemap' | 'overlay' | 'user' | 'spacer';
name?: string;
description?: string;
secondLine?: string;
url?: string;
darkUrl?: string; // optional url for dark mode
key?: string;
Icon?: LayerIcon;
attribution?: string[]; // missing in spacer TODO refactor ugly
attribution?: string[]; // missing in spacer TODO refactor this ugly type
maxzoom?: number;
minzoom?: number;
bboxes?: Bbox[];
}
};

// [z, lat, lon] - string because we use RoundedPosition
export type View = [string, string, string];
Expand Down
31 changes: 13 additions & 18 deletions src/components/utils/TooltipButton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React, { useEffect, useRef } from 'react';
import { IconButton, Tooltip } from '@mui/material';
import { IconButton, SxProps, Theme, Tooltip } from '@mui/material';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import { SvgIconOwnProps } from '@mui/material/SvgIcon/SvgIcon';
import { isMobileDevice, useBoolState } from '../helpers';
import styled from '@emotion/styled';

const useClickAwayListener = (
tooltipRef: React.MutableRefObject<HTMLDivElement>,
Expand All @@ -29,24 +27,21 @@ const useClickAwayListener = (
}, [hide, isMobile, tooltipRef]);
};

const StyledIconButton = styled(IconButton)<{ fontSize?: number }>`
font-size: ${({ fontSize }) => (fontSize ? `${fontSize}px` : 'inherit')};
`;

type Props = {
tooltip: React.ReactNode;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
color?: SvgIconOwnProps['color'];
fontSize?: number;
sx?: SxProps<Theme>;
};

export const TooltipButton = ({ tooltip, onClick, color, fontSize }: Props) => {
/**
* Button with InfoIcon, which works on both desktop and mobile.
* (Desktop onHover, Mobile onClick)
*/
export const TooltipButton = ({ tooltip, sx }: Props) => {
const isMobile = isMobileDevice();
const tooltipRef = useRef<HTMLDivElement>(null);
const [mobileTooltipShown, show, hide] = useBoolState(false);

const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(e);
if (isMobile) {
show();
}
Expand All @@ -55,10 +50,10 @@ export const TooltipButton = ({ tooltip, onClick, color, fontSize }: Props) => {

useClickAwayListener(tooltipRef, hide, isMobile);

const content = (
<StyledIconButton onClick={handleClick} fontSize={fontSize}>
<InfoOutlinedIcon fontSize="inherit" color={color} />
</StyledIconButton>
const button = (
<IconButton onClick={handleClick} sx={sx}>
<InfoOutlinedIcon fontSize="inherit" color="inherit" />
</IconButton>
);

// There is a bug in MUI, passing `open={undefined}` prop to Tooltip makes it uninteractive TODO check again eg 6/2025, or report
Expand All @@ -70,7 +65,7 @@ export const TooltipButton = ({ tooltip, onClick, color, fontSize }: Props) => {
open={mobileTooltipShown}
ref={tooltipRef}
>
{content}
{button}
</Tooltip>
) : (
<Tooltip
Expand All @@ -80,7 +75,7 @@ export const TooltipButton = ({ tooltip, onClick, color, fontSize }: Props) => {
//open={isMobile ? mobileTooltipShown : undefined} -- broken, see above
ref={tooltipRef}
>
{content}
{button}
</Tooltip>
);
};