Skip to content

Commit 559146e

Browse files
committed
feat: basemap
1 parent 75c3e73 commit 559146e

4 files changed

Lines changed: 97 additions & 5 deletions

File tree

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"dependencies": {
2222
"next": "^15.5.5",
2323
"react": "^19.2.0",
24-
"react-dom": "^19.2.0"
24+
"react-dom": "^19.2.0",
25+
"maplibre-gl": "^5.13.0"
2526
},
2627
"devDependencies": {
2728
"@tailwindcss/postcss": "^4.1.14",
@@ -32,4 +33,4 @@
3233
"tailwindcss": "^4.1.14",
3334
"typescript": "^5.9.3"
3435
}
35-
}
36+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
'use client';
2+
3+
import { useEffect, useRef } from 'react';
4+
import { Map } from 'maplibre-gl';
5+
import 'maplibre-gl/dist/maplibre-gl.css';
6+
7+
export function BaseMap({ onMapLoaded, onCleanup }: { onMapLoaded?: (map: Map) => void; onCleanup?: () => void }) {
8+
const mapContainerRef = useRef<HTMLDivElement>(null);
9+
const mapRef = useRef<Map | null>(null);
10+
11+
useEffect(() => {
12+
if (mapContainerRef.current === null) return;
13+
14+
const map = new Map({
15+
container: mapContainerRef.current,
16+
style: {
17+
version: 8,
18+
name: 'ExpTech Studio',
19+
sources: {
20+
map: {
21+
type: 'vector',
22+
url: 'https://lb.exptech.dev/api/v1/map/tiles/tiles.json',
23+
},
24+
},
25+
sprite: '',
26+
glyphs: 'https://glyphs.geolonia.com/{fontstack}/{range}.pbf',
27+
layers: [
28+
{
29+
id: 'background',
30+
type: 'background',
31+
paint: { 'background-color': '#1f2025' },
32+
},
33+
{
34+
'id': 'county',
35+
'type': 'fill',
36+
'source': 'map',
37+
'source-layer': 'city',
38+
'paint': { 'fill-color': '#3F4045' },
39+
},
40+
{
41+
'id': 'county-outline',
42+
'type': 'line',
43+
'source': 'map',
44+
'source-layer': 'city',
45+
'paint': { 'line-color': '#a9b4bc' },
46+
},
47+
{
48+
'id': 'town',
49+
'type': 'fill',
50+
'source': 'map',
51+
'source-layer': 'town',
52+
'paint': { 'fill-color': 'transparent' },
53+
},
54+
],
55+
},
56+
center: [121.6, 23.5],
57+
zoom: 6.8,
58+
attributionControl: false,
59+
pitchWithRotate: false,
60+
dragRotate: false,
61+
maxZoom: 12,
62+
minZoom: 4,
63+
});
64+
65+
mapRef.current = map;
66+
67+
const MAP_BOUNDS = [[118.0, 21.2], [124.0, 25.8]] as [[number, number], [number, number]];
68+
void map.fitBounds(MAP_BOUNDS, { padding: 20, duration: 0 });
69+
70+
if (onMapLoaded) onMapLoaded(map);
71+
72+
map.on('error', () => void 0);
73+
74+
return () => {
75+
if (onCleanup) onCleanup();
76+
map.remove();
77+
};
78+
}, []);
79+
80+
return <div ref={mapContainerRef} className="h-full w-full" />;
81+
}
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
'use client';
2+
import { BaseMap } from '../components/map/base';
3+
import { useState } from 'react';
4+
import { Map } from 'maplibre-gl';
5+
16
export default function HomePage() {
2-
return <div>Hello World 12</div>;
7+
const [map, setMap] = useState<Map | null>(null);
8+
return (
9+
<div className="h-full w-full">
10+
<BaseMap onMapLoaded={(map: Map) => setMap(map)} />
11+
</div>
12+
);
313
}

packages/web/src/app/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export default function RootLayout({
1212
children: React.ReactNode
1313
}) {
1414
return (
15-
<html lang="zh-TW">
16-
<body>{children}</body>
15+
<html lang="zh-TW" className="h-full">
16+
<body className="h-full m-0 p-0">{children}</body>
1717
</html>
1818
)
1919
}

0 commit comments

Comments
 (0)