Skip to content

Commit d2356f2

Browse files
committed
feat(mobile/map): floating add button to submit a mosque
Extends IconButton with an xl size (84 px, 150% of md) and adds a pill-shape filled green FAB in the bottom-right of the map screen that routes to /submit. Composes the existing IconButton primitive so haptics, pressed scale, and shadows stay consistent with the rest of the app. Safe-area aware. Sits below the mosque bottom-sheet so a selected pin overlays it naturally.
1 parent 50868ff commit d2356f2

4 files changed

Lines changed: 40 additions & 2 deletions

File tree

apps/mobile/components/ui/icon-button.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const iconButtonVariants = cva("items-center justify-center", {
99
size: {
1010
sm: "h-10 w-10",
1111
md: "h-14 w-14",
12+
// 150% of md — used for the floating action button on the map.
13+
xl: "size-[60px]",
1214
},
1315
shape: {
1416
pill: "rounded-pill",
@@ -27,9 +29,10 @@ const iconButtonVariants = cva("items-center justify-center", {
2729
},
2830
});
2931

30-
const ICON_SIZE: Record<"sm" | "md", number> = {
32+
const ICON_SIZE: Record<"sm" | "md" | "xl", number> = {
3133
sm: 18,
3234
md: 22,
35+
xl: 33,
3336
};
3437

3538
const SHADOW_SM = {
@@ -86,7 +89,7 @@ export function IconButton({
8689
const resolvedTone: Tone =
8790
tone ?? (variantKey === "filled" ? "white" : "ink");
8891
const shadow =
89-
variantKey === "ghost" ? null : sizeKey === "md" ? SHADOW_MD : SHADOW_SM;
92+
variantKey === "ghost" ? null : sizeKey === "sm" ? SHADOW_SM : SHADOW_MD;
9093

9194
return (
9295
<Pressable

apps/mobile/components/ui/icon.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
ParkingSquare,
1919
Pencil,
2020
Phone,
21+
Plus,
2122
Route,
2223
Search,
2324
Settings2,
@@ -54,6 +55,7 @@ export const iconMap = {
5455
pencil: Pencil,
5556
phone: Phone,
5657
pin: MapPin,
58+
plus: Plus,
5759
recenter: LocateFixed,
5860
search: Search,
5961
settings: Settings2,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { router } from "expo-router";
2+
import { View } from "react-native";
3+
import { useSafeAreaInsets } from "react-native-safe-area-context";
4+
import { IconButton } from "@/components/ui/icon-button";
5+
6+
const MARGIN = 20;
7+
8+
export function MapAddFab() {
9+
const insets = useSafeAreaInsets();
10+
11+
return (
12+
<View
13+
pointerEvents="box-none"
14+
className="absolute"
15+
style={{
16+
right: MARGIN,
17+
bottom: insets.bottom + MARGIN,
18+
}}
19+
>
20+
<IconButton
21+
icon="plus"
22+
size="xl"
23+
shape="pill"
24+
variant="filled"
25+
accessibilityLabel="Submit a mosque"
26+
onPress={() => router.push("/submit")}
27+
/>
28+
</View>
29+
);
30+
}

apps/mobile/features/map/components/map-screen.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import { usePrayerTimes } from "@/features/prayer-times";
1717
import { useUserLocation } from "@/lib/use-user-location";
1818
import { DHAKA_REGION } from "../lib/region";
19+
import { MapAddFab } from "./map-add-fab";
1920
import { MapMarkers } from "./map-markers";
2021
import { MapMosqueSheet } from "./map-mosque-sheet";
2122
import { MapTopOverlay } from "./map-top-overlay";
@@ -193,6 +194,8 @@ export function MapScreen() {
193194
onCloseMenu={() => setMenuOpen(false)}
194195
/>
195196

197+
<MapAddFab />
198+
196199
<MapMosqueSheet
197200
ref={sheetRef}
198201
mosque={selected}

0 commit comments

Comments
 (0)