Skip to content

Commit

Permalink
mobile responsiveness
Browse files Browse the repository at this point in the history
  • Loading branch information
nicokant committed Jul 15, 2024
1 parent 3bf7d0c commit c467b6f
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 64 deletions.
3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="SHORTCUT ICON" href="favicon.ico" type="image/x-icon">
<link rel="SHORTCUT ICON" href="favicon.ico" type="image/x-icon" />
<title>Maps | Norsk institutt for naturforskning</title>
<script src="/config.js"></script>
</head>
<body>
<div id="app"></div>
<div id="sidebar-portal"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
18 changes: 12 additions & 6 deletions src/components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Element } from "react-bulma-components";
import logo from "../assets/logowhite.png";
import MediaQuery from "react-responsive";
import { DESKTOP_SIZE, TABLET_SIZE } from "../constants";

export default function Footer() {
return (
Expand All @@ -20,13 +22,17 @@ export default function Footer() {
alignItems="baseline"
>
<img src={logo} alt="nina logo" className="logo" />
<Element className="has-text-white is-size-7" ml={2}>
Norsk institutt for naturforskning - www.nina.no
</Element>
</Element>
<Element className="has-text-white is-size-7" ml="auto">
Samarbeid og kunnskap for framtidas miljøløsninger
<MediaQuery minWidth={TABLET_SIZE}>
<Element className="has-text-white is-size-7" ml={2}>
Norsk institutt for naturforskning - www.nina.no
</Element>
</MediaQuery>
</Element>
<MediaQuery minWidth={DESKTOP_SIZE}>
<Element className="has-text-white is-size-7" ml="auto">
Samarbeid og kunnskap for framtidas miljøløsninger
</Element>
</MediaQuery>
</Element>
);
}
4 changes: 4 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export const BACKGROUND_TILES = {
y: 4,
z: 4,
};

export const TABLET_SIZE = 770;
export const DESKTOP_SIZE = 960;
export const HD_SIZE = 1200;
20 changes: 15 additions & 5 deletions src/pages/viewer/components/LayersVTree.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import LegendSymbol from "./LegendSymbol";
import { Icon } from "react-bulma-components";
import AutoSizer from "react-virtualized-auto-sizer";
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
import { HD_SIZE } from "../../../constants";
import { useMediaQuery } from "react-responsive";

function flyToLayer(map, layer) {
if (map && layer && layer.source) {
Expand Down Expand Up @@ -165,10 +167,12 @@ function Child({ data, isOpen, style, setOpen }) {
);
}

const getNodeData = (node, nestingLevel) => ({
const getNodeData = (node, nestingLevel, isSmallScreen) => ({
data: {
...node,
defaultHeight: (Math.round(node.name.length / 80) + 1) * 30,
defaultHeight: isSmallScreen
? 30
: (Math.round(node.name.length / 80) + 1) * 30,
// defaultHeight: 60,
id: node.id.toString(), // mandatory
isLeaf: node.children ? node.children.length === 0 : true,
Expand All @@ -181,10 +185,12 @@ const getNodeData = (node, nestingLevel) => ({
});

export default function Layers({ layers = [] }) {
const isSmallScreen = useMediaQuery({ maxWidth: HD_SIZE });

const tw = useMemo(() => {
function* treeWalker() {
for (let layer of layers) {
yield getNodeData(layer, 0);
yield getNodeData(layer, 0, isSmallScreen);
}

while (true) {
Expand All @@ -196,14 +202,18 @@ export default function Layers({ layers = [] }) {
for (let i = 0; i < parent.node.children.length; i++) {
// Step [3]: Yielding all the children of the provided component. Then we
// will return for the step [2] with the first children.
yield getNodeData(parent.node.children[i], parent.nestingLevel + 1);
yield getNodeData(
parent.node.children[i],
parent.nestingLevel + 1,
isSmallScreen,
);
}
}
}
}

return treeWalker;
}, [layers]);
}, [layers, isSmallScreen]);

return (
<div className="layers">
Expand Down
3 changes: 3 additions & 0 deletions src/pages/viewer/components/MapContextProvider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function setCursorStyle(style, map) {

export default function MapContextProvider({ children }) {
const map = React.useRef(null);
const [sidebar, setSidebar] = React.useState(true);
const [ready, setReady] = React.useState(false);
const [layers, setLayers] = React.useState({});
const [style, setStyle] = React.useState(null);
Expand Down Expand Up @@ -120,6 +121,8 @@ export default function MapContextProvider({ children }) {
basemaps,
visibleLayers,
setVisibleLayers,
setSidebar,
sidebar,
};

return <MapContext.Provider value={value}>{children}</MapContext.Provider>;
Expand Down
61 changes: 61 additions & 0 deletions src/pages/viewer/components/Sidebar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useCallback, useContext } from "react";
import { MapContext } from "../contexts";
import useClientHeight from "../useClientHeight";
import Metadata from "./Metadata";
import TabNav from "./TabNav";
import MediaQuery from "react-responsive";
import { HD_SIZE } from "../../../constants";
import { createPortal } from "react-dom";
import { Button, Icon } from "react-bulma-components";

export function DesktopSidebar({ data }) {
const { height, ref } = useClientHeight();
const { sidebar } = useContext(MapContext);
return (
<div id="sidebar" className={!sidebar ? "" : ""}>
<Metadata {...data.data} metadataRef={ref} />
<TabNav map={data} top={height} />
</div>
);
}

export function MobileSidebar({ data }) {
const { height, ref } = useClientHeight();
const { sidebar, setSidebar } = useContext(MapContext);

const onClose = useCallback(() => setSidebar(false), [setSidebar]);

if (!sidebar) {
return null;
}

return (
<div id="sidebar-mobile">
<div id="sidebar-close">
<Button onClick={onClose}>
<Icon>
<i className="fas fa-times"></i>
</Icon>
</Button>
</div>
<Metadata {...data.data} metadataRef={ref} />
<TabNav map={data} top={height} />
</div>
);
}

export default function Sidebar({ data }) {
return (
<>
<MediaQuery maxWidth={HD_SIZE}>
{createPortal(
<MobileSidebar data={data} />,
document.getElementById("sidebar-portal"),
)}
</MediaQuery>
<MediaQuery minWidth={HD_SIZE}>
<DesktopSidebar data={data} />
</MediaQuery>
</>
);
}
23 changes: 23 additions & 0 deletions src/pages/viewer/components/SidebarWidget.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useCallback, useContext } from "react";
import { Button, Icon } from "react-bulma-components";
import MediaQuery from "react-responsive";
import { HD_SIZE } from "../../../constants";
import { MapContext } from "../contexts";

export default function SidebarWidget() {
const { setSidebar } = useContext(MapContext);

const open = useCallback(() => setSidebar(true), [setSidebar]);

return (
<MediaQuery maxWidth={HD_SIZE}>
<div id="sidebar-open">
<Button onClick={open}>
<Icon>
<i className="fas fa-bars"></i>
</Icon>
</Button>
</div>
</MediaQuery>
);
}
37 changes: 37 additions & 0 deletions src/pages/viewer/components/TabNav.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useMemo, useState } from "react";
import useClientHeight from "../useClientHeight";
import { Content, Tabs } from "react-bulma-components";
import Layers from "./LayersVTree";
import parse from "html-react-parser";

const TABS = {
kartlag: {
label: "Kartlag",
render: (map) => <Layers layers={map.data.layers} />,
},
beskrivelse: {
label: "Beskrivelse",
render: (map) => <Content px={2}>{parse(map.data.description)}</Content>,
},
};

export default function TabNav({ map, top = 0 }) {
const [active, setActive] = useState("kartlag");

const { height, ref } = useClientHeight();

const render = useMemo(() => TABS[active].render(map), [active, map]);

return (
<>
<Tabs fullwidth mt={3} domRef={ref}>
{Object.keys(TABS).map((k) => (
<Tabs.Tab active={k === active} key={k} onClick={() => setActive(k)}>
{TABS[k].label}
</Tabs.Tab>
))}
</Tabs>
<div style={{ height: `calc(100vh - ${height + top}px)` }}>{render}</div>
</>
);
}
58 changes: 10 additions & 48 deletions src/pages/viewer/index.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { ErrorComponent, Route } from "@tanstack/react-router";
import parse from "html-react-parser";
import rootRoute from "../root";
import Layers from "./components/LayersVTree";
import Map from "./components/Map";
import MapContextProvider from "./components/MapContextProvider";
import Metadata from "./components/Metadata";
import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
import mapApi from "../../api";
import { Content, Element, Tabs } from "react-bulma-components";
import { useMemo, useState } from "react";
import { Element } from "react-bulma-components";
import { Helmet } from "react-helmet";
import ModalContextProvider from "./components/ModalContextProvider";
import Lazy from "./components/Lazy";
import ErrorWrapper from "../../components/ErrorWrapper";
import Footer from "../../components/Footer";
import useClientHeight from "./useClientHeight";
import SidebarWidget from "./components/SidebarWidget";
import Sidebar from "./components/Sidebar";

const fetchMap = async (mapSlug) => {
const map = await mapApi.get(`maps/${mapSlug}/metadata/`);
Expand Down Expand Up @@ -59,59 +56,24 @@ function MapErrorComponent({ error }) {
return <ErrorComponent error={error} />;
}

const TABS = {
kartlag: {
label: "Kartlag",
render: (map) => <Layers layers={map.data.layers} />,
},
beskrivelse: {
label: "Beskrivelse",
render: (map) => <Content px={2}>{parse(map.data.description)}</Content>,
},
};

function TabNav({ map, top = 0 }) {
const [active, setActive] = useState("kartlag");

const { height, ref } = useClientHeight();

const render = useMemo(() => TABS[active].render(map), [active, map]);

return (
<>
<Tabs fullwidth mt={3} domRef={ref}>
{Object.keys(TABS).map((k) => (
<Tabs.Tab active={k === active} key={k} onClick={() => setActive(k)}>
{TABS[k].label}
</Tabs.Tab>
))}
</Tabs>
<div style={{ height: `calc(100vh - ${height + top}px)` }}>{render}</div>
</>
);
}

export function Viewer() {
const { height, ref } = useClientHeight();
const { mapSlug } = viewerRoute.useParams();
const mapQuery = useSuspenseQuery(mapQueryOptions(mapSlug));
const map = mapQuery.data;
const mapData = mapQuery.data;

return (
<MapContextProvider>
<ModalContextProvider>
<Helmet title={map.data.title} />
<Helmet title={mapData.data.title} />
<div id="app-wrap" style={{ display: "flex" }}>
<div id="sidebar">
<Metadata {...map.data} metadataRef={ref} />
<TabNav map={map} top={height} />
</div>
<Element display="flex" flexDirection="column" id="content">
<Map {...map.data} />
<Sidebar data={mapData} />
<Element id="content">
<Map {...mapData.data} />
<SidebarWidget />
<Footer />
</Element>
</div>
<Lazy lazy={map.data.lazy} />
<Lazy lazy={mapData.data.lazy} />
</ModalContextProvider>
</MapContextProvider>
);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/viewer/useClientHeight.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function useClientHeight() {
setHeight(
height + parseInt(style.marginBottom) + parseInt(style.marginTop),
);
}, []);
}, [ref.current]);

return { height, ref };
}
Loading

0 comments on commit c467b6f

Please sign in to comment.