Skip to content
Merged
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
34 changes: 23 additions & 11 deletions frontend/src/lib/components/MapListToggle.svelte
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
<script>
import { page } from '$app/stores';
import { useLibre311Context } from '$lib/context/Libre311Context';
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { MapOrList, type MapOrListToggle } from './map_or_list_toggle';

const linkResolver = useLibre311Context().linkResolver;
export let toggled: MapOrListToggle = MapOrList.Map;

const dispatch = createEventDispatcher<{
change: MapOrListToggle;
}>();

function set(value: MapOrListToggle) {
toggled = value;
dispatch('change', value);
}
</script>

<div class="toggle">
<a href={linkResolver.issuesMap($page.url)}>
<button class="rounded-l-lg" class:selected={$page.route.id == '/issues/map'}> Map </button>
</a>

<a href={linkResolver.issuesList($page.url)}>
<button class="rounded-r-lg" class:selected={$page.route.id == '/issues/list'}>List </button>
</a>
<button
class="rounded-l-lg"
class:selected={toggled === MapOrList.List}
on:click={() => set(MapOrList.List)}>List</button
>
<button
class="rounded-r-lg"
class:selected={toggled === MapOrList.Map}
on:click={() => set(MapOrList.Map)}>Map</button
>
</div>

<style>
Expand Down
48 changes: 48 additions & 0 deletions frontend/src/lib/components/RequestListMap.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script lang="ts">
export let listHidden = false;
export let mapHidden = false;

$: layoutContainerClass = listHidden ? 'map-only' : mapHidden ? 'list-only' : 'list-and-map';
</script>

<div class="top-level {layoutContainerClass}">
<div class="list-view h-full">
<slot name="list-slot" />
</div>
<div class="map-view">
<slot name="map-slot" />
</div>
</div>

<style>
.top-level {
height: 100%;
}

.list-and-map {
display: grid;
grid-template-columns: 400px auto;
}

.list-only {
display: grid;
grid-template-columns: auto;
}

.map-only {
display: grid;
grid-template-columns: auto;
}

.map-only .list-view {
display: none;
}

.list-only .map-view {
display: none;
}

.list-view {
overflow-y: auto;
}
</style>
44 changes: 0 additions & 44 deletions frontend/src/lib/components/SideBarMainContentLayout.svelte

This file was deleted.

6 changes: 6 additions & 0 deletions frontend/src/lib/components/map_or_list_toggle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const MapOrList = {
Map: 'Map',
List: 'List'
} as const;

export type MapOrListToggle = (typeof MapOrList)[keyof typeof MapOrList];
16 changes: 16 additions & 0 deletions frontend/src/lib/components/media.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// media.ts
import { readable } from 'svelte/store';

export function mediaQuery(query: string) {
return readable(false, (set) => {
if (typeof window === 'undefined') return;

const mql = window.matchMedia(query);
set(mql.matches);

const handler = (e: MediaQueryListEvent) => set(e.matches);
mql.addEventListener('change', handler);

return () => mql.removeEventListener('change', handler);
});
}
69 changes: 0 additions & 69 deletions frontend/src/routes/issues/list/+page.svelte
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was duplicate code

This file was deleted.

51 changes: 44 additions & 7 deletions frontend/src/routes/issues/map/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import SideBarMainContentLayout from '$lib/components/SideBarMainContentLayout.svelte';
import RequestListMap from '$lib/components/RequestListMap.svelte';
import {
useSelectedServiceRequestStore,
useServiceRequestsResponseStore
Expand All @@ -10,7 +10,6 @@
import MapComponent from '$lib/components/MapComponent.svelte';
import MapMarkerCircle from '$lib/components/MapMarkerCircle.svelte';
import MapMarkerWaypoint from '$lib/components/MapMarkerWaypoint.svelte';
import Breakpoint from '$lib/components/Breakpoint.svelte';
import MapListToggle from '$lib/components/MapListToggle.svelte';

// Type imports
Expand All @@ -28,11 +27,16 @@
import { mapStatusLegendControlFactory } from '$lib/components/MapStatusLegendControl';
import { KEYBOARD_PAN_DELTA_COARSE, SELECTION_ZOOM_LEVEL } from '$lib/constants/map';

import { MapOrList, type MapOrListToggle } from '$lib/components/map_or_list_toggle';

const linkResolver = useLibre311Context().linkResolver;
const libre311 = useLibre311Context().service;
const serviceRequestsResponseStore = useServiceRequestsResponseStore();
const selectedServiceRequestStore = useSelectedServiceRequestStore();

import Breakpoint from '$lib/components/Breakpoint.svelte';
import { mediaQuery } from '$lib/components/media';

$: mapBounds = createMapBounds($serviceRequestsResponseStore);

// Compute flyTo target when a service request is selected
Expand Down Expand Up @@ -76,14 +80,47 @@
goto(linkResolver.issueDetailsMobile($page.url, serviceRequest.service_request_id));
}
}

let listHidden = false;
let mapHidden = false;

export let toggleState: MapOrListToggle = MapOrList.Map;

function handleToggle(toggled: MapOrListToggle) {
toggleState = toggled;

listHidden = toggled === MapOrList.Map;
mapHidden = toggled === MapOrList.List;
}

const isNarrow = mediaQuery('(max-width: 768px)');

$: {
// callback-like behavior
if ($isNarrow) {
listHidden = true;
mapHidden = false;
} else {
listHidden = false;
mapHidden = false;
toggleState = MapOrList.Map;
}
}
</script>

<SideBarMainContentLayout>
<slot slot="side-bar" />
<div slot="main-content" class="relative flex h-full">
<RequestListMap {listHidden} {mapHidden}>
<div slot="list-slot">
<Breakpoint>
<div slot="is-mobile-or-tablet" class="my-4 flex justify-center">
<MapListToggle toggled={toggleState} on:change={(e) => handleToggle(e.detail)} />
</div>
</Breakpoint>
<slot />
</div>
<div slot="map-slot" class="relative flex h-full">
<Breakpoint>
<div slot="is-mobile-or-tablet" class="absolute left-1/2 top-5 z-[1] -translate-x-1/2">
<MapListToggle />
<MapListToggle toggled={toggleState} on:change={(e) => handleToggle(e.detail)} />
</div>
</Breakpoint>
<MapComponent
Expand All @@ -105,4 +142,4 @@
</MapComponent>
<CreateServiceRequestButton />
</div>
</SideBarMainContentLayout>
</RequestListMap>