@trapar-waves/react-three-maplibre 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,13 +9,14 @@ import Map from "react-map-gl/maplibre";
9
9
  import { Canvas } from "react-three-map/maplibre";
10
10
  import { LineMaterial, LineSegments2, LineSegmentsGeometry } from "three-stdlib";
11
11
 
12
- extend({ LineSegmentsGeometry, LineMaterial, LineSegments2 });
12
+ // eslint-disable-next-line unicorn/no-top-level-side-effects -- R3F extend must run before render
13
+ extend({ LineMaterial, LineSegments2, LineSegmentsGeometry });
13
14
 
14
15
  declare module "@react-three/fiber" {
15
16
  interface ThreeElements {
16
- lineSegmentsGeometry: ThreeElements["bufferGeometry"];
17
- lineMaterial: ThreeElements["material"] & Partial<LineMaterial>;
17
+ lineMaterial: Partial<LineMaterial> & ThreeElements["material"];
18
18
  lineSegments2: ThreeElements["object3D"] & { children?: ReactNode };
19
+ lineSegmentsGeometry: ThreeElements["bufferGeometry"];
19
20
  }
20
21
  }
21
22
 
@@ -24,7 +25,9 @@ const latLon = {
24
25
  longitude: 121.417463,
25
26
  };
26
27
 
27
- /** Carto 托管的 MapLibre GL 矢量底图,无需 API Key(使用请遵守 https://carto.com/basemaps/ 条款)。 */
28
+ /**
29
+ * Carto 托管的 MapLibre GL 矢量底图,无需 API Key(使用请遵守 https://carto.com/basemaps/ 条款)。
30
+ */
28
31
  const MAP_STYLE_URL = "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json";
29
32
 
30
33
  function publicAssetUrl(filename: string): string {
@@ -37,23 +40,23 @@ const HOMEPAGE = "https://github.com/Trapar-waves/react-three-maplibre";
37
40
  const LOGOS_SET_URL = "https://icon-sets.iconify.design/logos/";
38
41
 
39
42
  interface TechPill {
43
+ iconClass: string;
40
44
  id: string;
41
45
  label: string;
42
- iconClass: string;
43
46
  }
44
47
 
45
48
  const techStack: TechPill[] = [
46
- { id: "react", label: "React 19", iconClass: "icon-[logos--react]" },
47
- { id: "ts", label: "TypeScript", iconClass: "icon-[logos--typescript-icon]" },
48
- { id: "tailwind", label: "Tailwind 4", iconClass: "icon-[logos--tailwindcss-icon]" },
49
- { id: "rsbuild", label: "Rsbuild", iconClass: "icon-[material-icon-theme--rstack]" },
50
- { id: "maplibre", label: "MapLibre · react-map-gl", iconClass: "icon-[simple-icons--maplibre]" },
51
- { id: "l7", label: "AntV L7", iconClass: "icon-[logos--markdown]" },
52
- { id: "r3f", label: "R3F · drei · three-stdlib", iconClass: "icon-[logos--threejs]" },
53
- { id: "rtmap", label: "react-three-map", iconClass: "icon-[logos--markdown]" },
54
- { id: "eslint", label: "ESLint", iconClass: "icon-[logos--eslint]" },
55
- { id: "pnpm", label: "pnpm", iconClass: "icon-[logos--pnpm]" },
56
- { id: "github", label: "GitHub Actions", iconClass: "icon-[logos--github-icon]" },
49
+ { iconClass: "icon-[logos--react]", id: "react", label: "React 19" },
50
+ { iconClass: "icon-[logos--typescript-icon]", id: "ts", label: "TypeScript" },
51
+ { iconClass: "icon-[logos--tailwindcss-icon]", id: "tailwind", label: "Tailwind 4" },
52
+ { iconClass: "icon-[material-icon-theme--rstack]", id: "rsbuild", label: "Rsbuild" },
53
+ { iconClass: "icon-[simple-icons--maplibre]", id: "maplibre", label: "MapLibre · react-map-gl" },
54
+ { iconClass: "icon-[logos--markdown]", id: "l7", label: "AntV L7" },
55
+ { iconClass: "icon-[logos--threejs]", id: "r3f", label: "R3F · drei · three-stdlib" },
56
+ { iconClass: "icon-[logos--markdown]", id: "rtmap", label: "react-three-map" },
57
+ { iconClass: "icon-[logos--eslint]", id: "eslint", label: "ESLint" },
58
+ { iconClass: "icon-[logos--pnpm]", id: "pnpm", label: "pnpm" },
59
+ { iconClass: "icon-[logos--github-icon]", id: "github", label: "GitHub Actions" },
57
60
  ];
58
61
 
59
62
  const readmeFeatures: string[] = [
@@ -72,44 +75,49 @@ const readmeFeatures: string[] = [
72
75
  const readmeTechNote = "演示使用 @react-three/drei 的 Stats;生产可按需移除。若需其他底图,可在 App.tsx 中替换 MAP_STYLE_URL 并自行处理鉴权。";
73
76
 
74
77
  function App() {
75
- const ref = useRef<HTMLDivElement>(null!);
76
- const mapRef = useRef<MapRef>(null!);
78
+ const divReference = useRef<HTMLDivElement>(null!);
79
+ const mapReference = useRef<MapRef>(null!);
77
80
  function initL7() {
78
- if (mapRef.current) {
79
- const scene = new Scene({
80
- id: "map",
81
- map: new MapLibre({
82
- mapInstance: mapRef.current.getMap(),
83
- }),
84
- });
85
- scene.on("loaded", () => {
86
- fetch(publicAssetUrl("BElVQFEFvpAKzddxFZxJ.txt"))
87
- .then(res => (res.ok ? res.text() : Promise.reject(new Error(String(res.status)))))
88
- .then((data) => {
89
- const pointLayer = new PointLayer({
90
- blend: "additive",
91
- })
92
- .source(data, {
93
- parser: {
94
- type: "csv",
95
- y: "lat",
96
- x: "lng",
97
- },
98
- })
99
- .size(0.5)
100
- .color("#080298");
81
+ if (!mapReference.current) {
82
+ return;
83
+ }
101
84
 
102
- scene.addLayer(pointLayer);
85
+ const scene = new Scene({
86
+ id: "map",
87
+ map: new MapLibre({
88
+ mapInstance: mapReference.current.getMap(),
89
+ }),
90
+ });
91
+ scene.on("loaded", async () => {
92
+ try {
93
+ const response = await fetch(publicAssetUrl("BElVQFEFvpAKzddxFZxJ.txt"));
94
+ if (!response.ok) {
95
+ return;
96
+ }
97
+ const data = await response.text();
98
+ const pointLayer = new PointLayer({
99
+ blend: "additive",
100
+ })
101
+ .source(data, {
102
+ parser: {
103
+ type: "csv",
104
+ x: "lng",
105
+ y: "lat",
106
+ },
103
107
  })
104
- .catch(() => {});
105
- });
106
- }
108
+ .size(0.5)
109
+ .color("#080298");
110
+
111
+ scene.addLayer(pointLayer);
112
+ }
113
+ catch {}
114
+ });
107
115
  }
108
116
  return (
109
- <div className="h-screen w-screen relative overflow-hidden" ref={ref}>
117
+ <div className="h-screen w-screen relative overflow-hidden" ref={divReference}>
110
118
  <aside
111
- className="pointer-events-none absolute left-4 top-4 z-10 max-w-sm motion-safe:transition motion-safe:duration-200"
112
119
  aria-label="模板说明"
120
+ className="pointer-events-none absolute left-4 top-4 z-10 max-w-sm motion-safe:transition motion-safe:duration-200"
113
121
  >
114
122
  <div
115
123
  className="pointer-events-auto max-h-[min(70vh,560px)] overflow-y-auto overscroll-contain rounded-xl border border-white/15 bg-slate-950/80 p-4 text-sm text-slate-100 shadow-xl backdrop-blur-md"
@@ -123,10 +131,10 @@ function App() {
123
131
  画布叠在 L7 之上。底图为 Carto 公开 GL 样式,无需地图服务 API Key。技术图标见
124
132
  {" "}
125
133
  <a
126
- href={LOGOS_SET_URL}
127
134
  className="font-medium text-sky-300 underline decoration-sky-700 underline-offset-2 hover:text-sky-200"
128
- target="_blank"
135
+ href={LOGOS_SET_URL}
129
136
  rel="noreferrer"
137
+ target="_blank"
130
138
  >
131
139
  Iconify logos
132
140
  </a>
@@ -138,10 +146,10 @@ function App() {
138
146
  <div className="mt-2 flex flex-wrap gap-2">
139
147
  {techStack.map(item => (
140
148
  <span
141
- key={item.id}
142
149
  className="inline-flex items-center gap-1.5 rounded-lg border border-white/10 bg-slate-900/80 px-2 py-1 text-xs text-slate-200"
150
+ key={item.id}
143
151
  >
144
- <span className={`${item.iconClass} text-base text-sky-300`} aria-hidden />
152
+ <span aria-hidden className={`${item.iconClass} text-base text-sky-300`} />
145
153
  {item.label}
146
154
  </span>
147
155
  ))}
@@ -160,15 +168,15 @@ function App() {
160
168
  <p className="mt-3 text-xs leading-relaxed text-slate-500">
161
169
  品牌图标来自
162
170
  {" "}
163
- <a href={LOGOS_SET_URL} className="text-sky-400 underline hover:text-sky-300" target="_blank" rel="noreferrer">SVG Logos</a>
171
+ <a className="text-sky-400 underline hover:text-sky-300" href={LOGOS_SET_URL} rel="noreferrer" target="_blank">SVG Logos</a>
164
172
  {" "}
165
173
  (CC0)。
166
174
  </p>
167
175
  <a
168
- href={HOMEPAGE}
169
176
  className="mt-3 inline-flex min-h-11 w-full items-center justify-center rounded-lg bg-sky-500 px-4 py-2 text-sm font-semibold text-slate-950 hover:bg-sky-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white"
170
- target="_blank"
177
+ href={HOMEPAGE}
171
178
  rel="noreferrer"
179
+ target="_blank"
172
180
  >
173
181
  查看仓库
174
182
  </a>
@@ -176,20 +184,20 @@ function App() {
176
184
  </aside>
177
185
  <Map
178
186
  id="map"
179
- ref={mapRef}
180
187
  initialViewState={{
181
188
  ...latLon,
182
- zoom: 11,
183
189
  pitch: 64.88,
190
+ zoom: 11,
184
191
  }}
185
192
  mapStyle={MAP_STYLE_URL}
186
193
  onLoad={initL7}
194
+ ref={mapReference}
187
195
  >
188
- <Stats className="stats" parent={ref} />
196
+ <Stats className="stats" parent={divReference} />
189
197
 
190
198
  <Canvas
191
- overlay
192
199
  id="trapar-r3f-overlay"
200
+ overlay
193
201
  {...latLon}
194
202
  >
195
203
  <hemisphereLight
package/src/index.tsx CHANGED
@@ -1,10 +1,10 @@
1
1
  import ReactDOM from "react-dom/client";
2
- import App from "./App";
2
+ import App from "./app";
3
3
  import "./global.css";
4
4
 
5
- const rootEl = document.getElementById("root");
6
- if (rootEl) {
7
- const root = ReactDOM.createRoot(rootEl);
5
+ const rootElement = document.querySelector("#root");
6
+ if (rootElement) {
7
+ const root = ReactDOM.createRoot(rootElement);
8
8
  root.render(
9
9
  <App />,
10
10
  );
File without changes