Skip to content

Commit 4236ca2

Browse files
committed
Improve support for safe area and rotating on mobile
1 parent 90be4a6 commit 4236ca2

5 files changed

Lines changed: 71 additions & 39 deletions

File tree

client/public/index.html

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
<!DOCTYPE html>
22
<html lang="en">
33
<head>
4-
<meta charset="utf-8">
5-
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6-
<meta name="viewport" content="width=device-width,initial-scale=1.0">
7-
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
4+
<meta charset="utf-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width,initial-scale=1.0, viewport-fit=cover" />
7+
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
88
<title><%= htmlWebpackPlugin.options.title %></title>
99
</head>
1010
<body>
1111
<noscript>
12-
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
12+
<strong
13+
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without
14+
JavaScript enabled. Please enable it to continue.</strong
15+
>
1316
</noscript>
1417
<!-- built files will be auto injected -->
1518
</body>

client/src/App.vue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ const minimised = ref(false);
2323
2424
const mapStyle = ref(MapStyle.STRAVA);
2525
26-
const sidebarOverlaySize = ref<DOMRectReadOnly>();
27-
2826
function clearMapItems(): void {
2927
mapItems.value = [];
3028
}
@@ -56,7 +54,7 @@ defineExpose({ mapItems });
5654

5755
<template>
5856
<div id="app">
59-
<CollapsibleSidebar v-model:minimised="minimised" @sidebar-size="sidebarOverlaySize = $event">
57+
<CollapsibleSidebar v-model:minimised="minimised">
6058
<ActivitiesPanel
6159
v-model:map-style="mapStyle"
6260
v-model:selected="selected"
@@ -75,7 +73,6 @@ defineExpose({ mapItems });
7573
v-model:center="location"
7674
v-model:zoom="zoom"
7775
v-model:selected="selected"
78-
:sidebar-overlay-size="sidebarOverlaySize"
7976
:terrain="terrain"
8077
:map-items="mapItems"
8178
/>
@@ -111,6 +108,11 @@ a {
111108
--background-strong: #ccc;
112109
--transition-speed: 0.5s;
113110
--link-color: blue;
111+
112+
--left-safe-area: env(safe-area-inset-left);
113+
--right-safe-area: env(safe-area-inset-right);
114+
--top-safe-area: env(safe-area-inset-top);
115+
--bottom-safe-area: env(safe-area-inset-bottom);
114116
}
115117
116118
@media (prefers-color-scheme: dark) {

client/src/components/CollapsibleSidebar.vue

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { onBeforeUnmount, onMounted, ref } from 'vue';
2+
import { ref } from 'vue';
33
44
import { useModel } from '../utils/useModel';
55
import Icon from './Icon.vue';
@@ -13,22 +13,8 @@ const props = withDefaults(
1313
1414
const emit = defineEmits<{
1515
(e: 'update:minimised', value: boolean): void;
16-
(e: 'sidebar-size', value: DOMRectReadOnly | undefined): void;
1716
}>();
1817
19-
function emitPaddingDimensions() {
20-
emit('sidebar-size', minimisedOverlay.value?.getBoundingClientRect());
21-
}
22-
23-
onMounted(() => {
24-
emitPaddingDimensions();
25-
window.addEventListener('resize', emitPaddingDimensions, { passive: true });
26-
});
27-
28-
onBeforeUnmount(() => {
29-
window.removeEventListener('resize', emitPaddingDimensions);
30-
});
31-
3218
const minimisedModel = useModel('minimised', props)(emit);
3319
3420
const minimisedOverlay = ref<HTMLElement>();
@@ -71,7 +57,6 @@ const minimisedOverlay = ref<HTMLElement>();
7157
</template>
7258

7359
<style lang="scss">
74-
$max-sidebar-width: calc(100vw - 6rem);
7560
$sidebar-width: 20rem;
7661
$minimised-width: 0rem;
7762
$corner-radius: 1rem;
@@ -81,7 +66,7 @@ $tab-width: 5rem;
8166
$tab-height: 5rem;
8267
$logo-height: 5rem;
8368
$max-size-to-minimise: 600px;
84-
$padding-top: 0.5rem;
69+
$padding-top: calc(0.5rem + var(--top-safe-area));
8570
8671
@mixin pseudo-element {
8772
content: '';
@@ -92,14 +77,14 @@ $padding-top: 0.5rem;
9277
9378
.sidebar {
9479
flex: 0 $sidebar-width;
95-
max-width: $max-sidebar-width;
9680
display: flex;
9781
flex-direction: column;
9882
color: var(--color);
9983
background-color: var(--background);
10084
transition: margin var(--transition-speed);
10185
z-index: 1;
10286
position: relative;
87+
padding-left: var(--left-safe-area);
10388
10489
.header {
10590
margin-left: auto;
@@ -128,6 +113,7 @@ $padding-top: 0.5rem;
128113
overflow: auto;
129114
transition: margin var(--transition-speed);
130115
background-color: var(--background);
116+
padding-bottom: var(--bottom-safe-area);
131117
}
132118
133119
.top-box {
@@ -278,9 +264,10 @@ $padding-top: 0.5rem;
278264
279265
@media screen and (max-width: $max-size-to-minimise) {
280266
.sidebar {
281-
$sidebar-overlap-fallback: $minimised-width - $sidebar-width;
282-
$sidebar-overlap: calc(#{$minimised-width} - min(#{$sidebar-width}, #{$max-sidebar-width}));
283-
margin-right: $sidebar-overlap-fallback;
267+
$sidebar-overlap: $minimised-width - $sidebar-width;
268+
$sidebar-overlay-width: $minimised-width + $tab-width;
269+
$sidebar-overlay-height: calc(#{$padding-top} + #{$logo-height} + #{$tab-height});
270+
284271
margin-right: $sidebar-overlap;
285272
286273
.overlay {
@@ -295,17 +282,17 @@ $padding-top: 0.5rem;
295282
z-index: 2;
296283
left: 100%;
297284
width: 100vw;
285+
width: 100dvw;
298286
}
299287
300288
&.minimised {
301289
left: 100%;
302-
right: -$minimised-width - $tab-width;
303-
height: $padding-top + $logo-height + $tab-height;
290+
right: -$sidebar-overlay-width;
291+
height: $sidebar-overlay-height;
304292
}
305293
}
306294
307295
&.minimised {
308-
margin-left: $sidebar-overlap-fallback;
309296
margin-left: $sidebar-overlap;
310297
margin-right: 0;
311298
@@ -323,6 +310,11 @@ $padding-top: 0.5rem;
323310
.minimised.overlay {
324311
pointer-events: all;
325312
}
313+
314+
+ * {
315+
--sidebar-overlay-width: #{$sidebar-overlay-width};
316+
--sidebar-overlay-height: #{$sidebar-overlay-height};
317+
}
326318
}
327319
328320
.tabs {

client/src/components/MapView.vue

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { useHead } from '@unhead/vue';
1717
import type { GeoJSON } from 'geojson';
1818
import { LngLatBounds } from 'mapbox-gl';
1919
import type { VNode } from 'vue';
20-
import { computed, ref } from 'vue';
20+
import { computed, onBeforeUnmount, ref } from 'vue';
2121
import { nextTick, onMounted, watch } from 'vue';
2222
2323
import { MapStyle } from '../MapStyle';
@@ -161,12 +161,10 @@ const props = withDefaults(
161161
mapItems: MapItem[];
162162
terrain?: boolean;
163163
mapStyle: MapStyle;
164-
sidebarOverlaySize?: DOMRectReadOnly;
165164
}>(),
166165
{
167166
selected: () => [],
168167
terrain: false,
169-
sidebarOverlaySize: undefined,
170168
},
171169
);
172170
@@ -235,7 +233,7 @@ function optimiseViewport(map: mapboxgl.Map, bounds: LngLatBounds) {
235233
236234
const aspectRatio = (northWest.y - southEast.y) / (northWest.x - southEast.x);
237235
238-
const topLeft = props.sidebarOverlaySize;
236+
const topLeft = container.value?.querySelector('.mapboxgl-ctrl-top-left');
239237
const topRight = container.value?.querySelector('.mapboxgl-ctrl-top-right');
240238
const bottomLeft = container.value?.querySelector('.mapboxgl-ctrl-bottom-left');
241239
const bottomRight = container.value?.querySelector('.mapboxgl-ctrl-bottom-right');
@@ -246,7 +244,7 @@ function optimiseViewport(map: mapboxgl.Map, bounds: LngLatBounds) {
246244
new Viewport(width, height, { left: padding, top: padding, bottom: padding, right: padding }),
247245
]
248246
.flatMap((viewport) =>
249-
[{ top: topLeft?.height }, { left: topLeft?.width ?? 0 }].map((offset) =>
247+
[{ top: topLeft?.clientHeight }, { left: topLeft?.clientWidth ?? 0 }].map((offset) =>
250248
viewport.withOffset(offset),
251249
),
252250
)
@@ -315,6 +313,16 @@ function zoomToSelection(): void {
315313
flyTo(selectedMapItems.value, true);
316314
}
317315
316+
const resizeHandler = () => map.value?.resize();
317+
318+
onMounted(() => {
319+
window.addEventListener('transitionend', resizeHandler, { passive: true });
320+
});
321+
322+
onBeforeUnmount(() => {
323+
window.removeEventListener('transitionend', resizeHandler);
324+
});
325+
318326
function applyMapItems(next: MapItem[], sourceID: string): void {
319327
const source = map.value?.getSource(sourceID);
320328
(source as mapboxgl.GeoJSONSource)?.setData(makeGeoJsonData(next));
@@ -430,5 +438,32 @@ defineExpose({ zoomToSelection });
430438
.mapboxgl-canvas {
431439
cursor: pointer;
432440
outline: none;
441+
left: 0;
442+
right: 0;
443+
margin-left: 50%;
444+
transform: translateX(-50%);
445+
}
446+
447+
.mapboxgl-ctrl-top-right {
448+
padding-top: var(--top-safe-area);
449+
}
450+
451+
.mapboxgl-ctrl-top-right {
452+
padding-top: var(--top-safe-area);
453+
}
454+
455+
.mapboxgl-ctrl-top-left {
456+
padding-top: max(var(--top-safe-area), var(--sidebar-overlay-height, 0));
457+
padding-left: var(--sidebar-overlay-width, 0);
458+
}
459+
460+
.mapboxgl-ctrl-bottom-left,
461+
.mapboxgl-ctrl-bottom-right {
462+
padding-bottom: var(--bottom-safe-area);
463+
}
464+
465+
.mapboxgl-ctrl-top-right,
466+
.mapboxgl-ctrl-bottom-right {
467+
padding-right: var(--right-safe-area);
433468
}
434469
</style>

client/vue.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ module.exports = {
4848
},
4949
},
5050
outputDir: '../dist/client',
51-
productionSourceMap: false,
51+
productionSourceMap: true,
5252
};

0 commit comments

Comments
 (0)