Skip to content

Commit

Permalink
ClimbingGradesTable: Add table
Browse files Browse the repository at this point in the history
  • Loading branch information
jvaclavik committed Nov 14, 2024
1 parent d485236 commit fd15fbd
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 25 deletions.
1 change: 1 addition & 0 deletions pages/climbing-grades.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from '../src/components/App/App';
2 changes: 2 additions & 0 deletions src/components/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
} from '../../services/climbing-areas/getClimbingAreas';
import { DirectionsBox } from '../Directions/DirectionsBox';
import { Scrollbars } from 'react-custom-scrollbars';
import { ClimbingGradesTable } from '../FeaturePanel/Climbing/ClimbingGradesTable';

const usePersistMapView = () => {
const { view } = useMapStateContext();
Expand Down Expand Up @@ -164,6 +165,7 @@ const IndexWithProviders = ({ climbingAreas }: IndexWithProvidersProps) => {
<HomepagePanel />
{router.pathname === '/my-ticks' && <MyTicksPanel />}
{router.pathname === '/install' && <InstallDialog />}
{router.pathname === '/climbing-grades' && <ClimbingGradesTable />}
{climbingAreas && <ClimbingAreasPanel areas={climbingAreas} />}
<Map />
<TitleAndMetaTags />
Expand Down
256 changes: 256 additions & 0 deletions src/components/FeaturePanel/Climbing/ClimbingGradesTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import React, { useState } from 'react';
import {
AppBar,
Chip,
Dialog,
DialogContent,
IconButton,
Paper,
Stack,
Table,
TableBody,
TableCell,
TableContainer,
TableFooter,
TableHead,
TableRow,
Toolbar,
Tooltip,
Typography,
} from '@mui/material';

import { ClimbingCragDialogHeader } from './ClimbingCragDialogHeader';
import { ClimbingView } from './ClimbingView';
import { GRADE_SYSTEMS, GRADE_TABLE } from './utils/grades/gradeData';
import CloseIcon from '@mui/icons-material/Close';
import zip from 'lodash/zip';
import styled from '@emotion/styled';
import { useTheme } from '@emotion/react';
import { deleteFromArray } from './utils/array';
import { getGradeSystemName } from './utils/grades/routeGrade';
import { convertHexToRgba } from '../../utils/colorUtils';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import { useFeatureContext } from '../../utils/FeatureContext';
import Router from 'next/router';
import { getUrlOsmId } from '../../../services/helpers';
import { useMobileMode } from '../../helpers';
import { t } from '../../../services/intl';

const Thead = styled.thead`
position: sticky;
top: 0px;
background: black;
`;

const Th = styled.th`
padding: 4px;
`;
// const Table = styled.table``;

const Td = styled.td<{ $isClicked: boolean }>`
padding: 4px;
cursor: pointer;
${({ $isClicked }) => $isClicked && `background-color: rgba(0, 0, 0, 0.2)`}
&:hover {
background-color: rgba(0, 0, 0, 0.8);
}
`;

export const ClimbingGradesTable = () => {
const [clickedItem, setClickedItem] = React.useState<{
row: number | undefined;
column: number | undefined;
}>({ row: undefined, column: undefined });
const isMobileMode = useMobileMode();

const [visibleGradeSystems, setVisibleGradeSystems] = useState(
GRADE_SYSTEMS.reduce((acc, { key }) => ({ ...acc, [key]: true }), {}),
);

const { feature } = useFeatureContext();

const theme = useTheme();
const handleClose = () => {
Router.push(
`/${feature?.osmMeta ? getUrlOsmId(feature.osmMeta) : ''}${window.location.hash}`,
);
};

const transposedTable = zip(...Object.values(GRADE_TABLE));

const getRowBackgroundColor = (rowIndex) => {
return clickedItem.row === rowIndex
? convertHexToRgba(theme.palette.primary.main, 0.1)
: 'transparent';
};

const getCellColors = (columnIndex, rowIndex) => {
if (clickedItem.row === rowIndex && clickedItem.column === columnIndex) {
return {
backgroundColor: convertHexToRgba(theme.palette.primary.main, 0.5),
color: theme.palette.getContrastText(theme.palette.primary.main),
fontWeight: 'bold',
};
}
if (clickedItem.column === columnIndex) {
return {
backgroundColor: convertHexToRgba(theme.palette.primary.main, 0.1),
};
}
return { backgroundColor: 'transparent' };
};

const columns = Object.keys(visibleGradeSystems).filter(
(gradeSystemKey) => visibleGradeSystems[gradeSystemKey],
);

const hiddenGradeSystems = GRADE_SYSTEMS.filter(
(gradeSystem) => !visibleGradeSystems[gradeSystem.key],
);

return (
<Dialog maxWidth="xl" open onClose={handleClose} fullScreen>
<AppBar position="static" color="transparent">
<Toolbar>
<Stack
direction="row"
spacing={1}
justifyContent="space-between"
width="100%"
>
<Typography noWrap variant="h6" component="div">
{t('climbing_grade_table.title')}
</Typography>
<IconButton color="primary" edge="end" onClick={handleClose}>
<CloseIcon fontSize="small" />
</IconButton>
</Stack>
</Toolbar>
</AppBar>

<TableContainer component={Paper} sx={{ maxHeight: '100vh' }}>
<Table stickyHeader size="small">
<TableHead>
<TableRow>
{columns.map((gradeSystemKey) => {
const grade = GRADE_SYSTEMS.find(
(item) => item.key === gradeSystemKey,
);
return (
<TableCell
sx={{ cursor: 'help' }}
key={`header-cell-${gradeSystemKey}`}
>
<Stack direction="row" spacing={1}>
<Tooltip
arrow
title={grade.description}
placement="right"
>
<Stack direction="row" spacing={2} alignItems="center">
<Typography variant="caption" fontWeight={900}>
{grade.name}
</Typography>

<InfoOutlinedIcon
fontSize="small"
color="secondary"
/>
</Stack>
</Tooltip>
<IconButton
size="small"
color="secondary"
onClick={() =>
setVisibleGradeSystems({
...visibleGradeSystems,
[grade.key]: false,
})
}
>
<CloseIcon />
</IconButton>
</Stack>
</TableCell>
);
})}
</TableRow>
</TableHead>
<TableBody>
{transposedTable.map((grades, rowIndex) => (
<TableRow
key={`row-${rowIndex}`}
sx={{
'&:last-child td, &:last-child th': { border: 0 },
backgroundColor: getRowBackgroundColor(rowIndex),
}}
>
{grades
.filter((_, columnIndex) => {
return Object.values(visibleGradeSystems)[columnIndex];
})
.map((grade, columnIndex) => (
<TableCell
key={`cell-${rowIndex}-${columnIndex}-${grade}`}
onClick={() => {
setClickedItem({
column: columnIndex,
row: rowIndex,
});
}}
sx={{
...getCellColors(columnIndex, rowIndex),
fontFamily: 'Courier, monospace',
'&:hover': {
cursor: 'pointer',
backgroundColor: convertHexToRgba(
theme.palette.primary.main,
0.3,
),
},
}}
>
{grade}
</TableCell>
))}
</TableRow>
))}
</TableBody>
{hiddenGradeSystems.length > 0 && (
<TableFooter>
<TableRow>
<TableCell colSpan={columns.length}>
<Stack direction="row" spacing={1}>
<Typography
variant="caption"
sx={{ mr: 2, mt: 1, alignSelf: 'center' }}
>
{t('climbing_grade_table.show')}
</Typography>
{hiddenGradeSystems.map((gradeSystem) => {
return (
<Chip
key={`chip-${gradeSystem.key}`}
size="small"
label={gradeSystem.name}
variant="outlined"
onClick={() => {
setVisibleGradeSystems({
...visibleGradeSystems,
[gradeSystem.key]: true,
});
}}
/>
);
})}
</Stack>
</TableCell>
</TableRow>
</TableFooter>
)}
</Table>
</TableContainer>
</Dialog>
);
};
63 changes: 40 additions & 23 deletions src/components/FeaturePanel/Climbing/GradeSystemSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import React from 'react';

import { Select, MenuItem, Tooltip, FormControl } from '@mui/material';
import ViewListIcon from '@mui/icons-material/ViewList';
import {
Select,
MenuItem,
Tooltip,
FormControl,
IconButton,
Stack,
} from '@mui/material';
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import styled from '@emotion/styled';
import { GRADE_SYSTEMS, GradeSystem } from './utils/grades/gradeData';
import Link from 'next/link';

type Props = {
selectedGradeSystem: GradeSystem;
Expand All @@ -29,25 +37,34 @@ export const GradeSystemSelect = ({
onClick,
allowUnsetValue = true,
}: Props) => (
<FormControl size="small">
<Select
value={selectedGradeSystem}
onClick={onClick}
onChange={(event: any) => {
setGradeSystem(event.target.value);
}}
>
{allowUnsetValue && <MenuItem value={null}>Original grade</MenuItem>}
{GRADE_SYSTEMS.map(({ key, name, description }) => (
<MenuItem key={key} value={key}>
<Row>
<div>{name}</div>
<Tooltip arrow title={description} placement="right">
<StyledInfoOutlinedIcon fontSize="small" color="secondary" />
</Tooltip>
</Row>
</MenuItem>
))}
</Select>
</FormControl>
<Stack direction="row" spacing={1} alignItems="center">
<FormControl size="small">
<Select
value={selectedGradeSystem}
onClick={onClick}
onChange={(event: any) => {
setGradeSystem(event.target.value);
}}
>
{allowUnsetValue && <MenuItem value={null}>Original grade</MenuItem>}
{GRADE_SYSTEMS.map(({ key, name, description }) => (
<MenuItem key={key} value={key}>
<Row>
<div>{name}</div>
<Tooltip arrow title={description} placement="right">
<StyledInfoOutlinedIcon fontSize="small" color="secondary" />
</Tooltip>
</Row>
</MenuItem>
))}
</Select>
</FormControl>
<Tooltip title="Show grades conversion table">
<Link href="/climbing-grades">
<IconButton size="small" color="secondary">
<ViewListIcon fontSize="small" />
</IconButton>
</Link>
</Tooltip>
</Stack>
);
19 changes: 17 additions & 2 deletions src/components/Map/TopMenu/HamburgerMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import BrightnessAutoIcon from '@mui/icons-material/BrightnessAuto';
import TerrainIcon from '@mui/icons-material/Terrain';
import Brightness4Icon from '@mui/icons-material/Brightness4';
import BrightnessHighIcon from '@mui/icons-material/BrightnessHigh';
import React, { useEffect, useState } from 'react';
Expand All @@ -18,6 +19,8 @@ import GithubIcon from '../../../assets/GithubIcon';
import { LangSwitcher } from './LangSwitcher';
import { HamburgerIconButton } from './HamburgerIconButton';
import { PROJECT_ID } from '../../../services/project';
import ViewListIcon from '@mui/icons-material/ViewList';
import Link from 'next/link';

const StyledGithubIcon = styled(GithubIcon)`
filter: ${({ theme }) => theme.palette.invertFilter};
Expand Down Expand Up @@ -116,10 +119,17 @@ const GithubLink = ({ closeMenu }) => (
</MenuItem>
);
const ClimbingAreasLink = ({ closeMenu }) => (
<MenuItem href="/climbing-areas" component="a" onClick={closeMenu}>
<MenuItem href="/climbing-areas" component={Link} onClick={closeMenu}>
<TerrainIcon fontSize="inherit" sx={{ mr: 1 }} />
{t('climbingareas.title')}
</MenuItem>
);
const ClimbingGradesTableLink = ({ closeMenu }) => (
<MenuItem href="/climbing-grades" component={Link} onClick={closeMenu}>
<ViewListIcon fontSize="small" />
{t('climbing_grade_table.title')}
</MenuItem>
);

const InstallLink = ({ closeMenu }) => {
const handleClick = () => {
Expand Down Expand Up @@ -194,7 +204,12 @@ export const HamburgerMenu = () => {
<InstallLink closeMenu={close} />
<AboutLink closeMenu={close} />
<GithubLink closeMenu={close} />
{isOpenClimbing && <ClimbingAreasLink closeMenu={close} />}
{isOpenClimbing && (
<>
<ClimbingAreasLink closeMenu={close} />
<ClimbingGradesTableLink closeMenu={close} />
</>
)}
<StyledDivider />
<LangSwitcher />
</Menu>
Expand Down
Loading

0 comments on commit fd15fbd

Please sign in to comment.