forked from MapComplete/MapComplete
UX+Refactoring: use side-drawer for menu, reorder menu structure
This commit is contained in:
parent
8465b59c7f
commit
124e816abe
25 changed files with 645 additions and 1059 deletions
|
@ -13,65 +13,40 @@
|
|||
import type { MapProperties } from "../Models/MapProperties"
|
||||
import Geosearch from "./BigComponents/Geosearch.svelte"
|
||||
import Translations from "./i18n/Translations"
|
||||
import usersettings from "../assets/generated/layers/usersettings.json"
|
||||
import {
|
||||
CogIcon,
|
||||
EyeIcon,
|
||||
HeartIcon,
|
||||
MenuIcon,
|
||||
XCircleIcon,
|
||||
MenuIcon
|
||||
} from "@rgossiaux/svelte-heroicons/solid"
|
||||
import Tr from "./Base/Tr.svelte"
|
||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
|
||||
import FloatOver from "./Base/FloatOver.svelte"
|
||||
import Constants from "../Models/Constants"
|
||||
import TabbedGroup from "./Base/TabbedGroup.svelte"
|
||||
import UserRelatedState from "../Logic/State/UserRelatedState"
|
||||
import LoginToggle from "./Base/LoginToggle.svelte"
|
||||
import LoginButton from "./Base/LoginButton.svelte"
|
||||
import CopyrightPanel from "./BigComponents/CopyrightPanel.svelte"
|
||||
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
|
||||
import ModalRight from "./Base/ModalRight.svelte"
|
||||
import LevelSelector from "./BigComponents/LevelSelector.svelte"
|
||||
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
|
||||
import type { RasterLayerPolygon } from "../Models/RasterLayers"
|
||||
import { AvailableRasterLayers } from "../Models/RasterLayers"
|
||||
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
|
||||
import IfHidden from "./Base/IfHidden.svelte"
|
||||
import { onDestroy } from "svelte"
|
||||
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
|
||||
import StateIndicator from "./BigComponents/StateIndicator.svelte"
|
||||
import ShareScreen from "./BigComponents/ShareScreen.svelte"
|
||||
import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"
|
||||
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
|
||||
import Cross from "../assets/svg/Cross.svelte"
|
||||
import LanguagePicker from "./InputElement/LanguagePicker.svelte"
|
||||
import Min from "../assets/svg/Min.svelte"
|
||||
import Plus from "../assets/svg/Plus.svelte"
|
||||
import Filter from "../assets/svg/Filter.svelte"
|
||||
import Community from "../assets/svg/Community.svelte"
|
||||
import Favourites from "./Favourites/Favourites.svelte"
|
||||
import ImageOperations from "./Image/ImageOperations.svelte"
|
||||
import VisualFeedbackPanel from "./BigComponents/VisualFeedbackPanel.svelte"
|
||||
import { Orientation } from "../Sensors/Orientation"
|
||||
import GeolocationIndicator from "./BigComponents/GeolocationIndicator.svelte"
|
||||
import Compass_arrow from "../assets/svg/Compass_arrow.svelte"
|
||||
import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte"
|
||||
import FilterPanel from "./BigComponents/FilterPanel.svelte"
|
||||
import PrivacyPolicy from "./BigComponents/PrivacyPolicy.svelte"
|
||||
import { BBox } from "../Logic/BBox"
|
||||
import ReviewsOverview from "./Reviews/ReviewsOverview.svelte"
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton.svelte"
|
||||
import CloseAnimation from "./Base/CloseAnimation.svelte"
|
||||
import { LastClickFeatureSource } from "../Logic/FeatureSource/Sources/LastClickFeatureSource"
|
||||
import ArrowDownTray from "@babeard/svelte-heroicons/mini/ArrowDownTray"
|
||||
import Share from "@babeard/svelte-heroicons/solid/Share"
|
||||
import ChevronRight from "@babeard/svelte-heroicons/solid/ChevronRight"
|
||||
import Marker from "./Map/Marker.svelte"
|
||||
import AboutMapComplete from "./BigComponents/AboutMapComplete.svelte"
|
||||
import HotkeyTable from "./BigComponents/HotkeyTable.svelte"
|
||||
import SelectedElementPanel from "./Base/SelectedElementPanel.svelte"
|
||||
import type { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson"
|
||||
import MenuDrawer from "./BigComponents/MenuDrawer.svelte"
|
||||
import DrawerLeft from "./Base/DrawerLeft.svelte"
|
||||
|
||||
export let state: ThemeViewState
|
||||
let layout = state.layout
|
||||
|
@ -120,7 +95,6 @@
|
|||
let visualFeedback = state.visualFeedback
|
||||
let viewport: UIEventSource<HTMLDivElement> = new UIEventSource<HTMLDivElement>(undefined)
|
||||
let mapproperties: MapProperties = state.mapProperties
|
||||
let usersettingslayer = new LayerConfig(<LayerConfigJson>usersettings, "usersettings", true)
|
||||
state.mapProperties.installCustomKeyboardHandler(viewport)
|
||||
let canZoomIn = mapproperties.maxzoom.map(
|
||||
(mz) => mapproperties.zoom.data < mz,
|
||||
|
@ -144,7 +118,7 @@
|
|||
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
|
||||
const bbox = new BBox([
|
||||
[topLeft.lng, topLeft.lat],
|
||||
[bottomRight.lng, bottomRight.lat],
|
||||
[bottomRight.lng, bottomRight.lat]
|
||||
])
|
||||
state.visualFeedbackViewportBounds.setData(bbox)
|
||||
}
|
||||
|
@ -156,7 +130,6 @@
|
|||
updateViewport()
|
||||
})
|
||||
let featureSwitches: FeatureSwitchState = state.featureSwitches
|
||||
let availableLayers = state.availableLayers
|
||||
let currentViewLayer: LayerConfig = layout.layers.find((l) => l.id === "current_view")
|
||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
|
||||
let rasterLayerName =
|
||||
|
@ -168,8 +141,13 @@
|
|||
})
|
||||
)
|
||||
let previewedImage = state.previewedImage
|
||||
|
||||
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
|
||||
let gpsAvailable = state.geolocation.geolocationState.gpsAvailable
|
||||
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
|
||||
let debug = state.featureSwitches.featureSwitchIsDebugging
|
||||
|
||||
|
||||
|
||||
debug.addCallbackAndRun((dbg) => {
|
||||
if (dbg) {
|
||||
document.body.classList.add("debug")
|
||||
|
@ -190,26 +168,7 @@
|
|||
animation?.cameraAnimation(mlmap)
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed for the animations
|
||||
*/
|
||||
let openMapButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
|
||||
let openMenuButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
|
||||
let openCurrentViewLayerButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(
|
||||
undefined
|
||||
)
|
||||
let _openNewElementButton: HTMLButtonElement
|
||||
let openNewElementButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
|
||||
|
||||
$: {
|
||||
openNewElementButton.setData(_openNewElementButton)
|
||||
}
|
||||
let openFilterButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
|
||||
let openBackgroundButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
|
||||
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
|
||||
|
||||
let gpsAvailable = state.geolocation.geolocationState.gpsAvailable
|
||||
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
|
||||
</script>
|
||||
|
||||
<main>
|
||||
|
@ -255,9 +214,8 @@
|
|||
<div class="float-left m-1 flex flex-col sm:mt-2">
|
||||
<If condition={state.featureSwitches.featureSwitchWelcomeMessage}>
|
||||
<MapControlButton
|
||||
on:click={() => state.guistate.themeIsOpened.setData(true)}
|
||||
on:click={() => state.guistate.pageStates.about_theme.set(true)}
|
||||
on:keydown={forwardEventToMap}
|
||||
htmlElem={openMapButton}
|
||||
>
|
||||
<div
|
||||
class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2"
|
||||
|
@ -272,9 +230,8 @@
|
|||
|
||||
<MapControlButton
|
||||
arialabel={Translations.t.general.labels.menu}
|
||||
on:click={() => state.guistate.menuIsOpened.setData(true)}
|
||||
on:click={() => {console.log("Opening...."); state.guistate.menuIsOpened.setData(true)}}
|
||||
on:keydown={forwardEventToMap}
|
||||
htmlElem={openMenuButton}
|
||||
>
|
||||
<MenuIcon class="h-8 w-8 cursor-pointer" />
|
||||
</MapControlButton>
|
||||
|
@ -285,7 +242,6 @@
|
|||
state.selectCurrentView()
|
||||
}}
|
||||
on:keydown={forwardEventToMap}
|
||||
htmlElem={openCurrentViewLayerButton}
|
||||
>
|
||||
<div class="h-8 w-8 cursor-pointer">
|
||||
<ToSvelte construct={() => currentViewLayer.defaultIcon()} />
|
||||
|
@ -322,7 +278,6 @@
|
|||
<button
|
||||
class="low-interaction pointer-events-auto w-fit"
|
||||
class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint}
|
||||
bind:this={_openNewElementButton}
|
||||
on:click={() => {
|
||||
state.openNewDialog()
|
||||
}}
|
||||
|
@ -346,7 +301,6 @@
|
|||
arialabel={Translations.t.general.labels.filter}
|
||||
on:click={() => state.guistate.openFilterView()}
|
||||
on:keydown={forwardEventToMap}
|
||||
htmlElem={openFilterButton}
|
||||
>
|
||||
<Filter class="h-6 w-6" />
|
||||
</MapControlButton>
|
||||
|
@ -355,25 +309,17 @@
|
|||
<OpenBackgroundSelectorButton
|
||||
hideTooltip={true}
|
||||
{state}
|
||||
htmlElem={openBackgroundButton}
|
||||
/>
|
||||
</If>
|
||||
<a
|
||||
class="bg-black-transparent pointer-events-auto ml-1 h-fit max-h-12 cursor-pointer self-end self-center overflow-hidden rounded-2xl px-1 text-white opacity-50 hover:opacity-100"
|
||||
on:click={() => {
|
||||
if (featureSwitches.featureSwitchWelcomeMessage.data) {
|
||||
state.guistate.themeViewTab.setData("copyright")
|
||||
state.guistate.themeIsOpened.setData(true)
|
||||
} else {
|
||||
state.guistate.copyrightPanelIsOpened.setData(true)
|
||||
}
|
||||
}}
|
||||
<button
|
||||
class="as-link bg-black-transparent pointer-events-auto ml-1 h-fit max-h-12 cursor-pointer self-end self-center overflow-hidden rounded-2xl px-1 text-white opacity-50 hover:opacity-100"
|
||||
on:click={() => {state.guistate.pageStates.copyright.set(true)}}
|
||||
>
|
||||
© <span class="hidden sm:inline sm:pr-2">
|
||||
OpenStreetMap
|
||||
<span class="hidden w-24 md:inline md:pr-2">, {rasterLayerName}</span>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -443,6 +389,11 @@
|
|||
<svelte:fragment slot="error" />
|
||||
</LoginToggle>
|
||||
|
||||
<DrawerLeft shown={state.guistate.menuIsOpened}>
|
||||
<MenuDrawer onlyLink={true} {state} />
|
||||
</DrawerLeft>
|
||||
<MenuDrawer onlyLink={false} {state} />
|
||||
|
||||
{#if $selectedElement !== undefined && $selectedLayer !== undefined && !$selectedLayer.popupInFloatover}
|
||||
<!-- right modal with the selected element view -->
|
||||
<ModalRight
|
||||
|
@ -485,225 +436,5 @@
|
|||
</FloatOver>
|
||||
</If>
|
||||
|
||||
<!-- big theme menu -->
|
||||
<If condition={state.guistate.themeIsOpened}>
|
||||
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
|
||||
<span slot="close-button"><!-- Disable the close button --></span>
|
||||
<TabbedGroup
|
||||
condition1={state.featureSwitches.featureSwitchEnableExport}
|
||||
tab={state.guistate.themeViewTabIndex}
|
||||
>
|
||||
<div slot="post-tablist">
|
||||
<XCircleIcon
|
||||
class="mr-2 h-8 w-8"
|
||||
on:click={() => state.guistate.themeIsOpened.setData(false)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex" slot="title0">
|
||||
<Marker icons={layout.icon} size="h-4 w-4" />
|
||||
<Tr t={layout.title} />
|
||||
</div>
|
||||
|
||||
<div class="m-4 h-full" slot="content0">
|
||||
<ThemeIntroPanel {state} />
|
||||
</div>
|
||||
|
||||
<div class="flex" slot="title1">
|
||||
<If condition={state.featureSwitches.featureSwitchEnableExport}>
|
||||
<ArrowDownTray class="h-4 w-4" />
|
||||
<Tr t={Translations.t.general.download.title} />
|
||||
</If>
|
||||
</div>
|
||||
<div class="m-4" slot="content1">
|
||||
<DownloadPanel {state} />
|
||||
</div>
|
||||
|
||||
<div slot="title2">
|
||||
<Tr t={Translations.t.general.attribution.title} />
|
||||
</div>
|
||||
|
||||
<div slot="content2" class="m-2 flex flex-col">
|
||||
<CopyrightPanel {state} />
|
||||
</div>
|
||||
|
||||
<div class="flex" slot="title3">
|
||||
<Share class="h-4 w-4" />
|
||||
<Tr t={Translations.t.general.sharescreen.title} />
|
||||
</div>
|
||||
<div class="m-2" slot="content3">
|
||||
<ShareScreen {state} />
|
||||
</div>
|
||||
</TabbedGroup>
|
||||
</FloatOver>
|
||||
</If>
|
||||
|
||||
<!-- Filterpane -->
|
||||
<If condition={state.guistate.filtersPanelIsOpened}>
|
||||
<FloatOver on:close={() => state.guistate.filtersPanelIsOpened.setData(false)}>
|
||||
<FilterPanel {state} />
|
||||
</FloatOver>
|
||||
</If>
|
||||
|
||||
<!-- background layer selector -->
|
||||
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
|
||||
<FloatOver
|
||||
on:close={() => {
|
||||
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
|
||||
}}
|
||||
>
|
||||
<RasterLayerOverview
|
||||
{availableLayers}
|
||||
map={state.map}
|
||||
mapproperties={state.mapProperties}
|
||||
userstate={state.userRelatedState}
|
||||
visible={state.guistate.backgroundLayerSelectionIsOpened}
|
||||
/>
|
||||
</FloatOver>
|
||||
</IfHidden>
|
||||
|
||||
<!-- Menu page -->
|
||||
<If condition={state.guistate.menuIsOpened}>
|
||||
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
|
||||
<span slot="close-button"><!-- Hide the default close button --></span>
|
||||
<TabbedGroup
|
||||
condition1={featureSwitches.featureSwitchEnableLogin}
|
||||
condition2={state.featureSwitches.featureSwitchCommunityIndex}
|
||||
tab={state.guistate.menuViewTabIndex}
|
||||
>
|
||||
<div slot="post-tablist">
|
||||
<XCircleIcon
|
||||
class="mr-2 h-8 w-8"
|
||||
on:click={() => state.guistate.menuIsOpened.setData(false)}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex" slot="title0">
|
||||
<Tr t={Translations.t.general.menu.aboutMapComplete} />
|
||||
</div>
|
||||
|
||||
<div slot="content0" class="flex flex-col">
|
||||
<AboutMapComplete {state} />
|
||||
<div class="m-2 flex flex-col">
|
||||
<HotkeyTable />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex" slot="title1">
|
||||
<CogIcon class="h-6 w-6" />
|
||||
<Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} />
|
||||
</div>
|
||||
|
||||
<div class="links-as-button py-8" slot="content1">
|
||||
<!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it -->
|
||||
<LoginToggle {state}>
|
||||
<div class="flex flex-col" slot="not-logged-in">
|
||||
<LanguagePicker availableLanguages={layout.language} />
|
||||
<Tr cls="alert" t={Translations.t.userinfo.notLoggedIn} />
|
||||
<LoginButton clss="primary" osmConnection={state.osmConnection} />
|
||||
</div>
|
||||
<SelectedElementView
|
||||
highlightedRendering={state.guistate.highlightedUserSetting}
|
||||
layer={usersettingslayer}
|
||||
selectedElement={{
|
||||
type: "Feature",
|
||||
properties: { id: "settings" },
|
||||
geometry: { type: "Point", coordinates: [0, 0] },
|
||||
}}
|
||||
{state}
|
||||
tags={state.userRelatedState.preferencesAsTags}
|
||||
/>
|
||||
</LoginToggle>
|
||||
</div>
|
||||
|
||||
<div class="flex" slot="title2">
|
||||
<HeartIcon class="h-6 w-6" />
|
||||
<Tr t={Translations.t.favouritePoi.tab} />
|
||||
</div>
|
||||
|
||||
<div class="m-2 flex flex-col" slot="content2">
|
||||
<h3>
|
||||
<Tr t={Translations.t.favouritePoi.title} />
|
||||
</h3>
|
||||
<Favourites {state} />
|
||||
<h3>
|
||||
<Tr t={Translations.t.reviews.your_reviews} />
|
||||
</h3>
|
||||
<ReviewsOverview {state} />
|
||||
</div>
|
||||
</TabbedGroup>
|
||||
</FloatOver>
|
||||
</If>
|
||||
|
||||
<!-- Privacy policy -->
|
||||
<If condition={state.guistate.privacyPanelIsOpened}>
|
||||
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
|
||||
<div class="flex h-full flex-col overflow-hidden">
|
||||
<h2 class="low-interaction m-0 flex items-center p-4 drop-shadow-md">
|
||||
<EyeIcon class="w-6 pr-2" />
|
||||
<Tr t={Translations.t.privacy.title} />
|
||||
</h2>
|
||||
<div class="overflow-auto p-4">
|
||||
<PrivacyPolicy {state} />
|
||||
</div>
|
||||
</div>
|
||||
</FloatOver>
|
||||
</If>
|
||||
|
||||
<!-- Attribution, copyright and about MapComplete (no menu case) -->
|
||||
<If condition={state.guistate.copyrightPanelIsOpened}>
|
||||
<FloatOver on:close={() => state.guistate.copyrightPanelIsOpened.setData(false)}>
|
||||
<div class="flex h-full flex-col overflow-hidden">
|
||||
<h1 class="low-interaction m-0 flex items-center p-4 drop-shadow-md">
|
||||
<Tr t={Translations.t.general.attribution.title} />
|
||||
</h1>
|
||||
<div class="overflow-auto p-4">
|
||||
<h2>
|
||||
<Tr t={Translations.t.general.menu.aboutMapComplete} />
|
||||
</h2>
|
||||
<AboutMapComplete {state} />
|
||||
<CopyrightPanel {state} />
|
||||
</div>
|
||||
</div>
|
||||
</FloatOver>
|
||||
</If>
|
||||
|
||||
<!-- Community index -->
|
||||
<If condition={state.guistate.communityIndexPanelIsOpened}>
|
||||
<FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}>
|
||||
<div class="flex h-full flex-col overflow-hidden">
|
||||
<h2 class="low-interaction m-0 flex items-center p-4">
|
||||
<Community class="h-6 w-6" />
|
||||
<Tr t={Translations.t.communityIndex.title} />
|
||||
</h2>
|
||||
<div class="overflow-auto p-4">
|
||||
<CommunityIndexView location={state.mapProperties.location} />
|
||||
</div>
|
||||
</div>
|
||||
</FloatOver>
|
||||
</If>
|
||||
|
||||
<CloseAnimation isOpened={state.guistate.themeIsOpened} moveTo={openMapButton} debug="theme" />
|
||||
<CloseAnimation isOpened={state.guistate.menuIsOpened} moveTo={openMenuButton} debug="menu" />
|
||||
<CloseAnimation
|
||||
isOpened={selectedLayer.map((sl) => sl !== undefined && sl === currentViewLayer)}
|
||||
moveTo={openCurrentViewLayerButton}
|
||||
debug="currentViewLayer"
|
||||
/>
|
||||
<CloseAnimation
|
||||
isOpened={selectedElement.map(
|
||||
(sl) => sl !== undefined && sl?.properties?.id === LastClickFeatureSource.newPointElementId
|
||||
)}
|
||||
moveTo={openNewElementButton}
|
||||
debug="newElement"
|
||||
/>
|
||||
<CloseAnimation
|
||||
isOpened={state.guistate.filtersPanelIsOpened}
|
||||
moveTo={openFilterButton}
|
||||
debug="filter"
|
||||
/>
|
||||
<CloseAnimation
|
||||
isOpened={state.guistate.backgroundLayerSelectionIsOpened}
|
||||
moveTo={openBackgroundButton}
|
||||
debug="bg"
|
||||
/>
|
||||
</main>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue