Skip to content

Commit

Permalink
LayerSwitcher: add secondLine for some layers (#963)
Browse files Browse the repository at this point in the history
  • Loading branch information
zbycz authored Feb 27, 2025
1 parent ae368fe commit 423c1a7
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 35 deletions.
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>
);
};

0 comments on commit 423c1a7

Please sign in to comment.