Android: add inset spacers for android

This commit is contained in:
Pieter Vander Vennet 2025-07-18 14:15:37 +02:00
parent 381ac4a2f2
commit 877bdfae95
19 changed files with 84 additions and 47 deletions

View file

@ -3075,6 +3075,10 @@ input[type="range"].range-lg::-moz-range-thumb {
border-width: 2px; border-width: 2px;
} }
.border-4 {
border-width: 4px;
}
.border-\[10px\] { .border-\[10px\] {
border-width: 10px; border-width: 10px;
} }
@ -5527,7 +5531,7 @@ h2.group {
.warning { .warning {
/* The class to convey important information, but not as grave as 'alert' */ /* The class to convey important information, but not as grave as 'alert' */
background-color: var(--low-interaction-background); background-color: var(--alert-color);
color: var(--alert-foreground-color); color: var(--alert-foreground-color);
font-weight: bold; font-weight: bold;
border-radius: 1em; border-radius: 1em;

View file

@ -36,7 +36,7 @@ export default class SearchState {
constructor(state: ThemeViewState) { constructor(state: ThemeViewState) {
this.state = state this.state = state
this.showSearchDrawer = state.guistate.pageStates.search
this.searchTerm = QueryParameters.GetQueryParameter("q", "", "The term in the search field") this.searchTerm = QueryParameters.GetQueryParameter("q", "", "The term in the search field")
this.locationSearchers = [ this.locationSearchers = [
@ -112,8 +112,6 @@ export default class SearchState {
) )
this.locationResults = new GeocodingFeatureSource(this.suggestions.stabilized(250)) this.locationResults = new GeocodingFeatureSource(this.suggestions.stabilized(250))
this.showSearchDrawer = new UIEventSource(false)
this.searchIsFocused.addCallbackAndRunD((sugg) => { this.searchIsFocused.addCallbackAndRunD((sugg) => {
if (sugg) { if (sugg) {
this.showSearchDrawer.set(true) this.showSearchDrawer.set(true)

View file

@ -113,6 +113,9 @@ export class AndroidPolyfill {
DatabridgePluginSingleton.request<{ top: number, bottom: number }>({ DatabridgePluginSingleton.request<{ top: number, bottom: number }>({
key: "insets", key: "insets",
}).then((result) => { }).then((result) => {
if(!result){
return
}
let v = result.value let v = result.value
if(typeof v === "string"){ if(typeof v === "string"){
v = JSON.parse(v) v = JSON.parse(v)

View file

@ -16,20 +16,21 @@ export type PageType = (typeof MenuState.pageNames)[number]
*/ */
export class MenuState { export class MenuState {
public static readonly pageNames = [ public static readonly pageNames = [
"about_theme",
"background",
"copyright", "copyright",
"copyright_icons", "copyright_icons",
"community_index", "community_index",
"hotkeys",
"privacy",
"filter",
"background",
"about_theme",
"download", "download",
"favourites",
"failedImages", "failedImages",
"usersettings", "favourites",
"share", "filter",
"hotkeys",
"menu", "menu",
"privacy",
"search",
"share",
"usersettings",
] as const ] as const
/** /**
@ -172,6 +173,7 @@ export class MenuState {
this._selectedElement.setData(undefined) this._selectedElement.setData(undefined)
return true return true
} }
} finally { } finally {
this.isClosingAll = false this.isClosingAll = false
} }

View file

@ -3,6 +3,8 @@
import { sineIn } from "svelte/easing" import { sineIn } from "svelte/easing"
import { Store } from "../../Logic/UIEventSource.js" import { Store } from "../../Logic/UIEventSource.js"
import { onMount } from "svelte" import { onMount } from "svelte"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
import InsetSpacer from "./InsetSpacer.svelte"
export let shown: Store<boolean> export let shown: Store<boolean>
let transitionParams = { let transitionParams = {
@ -16,11 +18,17 @@
hidden = !sh hidden = !sh
}) })
// Does not need a 'top-inset-spacer' as the code below will apply the padding automatically
let height = 0 let height = 0
onMount(() => {
function setHeight(){
let topbar = document.getElementById("top-bar") let topbar = document.getElementById("top-bar")
height = topbar?.clientHeight ?? 0 height = (topbar?.clientHeight ?? 0) + AndroidPolyfill.getInsetSizes().top.data
}) }
onMount(() => setHeight())
AndroidPolyfill.getInsetSizes().top.addCallback(() => setHeight())
</script> </script>
<Drawer <Drawer
@ -35,11 +43,15 @@
rightOffset="inset-y-0 right-0" rightOffset="inset-y-0 right-0"
bind:hidden bind:hidden
> >
<div class="flex flex-col h-full">
<div class="low-interaction h-screen"> <div class="low-interaction h-screen">
<div class="h-full" style={`padding-top: ${height}px`}> <div style={`padding-top: ${height}px`}>
<div class="flex h-full flex-col overflow-y-auto"> <div class="flex h-full flex-col overflow-y-auto">
<slot /> <slot />
</div> </div>
</div> </div>
</div> </div>
<InsetSpacer clss="low-interaction" height={AndroidPolyfill.getInsetSizes().bottom}/>
</div>
</Drawer> </Drawer>

View file

@ -1,36 +1,37 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { CloseButton } from "flowbite-svelte" import { CloseButton } from "flowbite-svelte"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
/** /**
* The slotted element will be shown on top, with a lower-opacity border * The slotted element will be shown on top, with a lower-opacity border
*/ */
const dispatch = createEventDispatcher<{ close }>() const dispatch = createEventDispatcher<{ close }>()
const top = AndroidPolyfill.getInsetSizes().top
const bottom = AndroidPolyfill.getInsetSizes().bottom
</script> </script>
<!-- Draw the background over the total screen --> <!-- Draw the background over the total screen -->
<div <div
class="absolute left-0 top-0 h-screen w-screen" class="absolute left-0 top-0 h-screen w-screen"
style={`background-color: #00000088; z-index: 20; padding-top: calc( max( 1rem, ${$top} ));padding-bottom: calc( max( 1rem, ${$bottom} )); `}
on:click={() => { on:click={() => {
console.log("OnClose") console.log("OnClose")
dispatch("close") dispatch("close")
}} }}
style="background-color: #00000088; z-index: 20"
/> />
<!-- draw a _second_ absolute div, placed using 'bottom' which will be above the navigation bar on mobile browsers --> <!-- draw a _second_ absolute div, placed using 'bottom' which will be above the navigation bar on mobile browsers -->
<div <div
class="pointer-events-none absolute bottom-0 right-0 h-full w-screen p-4 md:p-6" class="pointer-events-none absolute bottom-0 right-0 h-full w-screen p-4 md:p-6"
style="z-index: 21" style="z-index: 21"
on:click={() => { on:click={() => { dispatch("close") }}
console.log("Closing...")
dispatch("close")
}}
> >
<div <div
class="content normal-background pointer-events-auto relative h-full" class="content normal-background pointer-events-auto relative h-full"
on:click|stopPropagation={() => {}} on:click|stopPropagation={() => {}}
> >
<div class="h-full rounded-xl"> <div class="h-full">
<!-- THe main content-->
<slot /> <slot />
</div> </div>
<slot name="close-button"> <slot name="close-button">

View file

@ -1,8 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { Store } from "../../Logic/UIEventSource"
/** /**
* in pixels * in pixels
*/ */
export let height: Store<number> export let height: Store<number>
export let clss: string = ""
</script> </script>
<div style={"height: "+$height+"px; background: red;"} /> <div class={clss+" shrink-0"} style={"height: "+$height+"px"} />

View file

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { Modal } from "flowbite-svelte" import { Modal } from "flowbite-svelte"
import { UIEventSource } from "../../Logic/UIEventSource" import { UIEventSource } from "../../Logic/UIEventSource"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
/** /**
* Basically a flowbite-svelte modal made more ergonomical * Basically a flowbite-svelte modal made more ergonomical
@ -8,7 +9,7 @@
export let fullscreen: boolean = false export let fullscreen: boolean = false
export let bodyPadding = "p-4 md:p-5 " export let bodyPadding = "p-4 md:p-5 "
export let shown: UIEventSource<boolean> export let shown: UIEventSource<boolean> = new UIEventSource(false)
export let dismissable = true export let dismissable = true
/** /**
* Default: 50 * Default: 50
@ -17,7 +18,7 @@
const shared = const shared =
"in-page normal-background dark:bg-gray-800 rounded-lg border-gray-200 dark:border-gray-700 border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md" "in-page normal-background dark:bg-gray-800 rounded-lg border-gray-200 dark:border-gray-700 border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md"
let defaultClass = "relative flex flex-col mx-auto w-full divide-y " + shared let defaultClass = "relative flex flex-col mx-auto w-full divide-y border-4 border-red-500 " + shared
if (fullscreen) { if (fullscreen) {
defaultClass = shared defaultClass = shared
} }
@ -40,6 +41,9 @@
shown.addCallbackAndRun((sh) => { shown.addCallbackAndRun((sh) => {
_shown = sh _shown = sh
}) })
let marginTop = AndroidPolyfill.getInsetSizes().top
let marginBottom = AndroidPolyfill.getInsetSizes().bottom
</script> </script>
<Modal <Modal
@ -54,6 +58,7 @@
{headerClass} {headerClass}
{backdropClass} {backdropClass}
color="none" color="none"
style={`margin-top: ${$marginTop}px; margin-bottom: ${$marginBottom}px`}
> >
<svelte:fragment slot="header"> <svelte:fragment slot="header">
{#if $$slots.header} {#if $$slots.header}

View file

@ -5,6 +5,8 @@
import SelectedElementTitle from "../BigComponents/SelectedElementTitle.svelte" import SelectedElementTitle from "../BigComponents/SelectedElementTitle.svelte"
import Loading from "./Loading.svelte" import Loading from "./Loading.svelte"
import { onDestroy } from "svelte" import { onDestroy } from "svelte"
import InsetSpacer from "./InsetSpacer.svelte"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
export let selected: Feature export let selected: Feature
@ -39,6 +41,7 @@
<Loading /> <Loading />
{:else} {:else}
<div class="normal-background flex h-full w-full flex-col" class:absolute> <div class="normal-background flex h-full w-full flex-col" class:absolute>
<InsetSpacer clss="low-interaction" height={AndroidPolyfill.getInsetSizes().top} />
<SelectedElementTitle {state} {layer} selectedElement={selected} /> <SelectedElementTitle {state} {layer} selectedElement={selected} />
<SelectedElementView {state} {layer} selectedElement={selected} /> <SelectedElementView {state} {layer} selectedElement={selected} />
</div> </div>

View file

@ -10,7 +10,6 @@
import OpenIdEditor from "./OpenIdEditor.svelte" import OpenIdEditor from "./OpenIdEditor.svelte"
import OpenJosm from "../Base/OpenJosm.svelte" import OpenJosm from "../Base/OpenJosm.svelte"
import MapillaryLink from "./MapillaryLink.svelte" import MapillaryLink from "./MapillaryLink.svelte"
import UserRelatedState from "../../Logic/State/UserRelatedState"
import ArrowDownTray from "@babeard/svelte-heroicons/mini/ArrowDownTray" import ArrowDownTray from "@babeard/svelte-heroicons/mini/ArrowDownTray"
import DownloadPanel from "../DownloadFlow/DownloadPanel.svelte" import DownloadPanel from "../DownloadFlow/DownloadPanel.svelte"
import Share from "@babeard/svelte-heroicons/solid/Share" import Share from "@babeard/svelte-heroicons/solid/Share"
@ -25,20 +24,12 @@
import { UIEventSource } from "../../Logic/UIEventSource" import { UIEventSource } from "../../Logic/UIEventSource"
import ChartBar from "@babeard/svelte-heroicons/solid/ChartBar" import ChartBar from "@babeard/svelte-heroicons/solid/ChartBar"
import QueueList from "@babeard/svelte-heroicons/solid/QueueList" import QueueList from "@babeard/svelte-heroicons/solid/QueueList"
import { MenuState } from "../../Models/MenuState"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import FeatureSwitchState from "../../Logic/State/FeatureSwitchState"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import type { MapProperties } from "../../Models/MapProperties"
import FavouritesFeatureSource from "../../Logic/FeatureSource/Sources/FavouritesFeatureSource"
import Hotkeys from "../Base/Hotkeys" import Hotkeys from "../Base/Hotkeys"
import MenuDrawerIndex from "./MenuDrawerIndex.svelte" import MenuDrawerIndex from "./MenuDrawerIndex.svelte"
import ThemeViewState from "../../Models/ThemeViewState" import ThemeViewState from "../../Models/ThemeViewState"
export let onlyLink: boolean export let onlyLink: boolean
export let state: ThemeViewState export let state: ThemeViewState
let hotkeys = Hotkeys._docs
let userdetails = state.osmConnection.userDetails
let theme = state.theme let theme = state.theme
let featureSwitches = state.featureSwitches let featureSwitches = state.featureSwitches

View file

@ -333,8 +333,9 @@
Android Android
{/if} {/if}
</div> </div>
</div>
{#if onlyLink} {#if onlyLink}
<InsetSpacer height={AndroidPolyfill.getInsetSizes().bottom} /> <InsetSpacer height={AndroidPolyfill.getInsetSizes().bottom} />
{/if} {/if}
</div>
</div> </div>

View file

@ -11,6 +11,8 @@
import BackButton from "../Base/BackButton.svelte" import BackButton from "../Base/BackButton.svelte"
import TagRenderingEditableDynamic from "../Popup/TagRendering/TagRenderingEditableDynamic.svelte" import TagRenderingEditableDynamic from "../Popup/TagRendering/TagRenderingEditableDynamic.svelte"
import { LastClickFeatureSource } from "../../Logic/FeatureSource/Sources/LastClickFeatureSource" import { LastClickFeatureSource } from "../../Logic/FeatureSource/Sources/LastClickFeatureSource"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
import InsetSpacer from "../Base/InsetSpacer.svelte"
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
export let selectedElement: Feature export let selectedElement: Feature
@ -93,5 +95,7 @@
config.classes.join(" ")} config.classes.join(" ")}
/> />
{/each} {/each}
<InsetSpacer height={AndroidPolyfill.getInsetSizes().bottom} />
</div> </div>
{/if} {/if}

View file

@ -6,7 +6,7 @@
*/ */
import type { SpecialVisualizationState } from "../SpecialVisualization" import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource" import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import type { NoteId, OsmTags, OsmId } from "../../Models/OsmFeature" import type { NoteId, OsmTags, OsmId } from "../../Models/OsmFeature"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte" import Tr from "../Base/Tr.svelte"
@ -25,6 +25,9 @@
Number of images uploaded succesfully Number of images uploaded succesfully
*/ */
function getCount(input: Store<string[]>): Store<number> { function getCount(input: Store<string[]>): Store<number> {
if(!input){
return new ImmutableStore(0)
}
if (featureId == "*") { if (featureId == "*") {
return input.map((inp) => inp.length) return input.map((inp) => inp.length)
} }

View file

@ -17,6 +17,9 @@
import type { GeocodeResult } from "../../Logic/Search/GeocodingProvider" import type { GeocodeResult } from "../../Logic/Search/GeocodingProvider"
import { default as GeocodeResultSvelte } from "./GeocodeResult.svelte" import { default as GeocodeResultSvelte } from "./GeocodeResult.svelte"
/**
* The big overview of all search bar results
*/
export let state: WithSearchState export let state: WithSearchState
let activeFilters: Store<(ActiveFilter & FilterSearchResult)[]> = let activeFilters: Store<(ActiveFilter & FilterSearchResult)[]> =
state.layerState.activeFilters.map((fs) => state.layerState.activeFilters.map((fs) =>

View file

@ -20,6 +20,7 @@
import DeleteButton from "./DeleteButton.svelte" import DeleteButton from "./DeleteButton.svelte"
import StudioHashSetter from "./StudioHashSetter" import StudioHashSetter from "./StudioHashSetter"
import TitledPanel from "../Base/TitledPanel.svelte" import TitledPanel from "../Base/TitledPanel.svelte"
import Popup from "../Base/Popup.svelte"
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw const layerSchema: ConfigMeta[] = <any>layerSchemaRaw

View file

@ -343,11 +343,13 @@
<!-- Top components --> <!-- Top components -->
<div class="z-4 pointer-events-none absolute left-0 top-0 w-full"> <div class="z-4 pointer-events-none absolute left-0 top-0 w-full">
<InsetSpacer height={AndroidPolyfill.getInsetSizes().top}/>
<div <div
id="top-bar" id="top-bar"
class="bg-black-light-transparent pointer-events-auto flex flex-wrap items-center justify-between px-4 py-1" class="bg-black-light-transparent pointer-events-auto flex flex-wrap items-center justify-between px-4 py-1"
> >
<div class="w-full">
<InsetSpacer height={AndroidPolyfill.getInsetSizes().top}/>
</div>
<!-- Top bar with tools --> <!-- Top bar with tools -->
<div class="flex items-center"> <div class="flex items-center">
<MapControlButton <MapControlButton
@ -466,6 +468,7 @@
{#if $selectedElement !== undefined && $selectedLayer !== undefined && !$selectedLayer.popupInFloatover} {#if $selectedElement !== undefined && $selectedLayer !== undefined && !$selectedLayer.popupInFloatover}
<!-- right modal with the selected element view --> <!-- right modal with the selected element view -->
<Drawer <Drawer
placement="right" placement="right"
transitionType="fly" transitionType="fly"

View file

@ -1,4 +1,4 @@
export function dragDetection(htmlElement: HTMLElement, callback: () => {}) { export function dragDetection(htmlElement: HTMLElement, callback: () => void) {
let isDown = false let isDown = false
const threshold = 5 const threshold = 5
let start = null let start = null

View file

@ -406,7 +406,7 @@ h2.group {
.warning { .warning {
/* The class to convey important information, but not as grave as 'alert' */ /* The class to convey important information, but not as grave as 'alert' */
background-color: var(--low-interaction-background); background-color: var(--alert-color);
color: var(--alert-foreground-color); color: var(--alert-foreground-color);
font-weight: bold; font-weight: bold;
border-radius: 1em; border-radius: 1em;

View file

@ -74,7 +74,7 @@
</div> </div>
</div> </div>
<div class="flex justify-between items-end w-full absolute bottom-0 p-4 h-fit delete-on-load"> <div class="flex justify-between items-end w-full absolute bottom-0 p-8 h-fit delete-on-load">
<!-- IMAGE-START --> <!-- IMAGE-START -->
<img aria-hidden="true" class="p-4 h-32 w-32 self-start" src="./assets/svg/add.svg"> <img aria-hidden="true" class="p-4 h-32 w-32 self-start" src="./assets/svg/add.svg">