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

Desktop Layout [WIP] #398

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"@formatjs/intl-listformat": "^7.1.7",
"@headlessui/react": "^2.2.0",
"@nivo/line": "^0.88.0",
"@nivo/axes": "^0.88.0",
"@nivo/scales": "^0.88.0",
"@placemarkio/geo-viewport": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.4",
Expand Down Expand Up @@ -42,6 +44,7 @@
"react-intl": "^7.0.1",
"react-map-gl": "^7.0.6",
"react-redux": "^8.0.5",
"react-reverse-portal": "^2.1.2",
"react-spinners": "^0.14.1",
"redux": "^4.1.2",
"redux-thunk": "^2.4.1",
Expand Down
75 changes: 51 additions & 24 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useState } from 'react';
import { useEffect, useState, useRef, useMemo } from 'react';
import type { FocusEvent } from 'react';
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
import { IntlProvider } from 'react-intl';
import type { MessageFormatElement } from 'react-intl';
import type { OnErrorFn } from '@formatjs/intl';
import { Transition } from '@headlessui/react';
import { Provider as ToastProvider } from '@radix-ui/react-toast';
import { createHtmlPortalNode, InPortal } from 'react-reverse-portal';

import DirectionsNullState from './DirectionsNullState';
import MapPlusOverlay from './MapPlusOverlay';
import Routes from './Routes';
import SearchDropdown from './SearchDropdown';
import Toasts from './Toasts';
Expand All @@ -20,6 +20,11 @@ import {
import { RootState } from '../store';

import './App.css';
import BikeHopperMap from './BikeHopperMap';
import useMapRefs from '../hooks/useMapRefs';
import DesktopMapLayout from './DesktopMapLayout';
import MobileMapLayout from './MobileMapLayout';
import useIsMobile from '../hooks/useIsMobile';

type Props = {
locale: string;
Expand Down Expand Up @@ -54,22 +59,20 @@ function App(props: Props) {
dispatch(enterDestinationFocused());
};

let bottomContent;
if (isEditingLocations) {
bottomContent = <SearchDropdown startOrEnd={editingLocation} />;
} else if (hasRoutes) {
bottomContent = <Routes />;
} else if (!hasLocations) {
bottomContent = (
<DirectionsNullState onInputFocus={handleBottomInputFocus} />
);
}
const isMobile = useIsMobile();

const infoBox = isEditingLocations ? (
<SearchDropdown startOrEnd={editingLocation} />
) : hasRoutes ? (
<Routes />
) : hasLocations ? undefined : (
<DirectionsNullState onInputFocus={handleBottomInputFocus} />
);

const shouldDisplayTopBar = !viewingDetails;
const [haveTopBarIncludingFade, setHaveTopBarIncludingFade] =
useState(shouldDisplayTopBar);
const [_, setHaveTopBarIncludingFade] = useState(shouldDisplayTopBar);

const topBar = (
const header = (
<Transition
as="div"
show={shouldDisplayTopBar}
Expand All @@ -89,24 +92,48 @@ function App(props: Props) {
</Transition>
);

const mapRefs = useMapRefs();

const mapPortal = useMemo(
() => createHtmlPortalNode({ attributes: { id: 'map-portal-node' } }),
[],
);

return (
<IntlProvider
messages={props.messages}
locale={props.locale}
defaultLocale="en"
onError={import.meta.env.DEV ? handleDebugIntlError : () => {}}
>
<InPortal node={mapPortal}>
<BikeHopperMap
ref={mapRefs.mapRef}
onMapLoad={mapRefs.handleMapLoad}
overlayRef={mapRefs.mapOverlayRef}
hidden={isMobile && isEditingLocations}
isMobile={isMobile}
/>
</InPortal>
<ToastProvider>
<div className="App">
<MapPlusOverlay
topContent={topBar}
topBarEmpty={
/* prop change forces controls to move */
!haveTopBarIncludingFade
}
hideMap={isEditingLocations}
bottomContent={bottomContent}
/>
{isMobile ? (
<MobileMapLayout
mapPortal={mapPortal}
mapRefs={mapRefs}
header={header}
hideMap={isEditingLocations}
infoBox={infoBox}
/>
) : (
<DesktopMapLayout
mapPortal={mapPortal}
header={header}
infoBox={infoBox}
mapRefs={mapRefs}
/>
)}

<Toasts />
</div>
</ToastProvider>
Expand Down
9 changes: 8 additions & 1 deletion src/components/BikeHopperMap.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@
top: 0;
bottom: 0;
height: 100vh;
width: 100%;
z-index: 1;
}

.BikeHopperMap__mobile {
width: 100%;
}

.BikeHopperMap__desktop {
width: calc(100vw - 400px);
}

/* render draggable marker over user location circle*/
.maplibregl-marker {
z-index: 3;
Expand Down
24 changes: 16 additions & 8 deletions src/components/BikeHopperMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@ import {
DEFAULT_INACTIVE_COLOR,
} from '../lib/colors';
import { RouteResponsePath } from '../lib/BikeHopperClient';
import useIsMobile from '../hooks/useIsMobile';
import classnames from 'classnames';

const _isTouch = 'ontouchstart' in window;

type Props = {
onMapLoad: () => void;
onMapLoad?: () => void;
overlayRef: RefObject<HTMLElement>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this type not match the overlayRef type in src/hooks/useMapRefs? (This type is not nullable, and could be any HTML element, not just a div.)

hidden: boolean;
isMobile: boolean;
};

type Bbox = [number, number, number, number];
Expand Down Expand Up @@ -355,12 +358,9 @@ const BikeHopperMap = forwardRef(function BikeHopperMapInternal(
};

const resizeRef = useResizeObserver(
useCallback(
([width, height]) => {
if (mapRef.current) mapRef.current.resize();
},
[mapRef],
),
useCallback(() => {
if (mapRef.current) mapRef.current.resize();
}, [mapRef]),
);

// Center viewport on points or routes
Expand Down Expand Up @@ -513,7 +513,15 @@ const BikeHopperMap = forwardRef(function BikeHopperMapInternal(
const viewStateOnFirstRender = useRef(viewState);

return (
<div className="BikeHopperMap" ref={resizeRef} aria-hidden={props.hidden}>
<div
className={classnames({
BikeHopperMap: true,
BikeHopperMap__mobile: props.isMobile,
BikeHopperMap__desktop: !props.isMobile,
})}
ref={resizeRef}
aria-hidden={props.hidden}
>
<MapGL
initialViewState={viewStateOnFirstRender.current}
ref={mapRef}
Expand Down
22 changes: 22 additions & 0 deletions src/components/DesktopMapLayout.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.DesktopMapLayout {
overflow: hidden;
display: flex;
width: 100%;
height: 100%;
flex-flow: row nowrap;
}

.DesktopMapLayout_sidebar {
display: flex;
flex-basis: 400px;
height: 100%;
overflow-y: scroll;
flex-direction: column;
}

.DesktopMapLayout_map {
display: flex;
flex-grow: 1;
height: 100%;
overflow: hidden;
}
65 changes: 65 additions & 0 deletions src/components/DesktopMapLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { MapRefs } from '../hooks/useMapRefs';
import { HtmlPortalNode, OutPortal } from 'react-reverse-portal';

type Props = {
header: JSX.Element;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I elsewhere use React.ReactNode for these. Is there an argument for JSX.Element being more correct?

infoBox?: JSX.Element;
mapRefs: MapRefs;
mapPortal: HtmlPortalNode;
};

import './DesktopMapLayout.css';
import useIsMobile from '../hooks/useIsMobile';
import { useEffect } from 'react';

export default function DesktopMapLayout({
header,
infoBox,
mapRefs,
mapPortal,
}: Props) {
const hideMap = false;

const isMobile = useIsMobile();

const {
mapRef,
mapControlBottomLeftRef,
mapControlBottomRightRef,
mapControlTopLeftRef,
mapControlTopRightRef,
} = mapRefs;

const updateMapTopControls = () => {
if (
!mapRef.current ||
!mapControlTopLeftRef.current ||
!mapControlTopRightRef.current
) {
return;
}

mapControlTopLeftRef.current.style.transform = 'translate3d(0,0,0)';
mapControlTopRightRef.current.style.transform = 'translate3d(0,0,0)';
};

useEffect(() => {
if (!isMobile) {
console.log('now Desktop');
updateMapTopControls();
}
return () => console.log('no longer Desktop');
}, [isMobile]);

return (
<div className="DesktopMapLayout">
<div className="DesktopMapLayout_sidebar">
{header}
{infoBox}
</div>
<div className="DesktopMapLayout_map">
<OutPortal node={mapPortal} isMobile={false} />
</div>
</div>
);
}
12 changes: 1 addition & 11 deletions src/components/DirectionsNullState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,6 @@ export default function DirectionsNullState(props: Props) {
/>
</p>
)}
<p className="hidden lg:block">
<FormattedMessage
defaultMessage={
'In this early beta, BikeHopper is <strong>designed for phone screens</strong>,' +
' so it might look strange on your computer.'
}
description="paragraph in welcome screen"
values={{ strong }}
/>
</p>
<p>
<FormattedMessage
defaultMessage={
Expand Down Expand Up @@ -112,7 +102,7 @@ export default function DirectionsNullState(props: Props) {
values={{
link: (chunks) => (
<a
href="https://github.com/bikehopper/bikehopper-ui"
href="https://github.com/bikehopper/"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer to keep this pointed at ui for now since its readme has an overview of BikeHopper and BikeHopper's stack

I'm aware it's been argued that that isn't the correct place for an overview to live. however, currently it's the only public overview we have.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will make a more suitable public overview repo/page before merging this PR

target="_blank"
rel="noreferrer"
>
Expand Down
4 changes: 4 additions & 0 deletions src/components/ItineraryElevationProfile.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.ItineraryElevation__chart {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per coding conventions please use Tailwind for new components

display: flex;
overflow: hidden;
}
Loading
Loading