react-route-profile 0.1.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.
- package/README.md +129 -0
- package/bin/fetch-elevation.js +194 -0
- package/dist/index.cjs +6 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +2 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +137 -0
- package/dist/index.d.ts +137 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# react-route-profile
|
|
2
|
+
|
|
3
|
+
React component that renders a Google Map route with an interactive elevation profile, plus optional sticky header helper and theming.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install react-route-profile
|
|
9
|
+
# or
|
|
10
|
+
yarn add react-route-profile
|
|
11
|
+
# or (preferred)
|
|
12
|
+
bun add react-route-profile
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { RouteMap } from "react-route-profile";
|
|
19
|
+
import type { RouteConfig } from "react-route-profile";
|
|
20
|
+
|
|
21
|
+
const apiKey = process.env.VITE_GOOGLE_MAPS_API_KEY || "";
|
|
22
|
+
|
|
23
|
+
const myRoute: RouteConfig = {
|
|
24
|
+
id: "01",
|
|
25
|
+
name: "Sample route",
|
|
26
|
+
center: { lat: 48.9, lng: 20.5 },
|
|
27
|
+
zoomHorizontal: 14,
|
|
28
|
+
zoomVertical: 12,
|
|
29
|
+
geoJson: myGeoJsonObject,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
<RouteMap apiKey={apiKey} route={myRoute} height="100dvh" />;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Precompute elevation offline
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bunx fetch-elevation --in path/to/route.geojson --out path/to/route.elevation.json --samples 200 --key $GOOGLE_MAPS_API_KEY
|
|
39
|
+
# - Elevation API must be enabled for this key
|
|
40
|
+
# - Omit --out to overwrite the input file with elevationProfile
|
|
41
|
+
# - Pass the output json to RouteConfig
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Customization
|
|
45
|
+
|
|
46
|
+
### Custom theme
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import type { Theme } from "react-route-profile";
|
|
50
|
+
|
|
51
|
+
const myTheme: Theme = {
|
|
52
|
+
colors: {
|
|
53
|
+
primary: "rgba(14, 165, 233, 1)",
|
|
54
|
+
primaryMuted: "rgba(14, 165, 233, 0.7)",
|
|
55
|
+
accent: "rgba(132, 204, 22, 1)",
|
|
56
|
+
surface: "rgba(248, 250, 252, 1)",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
<RouteMap apiKey={apiKey} route={myRoute} theme={myTheme} />;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Sticky header sizing
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { useMapHeader } from "react-route-profile";
|
|
67
|
+
|
|
68
|
+
const { refHeader, refMapContainer, targetHeaderFraction, effectiveHeaderHeight, mapHeight } =
|
|
69
|
+
useMapHeader();
|
|
70
|
+
|
|
71
|
+
<header ref={refHeader} style={{ height: `${targetHeaderFraction * 100}vh` }}>Header</header>
|
|
72
|
+
<div ref={refMapContainer}>
|
|
73
|
+
<RouteMap apiKey={apiKey} route={myRoute} height={mapHeight} />
|
|
74
|
+
</div>;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## API
|
|
78
|
+
|
|
79
|
+
### RouteMap props
|
|
80
|
+
|
|
81
|
+
| Prop | Type | Description |
|
|
82
|
+
| --------- | ---------------- | ------------------------------------------------------ |
|
|
83
|
+
| apiKey | string | Required Google Maps JS API key. |
|
|
84
|
+
| route | RouteConfig | Route data (center, zooms, geoJson). |
|
|
85
|
+
| height | number \| string | Map height (e.g., `520` or `"100dvh"`). |
|
|
86
|
+
| className | string | Optional wrapper class. |
|
|
87
|
+
| style | CSSProperties | Inline style overrides. |
|
|
88
|
+
| theme | Theme | Optional theme override (colors, marker/dots, layout). |
|
|
89
|
+
|
|
90
|
+
### RouteConfig
|
|
91
|
+
|
|
92
|
+
| Field | Type | Description |
|
|
93
|
+
| -------------- | ------------------------------ | ------------------------------ |
|
|
94
|
+
| id | string | Identifier for the route. |
|
|
95
|
+
| name | string | Display name. |
|
|
96
|
+
| center | `{ lat: number; lng: number }` | Map center. |
|
|
97
|
+
| zoomHorizontal | number (optional) | Zoom when landscape. |
|
|
98
|
+
| zoomVertical | number (optional) | Zoom when portrait. |
|
|
99
|
+
| geoJson | FeatureCollection | GeoJSON geometry and features. |
|
|
100
|
+
|
|
101
|
+
### Theme
|
|
102
|
+
|
|
103
|
+
| Field | Type | Description |
|
|
104
|
+
| ------------------- | ------ | --------------------------------------- |
|
|
105
|
+
| colors.primary | string | Main accent color. |
|
|
106
|
+
| colors.primaryMuted | string | Softer variant of the primary. |
|
|
107
|
+
| colors.accent | string | Secondary/accent color for labels. |
|
|
108
|
+
| colors.surface | string | Background for loader/surfaces. |
|
|
109
|
+
| marker | object | Marker palette (outer/inner/start/end). |
|
|
110
|
+
| dots | object | Hover/line dot colors. |
|
|
111
|
+
| map | object | Map stroke/marker sizing. |
|
|
112
|
+
| chart | object | Chart spacing/strokes/ticks. |
|
|
113
|
+
| tooltip | object | Tooltip background/text/padding. |
|
|
114
|
+
| markerShape | object | Marker icon/label sizing/offsets. |
|
|
115
|
+
|
|
116
|
+
### useMapHeader
|
|
117
|
+
|
|
118
|
+
Returns helpers to size the map below a sticky header:
|
|
119
|
+
|
|
120
|
+
- `refHeader`: attach to your header element.
|
|
121
|
+
- `refMapContainer`: attach to the map container.
|
|
122
|
+
- `targetHeaderFraction`: target header height fraction (based on orientation).
|
|
123
|
+
- `effectiveHeaderHeight`: measured or fallback header height (px).
|
|
124
|
+
- `mapHeight`: string/number height you can pass to `RouteMap`.
|
|
125
|
+
|
|
126
|
+
## Notes
|
|
127
|
+
|
|
128
|
+
- Requires a valid Google Maps JavaScript API key with Maps JavaScript enabled.
|
|
129
|
+
- Provide `geoJson` as a FeatureCollection including your route geometry and optional point features for start/finish markers.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fetch elevation samples for a LineString in a GeoJSON file and persist them
|
|
4
|
+
* into the GeoJSON (properties.elevationProfile).
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* fetch-elevation --in path/to/route.geojson --out path/to/out.geojson --samples 128 --key YOUR_GOOGLE_API_KEY
|
|
8
|
+
*
|
|
9
|
+
* - --in: input GeoJSON file containing a FeatureCollection with a LineString route
|
|
10
|
+
* - --out: output path (defaults to input path with ".elevation.json" suffix)
|
|
11
|
+
* - --samples: number of samples along the path (default 128)
|
|
12
|
+
* - --key: Google Maps API key (or set GOOGLE_MAPS_API_KEY env var)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from "fs/promises";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import process from "process";
|
|
18
|
+
|
|
19
|
+
const toRad = (deg) => (deg * Math.PI) / 180;
|
|
20
|
+
|
|
21
|
+
const haversineMeters = (a, b) => {
|
|
22
|
+
const R = 6371000;
|
|
23
|
+
const dLat = toRad(b.lat - a.lat);
|
|
24
|
+
const dLng = toRad(b.lng - a.lng);
|
|
25
|
+
const la1 = toRad(a.lat);
|
|
26
|
+
const la2 = toRad(b.lat);
|
|
27
|
+
const sinDLat = Math.sin(dLat / 2);
|
|
28
|
+
const sinDLng = Math.sin(dLng / 2);
|
|
29
|
+
const h =
|
|
30
|
+
sinDLat * sinDLat +
|
|
31
|
+
Math.cos(la1) * Math.cos(la2) * sinDLng * sinDLng;
|
|
32
|
+
return 2 * R * Math.asin(Math.min(1, Math.sqrt(h)));
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const parseArgs = () => {
|
|
36
|
+
const args = process.argv.slice(2);
|
|
37
|
+
const out = {};
|
|
38
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
39
|
+
const key = args[i];
|
|
40
|
+
const val = args[i + 1];
|
|
41
|
+
if (!key?.startsWith("--")) continue;
|
|
42
|
+
out[key.slice(2)] = val;
|
|
43
|
+
if (val === undefined) i -= 1;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const validateGeoJson = (geoJson) => {
|
|
49
|
+
if (!geoJson || geoJson.type !== "FeatureCollection") {
|
|
50
|
+
throw new Error("GeoJSON must be a FeatureCollection");
|
|
51
|
+
}
|
|
52
|
+
if (!Array.isArray(geoJson.features)) {
|
|
53
|
+
throw new Error("GeoJSON FeatureCollection must have a features array");
|
|
54
|
+
}
|
|
55
|
+
const features = geoJson.features.filter(Boolean);
|
|
56
|
+
if (!features.length) {
|
|
57
|
+
throw new Error("GeoJSON has no features");
|
|
58
|
+
}
|
|
59
|
+
return features;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const extractPath = (geoJson) => {
|
|
63
|
+
const features = validateGeoJson(geoJson);
|
|
64
|
+
const line = features.find(
|
|
65
|
+
(f) =>
|
|
66
|
+
f?.geometry?.type === "LineString" &&
|
|
67
|
+
Array.isArray(f.geometry.coordinates)
|
|
68
|
+
);
|
|
69
|
+
if (!line) {
|
|
70
|
+
throw new Error("No LineString geometry found in GeoJSON");
|
|
71
|
+
}
|
|
72
|
+
const coords = line.geometry.coordinates || [];
|
|
73
|
+
const path = coords
|
|
74
|
+
.map((c) => ({ lng: Number(c[0]), lat: Number(c[1]) }))
|
|
75
|
+
.filter((c) => Number.isFinite(c.lat) && Number.isFinite(c.lng));
|
|
76
|
+
if (path.length < 2) {
|
|
77
|
+
throw new Error("LineString must contain at least two valid coordinates");
|
|
78
|
+
}
|
|
79
|
+
return { path, lineFeature: line };
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const downsamplePath = (points, maxPoints = 250) => {
|
|
83
|
+
if (points.length <= maxPoints) return points;
|
|
84
|
+
const stride = Math.ceil(points.length / maxPoints);
|
|
85
|
+
return points.filter((_, idx) => idx % stride === 0 || idx === points.length - 1);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const buildUrl = (apiKey, pathPoints, samples) => {
|
|
89
|
+
const pathParam = pathPoints.map((p) => `${p.lat},${p.lng}`).join("|");
|
|
90
|
+
const encodedPath = encodeURIComponent(pathParam);
|
|
91
|
+
return `https://maps.googleapis.com/maps/api/elevation/json?path=${encodedPath}&samples=${samples}&key=${apiKey}`;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const toElevationPoints = (results) => {
|
|
95
|
+
const points = [];
|
|
96
|
+
let cumulative = 0;
|
|
97
|
+
results.forEach((r, idx) => {
|
|
98
|
+
if (!r?.location) return;
|
|
99
|
+
if (idx > 0) {
|
|
100
|
+
const prev = results[idx - 1];
|
|
101
|
+
cumulative += haversineMeters(
|
|
102
|
+
{ lat: prev.location.lat, lng: prev.location.lng },
|
|
103
|
+
{ lat: r.location.lat, lng: r.location.lng }
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
points.push({
|
|
107
|
+
lat: r.location.lat,
|
|
108
|
+
lng: r.location.lng,
|
|
109
|
+
distance: Math.round(cumulative),
|
|
110
|
+
elevation: r.elevation,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
return points;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const main = async () => {
|
|
117
|
+
const args = parseArgs();
|
|
118
|
+
const inputPath = args.in || args.input;
|
|
119
|
+
if (!inputPath) {
|
|
120
|
+
console.error("Missing --in <path/to/geojson>");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const outputPath = args.out || inputPath;
|
|
124
|
+
const samples = Number(args.samples) || 128;
|
|
125
|
+
const apiKey = args.key || process.env.GOOGLE_MAPS_API_KEY;
|
|
126
|
+
if (!apiKey) {
|
|
127
|
+
console.error("Missing API key. Pass --key or set GOOGLE_MAPS_API_KEY.");
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const raw = await fs.readFile(inputPath, "utf8");
|
|
132
|
+
const geoJson = JSON.parse(raw);
|
|
133
|
+
const { path: rawPath, lineFeature } = extractPath(geoJson);
|
|
134
|
+
const pathPoints = downsamplePath(rawPath);
|
|
135
|
+
if (rawPath.length !== pathPoints.length) {
|
|
136
|
+
console.log(`Downsampled path from ${rawPath.length} to ${pathPoints.length} points.`);
|
|
137
|
+
}
|
|
138
|
+
if (pathPoints.length < 2) {
|
|
139
|
+
throw new Error("Not enough coordinates to sample elevation");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const url = buildUrl(apiKey, pathPoints, samples);
|
|
143
|
+
console.log(`Requesting elevation (${samples} samples)...`);
|
|
144
|
+
const res = await fetch(url);
|
|
145
|
+
let json;
|
|
146
|
+
try {
|
|
147
|
+
json = await res.json();
|
|
148
|
+
} catch {
|
|
149
|
+
/* ignore */
|
|
150
|
+
}
|
|
151
|
+
if (!res.ok) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Elevation API failed: ${res.status} ${res.statusText}${
|
|
154
|
+
json?.status ? ` (${json.status})` : ""
|
|
155
|
+
}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (json?.status !== "OK") {
|
|
159
|
+
throw new Error(`Elevation API error: ${json?.status || "Unknown error"}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const points = toElevationPoints(json.results || []);
|
|
163
|
+
lineFeature.properties = {
|
|
164
|
+
...(lineFeature.properties || {}),
|
|
165
|
+
elevationProfile: {
|
|
166
|
+
samples,
|
|
167
|
+
points,
|
|
168
|
+
source: "google-elevation",
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const output = geoJson;
|
|
173
|
+
|
|
174
|
+
await fs.writeFile(outputPath, JSON.stringify(output, null, 2), "utf8");
|
|
175
|
+
const totalDistance =
|
|
176
|
+
points.length > 0 ? points[points.length - 1].distance : 0;
|
|
177
|
+
const elevations = points.map((p) => p.elevation);
|
|
178
|
+
const min = elevations.length ? Math.min(...elevations) : 0;
|
|
179
|
+
const max = elevations.length ? Math.max(...elevations) : 0;
|
|
180
|
+
|
|
181
|
+
console.log(
|
|
182
|
+
`Saved ${points.length} elevation points to ${path.resolve(outputPath)}`
|
|
183
|
+
);
|
|
184
|
+
console.log(
|
|
185
|
+
`Distance ~${Math.round(totalDistance / 1000)} km | Elevation range ${Math.round(
|
|
186
|
+
min
|
|
187
|
+
)}m – ${Math.round(max)}m`
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
main().catch((err) => {
|
|
192
|
+
console.error(err);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
});
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
'use strict';var reactWrapper=require('@googlemaps/react-wrapper'),S=require('react'),jsxRuntime=require('react/jsx-runtime'),recharts=require('recharts');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var S__namespace=/*#__PURE__*/_interopNamespace(S);var _=()=>typeof window>"u"?true:window.innerWidth>=window.innerHeight,L=()=>{let[e,r]=S.useState(_);return S.useEffect(()=>{let o=()=>r(_());return window.addEventListener("resize",o),()=>window.removeEventListener("resize",o)},[]),{isHorizontal:e,isVertical:!e}};var g={colors:{primary:"rgba(14, 165, 233, 1)",primaryMuted:"rgba(14, 165, 233, 0.7)",accent:"rgba(132, 204, 22, 1)",surface:"rgba(248, 250, 252, 1)"},marker:{outer:"rgba(132, 204, 22, 1)",inner:"rgba(248, 250, 252, 1)",startInner:"rgba(34, 197, 94, 1)",finishInner:"rgba(239, 68, 68, 1)"},dots:{mapActive:"rgba(132, 204, 22, 1)",chart:"rgba(132, 204, 22, 1)",chartActive:"rgba(132, 204, 22, 1)"},map:{strokeWeight:10,markerSize:50,markerLabelFontSize:20,markerLabelFontWeight:"bold",hoverMarkerScale:6},chart:{margin:{top:4,right:8,bottom:4,left:8},gridStroke:"rgba(255,255,255,0.08)",gridDasharray:"3 3",axisStroke:"rgba(226, 232, 240, 0.7)",cursorStroke:"rgba(226,232,240,0.4)",cursorStrokeWidth:1,yAxisWidth:60,lineStrokeWidth:1,dotRadius:3,dotOpacity:.9,activeDotRadius:3,referenceDotRadius:7,referenceLineOpacity:.5,gradientStartOpacity:.6,gradientEndOpacity:.1,xTickFontSize:12,xTickDy:12,xTickUnitFontSize:10,xTickUnitDx:2,yTickFontSize:12,yTickDy:4,yTickUnitFontSize:10,yTickUnitDx:2},tooltip:{background:"rgba(15,23,42,0.9)",textColor:"#e2e8f0",padding:"6px 8px",borderRadius:6},markerShape:{size:33,lift:20,text:{fontSize:12,fontWeight:300,letterSpacing:2,xOffset:15,lineHeight:12,startLiftPerWord:10}}};var U=S.createContext(g),Ie=e=>({colors:{...g.colors,...e?.colors??{}},marker:{...g.marker,...e?.marker??{}},dots:{...g.dots,...e?.dots??{}},map:{...g.map,...e?.map??{}},chart:{...g.chart,...e?.chart??{}},tooltip:{...g.tooltip,...e?.tooltip??{}},markerShape:{...g.markerShape,...e?.markerShape??{},text:{...g.markerShape.text,...e?.markerShape?.text??{}}}}),M=({theme:e,children:r})=>{let o=S.useMemo(()=>Ie(e),[e]);return jsxRuntime.jsx(U.Provider,{value:o,children:r})},u=()=>S.useContext(U);var B={};var He=({message:e="Loading map...",height:r="100dvh"})=>{let o=u(),t={height:r,background:o.colors.surface,color:o.colors.primary};return jsxRuntime.jsx("div",{className:B.loader,style:t,children:e})},Y=He;var V=e=>{let{x:r,y:o,payload:t}=e,n=u(),i=Math.round((t?.value??0)/1e3);return jsxRuntime.jsxs("text",{x:r,y:o,fill:n.chart.axisStroke,fontSize:n.chart.xTickFontSize,textAnchor:"middle",dy:n.chart.xTickDy,children:[jsxRuntime.jsx("tspan",{children:i}),jsxRuntime.jsx("tspan",{fontSize:n.chart.xTickUnitFontSize,dx:n.chart.xTickUnitDx,children:"km"})]})};var J=S.createContext(void 0),$=({children:e})=>{let[r,o]=S.useState({}),t=S.useCallback(a=>o(a),[]),n=S.useCallback(()=>o({}),[]),i=S.useMemo(()=>({hover:r,setHover:t,clearHover:n}),[r,t,n]);return jsxRuntime.jsx(J.Provider,{value:i,children:e})},P=()=>{let e=S.useContext(J);if(!e)throw new Error("useHover must be used within HoverProvider");return e};var K=e=>{let{cx:r,cy:o,fill:t}=e,n=u();return r===void 0||o===void 0?null:jsxRuntime.jsx("circle",{cx:r,cy:o,r:n.chart.dotRadius,fill:t,opacity:n.chart.dotOpacity})},Z=e=>{let r=u();return jsxRuntime.jsx(K,{...e,fill:r.dots.chart})},Q=e=>{let r=u();return jsxRuntime.jsx(K,{...e,fill:r.dots.mapActive})};var j=e=>{let{x:r,y:o,payload:t}=e,n=u(),i=Math.round(t?.value??0);return jsxRuntime.jsxs("text",{x:r,y:o,fill:n.chart.axisStroke,fontSize:n.chart.yTickFontSize,textAnchor:"end",dy:n.chart.yTickDy,children:[jsxRuntime.jsx("tspan",{children:i}),jsxRuntime.jsx("tspan",{fontSize:n.chart.yTickUnitFontSize,dx:n.chart.yTickUnitDx,children:"m"})]})};var Xe=300,ee=({active:e,payload:r,label:o,accent:t,primary:n,markers:i})=>{let{tooltip:a}=u();if(!e||!r?.length)return null;let s=r[0]?.payload,c=i.find(p=>Math.abs((p?.distance??-1)-(s?.distance??0))<=Xe),d=Math.trunc(o/1e3),l=Math.round(o%1e3);return jsxRuntime.jsxs("div",{style:{background:a.background,border:"none",color:a.textColor,padding:a.padding,borderRadius:a.borderRadius},children:[jsxRuntime.jsxs("div",{style:{fontWeight:600,color:n},children:[d," km ",l," m"]}),jsxRuntime.jsxs("div",{children:["Elevation: ",jsxRuntime.jsxs("strong",{children:[Math.round(s?.elevation??0)," m"]})]}),c?.name?jsxRuntime.jsx("div",{style:{color:t,fontWeight:600},children:c.name}):null]})};var te=`<svg stroke="currentColor" fill="#84CC16" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
|
3
|
+
<path fill="white" stroke-linecap="round" stroke-linejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
4
|
+
</svg>
|
|
5
|
+
`;var Je=(e,r,o)=>{let t=0;return e.replace(/fill="[^"]*"/g,n=>(t+=1,t===1?`fill="${r}"`:t===2?`fill="${o}"`:n))},b=(e,r)=>{let o=Je(te,e,r).trim();return `data:image/svg+xml,${encodeURIComponent(o).replace(/'/g,"%27").replace(/"/g,"%22")}`};var re=e=>{let{cx:r,cy:o,fill:t,name:n}=e,i=u(),a=i.markerShape,s=a.size,c=S.useMemo(()=>b(i.marker.outer,i.marker.inner),[i.marker.inner,i.marker.outer]);if(r===void 0||o===void 0)return null;let d=typeof n=="string"?n.split(/\s+/).map(l=>l.trim()).filter(Boolean):[];return jsxRuntime.jsxs("g",{children:[jsxRuntime.jsx(Q,{cx:r,cy:o}),jsxRuntime.jsx("image",{x:r-s/2,y:o-s/2-a.lift,width:s,height:s,href:c}),n?jsxRuntime.jsx(Ze,{words:d,cx:r,cy:o,fill:t}):null]})},Ze=({words:e,cx:r,cy:o,fill:t})=>{let i=u().markerShape.text;return jsxRuntime.jsx("text",{y:o-e.length*i.startLiftPerWord,fill:t||"#fff",fontSize:i.fontSize,fontWeight:i.fontWeight,letterSpacing:i.letterSpacing,style:{userSelect:"none"},children:e.map((a,s)=>jsxRuntime.jsx(Qe,{word:a,index:s,cx:r},a))})},Qe=({word:e,index:r,cx:o})=>{let n=u().markerShape.text,i=S.useId();return jsxRuntime.jsx("tspan",{x:o+n.xOffset,dy:r===0?0:n.lineHeight,children:e},i)};function oe(e){let[r,o]=S.useState(null),t=S.useMemo(()=>r!=null?e[r]:null,[r,e]),n=S.useCallback(()=>{o(null);},[]),i=S.useCallback(a=>{if(!e.length)return;let s=0,c=e.length-1;for(;s<c;){let l=Math.floor((s+c)/2);e[l].distance<a?s=l+1:c=l;}let d=s;if(s>0){let l=e[s-1],p=e[s];Math.abs(l.distance-a)<Math.abs(p.distance-a)&&(d=s-1);}o(d);},[e]);return {activeIndex:r,activePoint:t,triggerByXValue:i,clearActiveIndex:n}}var ie=e=>e.length?e[e.length-1].distance:0,ae=(e,r=2e3)=>{let o=[];for(let t=0;t<=e;t+=r)o.push(t);return o[o.length-1]<e&&o.push(e),o},se=e=>[...e.geoJson?.features?.find(n=>n?.geometry?.type==="LineString"&&n?.properties?.elevationProfile?.points)?.properties?.elevationProfile?.points||[]].sort((n,i)=>(n?.distance??0)-(i?.distance??0)),ce=e=>(e.geoJson?.features?.find(t=>t?.geometry?.type==="LineString"&&t?.geometry?.coordinates)).geometry.coordinates,ue=e=>{if(!e.length)return [0,0];let r=e.map(i=>i.elevation),o=Math.min(...r),t=Math.max(...r),n=Math.max(10,(t-o)*.05);return [Math.floor(o-n),Math.ceil(t+n)]},le=e=>{let[r,o]=e,t=(o-r)*1.2,n=Math.max(10,Math.round(t/6/10)*10||50),i=Math.floor((r-(t-(o-r))/2)/n)*n,a=Math.ceil((o+(t-(o-r))/2)/n)*n,s=[];for(let c=i;c<=a+n/2;c+=n)s.push(c);return [i,a,s]},de=(e,r)=>{if(!e?.length)return [];let t=(r?.features??[]).filter(i=>i?.geometry?.type==="Point"),n=[];return t.forEach(i=>{let a=i?.geometry?.coordinates;if(!Array.isArray(a)||a.length<2)return;let[s,c]=a;if(!Number.isFinite(c)||!Number.isFinite(s))return;let d=e.reduce((l,p)=>{let f=Math.pow(p.lat-c,2)+Math.pow(p.lng-s,2);return f<l.dist?{point:p,dist:f}:l},{point:null,dist:Number.POSITIVE_INFINITY});if(d.point){if(d.point.distance===0)return;n.push({distance:d.point.distance,elevation:d.point.elevation,name:i?.properties?.name});}}),n.sort((i,a)=>(i.distance??0)-(a.distance??0))},me=(e,r)=>{if(!e.length)return null;let o=null,t=Number.POSITIVE_INFINITY;return e.forEach(([n,i])=>{let a=(i-r.lat)**2+(n-r.lng)**2;a<t&&(t=a,o={lat:i,lng:n});}),o},pe=(e,r)=>{if(!e.length)return null;let o=null,t=Number.POSITIVE_INFINITY;return e.forEach(n=>{let i=(n.lat-r[0])**2+(n.lng-r[1])**2;i<t&&(t=i,o=n);}),o};var fe=(e,r,o=1e3)=>r&&Math.abs((r.distance??0)-(e.distance??0))<o;var A=({route:e})=>{let{hover:r,setHover:o}=P(),t=u(),n=S.useMemo(()=>se(e),[e]),i=S.useMemo(()=>de(n,e.geoJson),[n,e.geoJson]),a=ie(n),s=ae(a),[c,d]=ue(n),[l,p,f]=S.useMemo(()=>le([c,d]),[c,d]),{activeIndex:C,activePoint:y,triggerByXValue:x,clearActiveIndex:w}=oe(n);return S.useEffect(()=>{if(r.source==="chart"||!r.lat||!r.lng)return;let h=pe(n,[r.lat,r.lng]);h&&x(h.distance);},[r,n,x]),n.length?jsxRuntime.jsx(recharts.ResponsiveContainer,{width:"100%",height:"100%",style:{userSelect:"none"},children:jsxRuntime.jsxs(recharts.ComposedChart,{data:n,margin:t.chart.margin,onMouseMove:({activePayload:h})=>{C&&w();let E=h?.[0];if(!E)return;let{lat:k,lng:T}=E.payload;o({lat:k,lng:T,source:"chart"});},onMouseEnter:()=>w(),children:[jsxRuntime.jsx("defs",{children:jsxRuntime.jsxs("linearGradient",{id:"elevationGradient",x1:"0",y1:"0",x2:"0",y2:"1",children:[jsxRuntime.jsx("stop",{offset:"0%",stopColor:t.colors.primary,stopOpacity:t.chart.gradientStartOpacity}),jsxRuntime.jsx("stop",{offset:"100%",stopColor:t.colors.primaryMuted,stopOpacity:t.chart.gradientEndOpacity})]})}),jsxRuntime.jsx(recharts.CartesianGrid,{stroke:t.chart.gridStroke,strokeDasharray:t.chart.gridDasharray}),jsxRuntime.jsx(recharts.XAxis,{dataKey:"distance",type:"number",domain:[0,a],ticks:s,tick:jsxRuntime.jsx(V,{}),stroke:t.chart.axisStroke}),jsxRuntime.jsx(recharts.YAxis,{dataKey:"elevation",tick:jsxRuntime.jsx(j,{}),domain:[l,p],ticks:f,stroke:t.chart.axisStroke,width:t.chart.yAxisWidth}),jsxRuntime.jsx(recharts.Tooltip,{cursor:{stroke:t.chart.cursorStroke,strokeWidth:t.chart.cursorStrokeWidth},content:jsxRuntime.jsx(ee,{accent:t.colors.accent,primary:t.colors.primary,markers:i})}),jsxRuntime.jsx(recharts.Line,{type:"monotone",dataKey:"elevation",stroke:t.colors.primary,strokeWidth:t.chart.lineStrokeWidth,dot:h=>{let{cx:E,cy:k,index:T}=h;return !(T===C)||!y?jsxRuntime.jsx(jsxRuntime.Fragment,{}):jsxRuntime.jsx(Z,{cx:E,cy:k})},activeDot:{r:t.chart.activeDotRadius,fill:t.dots.chartActive,strokeWidth:0},fill:"url(#elevationGradient)",isAnimationActive:false}),i.length>0&&i.map((h,E)=>{let k=fe(i[E],i[E+1]);return jsxRuntime.jsx(recharts.ReferenceDot,{x:h.distance,y:h.elevation,r:t.chart.referenceDotRadius,shape:T=>jsxRuntime.jsx(re,{...T,name:k?void 0:h.name,fill:t.colors.accent})},`${h.distance}-${E}`)}),y&&jsxRuntime.jsx(recharts.ReferenceLine,{x:y.distance,opacity:t.chart.referenceLineOpacity})]})}):null};var ge={lat:48.9325937,lng:20.3452306},ve=13,ye=12;var Se=.2,we=.15;var H={};var be=({route:e,height:r,isHorizontal:o})=>{let t=u(),{hover:n,setHover:i}=P(),a=S.useRef(null),s=S.useRef(null),c=S.useRef(null),d=S.useMemo(()=>ce(e),[e]),l=S.useMemo(()=>({default:b(t.marker.outer,t.marker.inner),start:b(t.marker.outer,t.marker.startInner),finish:b(t.marker.outer,t.marker.finishInner)}),[t.marker]);return S.useEffect(()=>{if(!a.current||!window.google?.maps)return;let p=o&&(e.zoomHorizontal||ve)||e.zoomVertical||ye,f=new window.google.maps.Map(a.current,{center:e.center||ge,zoom:p,mapTypeId:window.google.maps.MapTypeId.SATELLITE,streetViewControl:false});c.current=f,f.data.setStyle(y=>{let x=y.getProperty("name"),w=y.getProperty("first"),h=y.getProperty("last");return {strokeColor:t.colors.primaryMuted,strokeWeight:t.map.strokeWeight,icon:{url:w?l.start:h?l.finish:l.default,scaledSize:new window.google.maps.Size(t.map.markerSize,t.map.markerSize),optimized:false,zIndex:w||h?100:10,collisionBehavior:window.google?.maps?.CollisionBehavior?.REQUIRED_AND_HIDES_OPTIONAL},label:{className:H.markerLabel,fontSize:`${t.map.markerLabelFontSize}px`,fontWeight:t.map.markerLabelFontWeight,color:t.colors.accent,text:x}}}),f.data.addGeoJson(e.geoJson);let C=f.addListener("mousemove",y=>{let x=y.latLng;if(!x)return;let w=me(d,{lat:x.lat(),lng:x.lng()});w&&i({lat:w.lat,lng:w.lng,source:"map"});});return ()=>{C.remove(),f.data.forEach(y=>{f.data.remove(y);}),c.current=null;}},[e,o,t,d,i,l]),S.useEffect(()=>{if(!a.current||!window.google?.maps)return;let p=c.current;if(!p)return;if(!n.lat||!n.lng){s.current&&(s.current.setMap(null),s.current=null);return}let f={path:window.google.maps.SymbolPath.CIRCLE,scale:t.map.hoverMarkerScale,fillColor:t.dots.mapActive,fillOpacity:1,strokeWeight:0};s.current?s.current.setIcon(f):s.current=new window.google.maps.Marker({map:p,icon:f}),s.current.setPosition({lat:n.lat,lng:n.lng}),s.current.setMap(p);},[n,t]),jsxRuntime.jsx("div",{ref:a,className:H.mapCanvas,style:{height:r}})};var z={};var vt={apiKey:"Oops! Cannot display the map: Google Maps API key missing",[reactWrapper.Status.FAILURE]:"Unable to load Google Maps API. Check your API key or network.",[reactWrapper.Status.LOADING]:void 0,[reactWrapper.Status.SUCCESS]:void 0},Re=({type:e,height:r})=>jsxRuntime.jsx("div",{style:{height:r},children:jsxRuntime.jsx(Y,{message:vt[e],height:r})}),yt=(e,r)=>jsxRuntime.jsx(Re,{type:e,height:r}),ke=({apiKey:e,route:r,height:o="100dvh",className:t,style:n,theme:i=g})=>{let{isHorizontal:a}=L();if(!e)return jsxRuntime.jsx(M,{theme:i,children:jsxRuntime.jsx(Re,{type:"apiKey",height:o})});let s={height:o,width:"100%",...n};return jsxRuntime.jsx(M,{theme:i,children:jsxRuntime.jsx($,{children:jsxRuntime.jsxs("div",{className:t,style:s,children:[jsxRuntime.jsx(reactWrapper.Wrapper,{apiKey:e,render:c=>yt(c,o),children:jsxRuntime.jsx(be,{route:r,height:o,isHorizontal:a})}),jsxRuntime.jsx("div",{className:z.chartLayer,children:jsxRuntime.jsx("div",{className:z.chartBody,children:jsxRuntime.jsx(A,{route:r})})})]})})})};function W(){let[e,r]=S__namespace.useState({width:null,height:null}),o=S__namespace.useRef(null);return [S__namespace.useCallback(n=>{if(o.current&&(o.current.disconnect(),o.current=null),n?.nodeType===Node.ELEMENT_NODE){let i=new ResizeObserver(([a])=>{if(a&&a.borderBoxSize){let{inlineSize:s,blockSize:c}=a.borderBoxSize[0];r({width:s,height:c});}});i.observe(n),o.current=i;}},[]),e]}var Te=100,wt=()=>{let{isVertical:e}=L(),[r,{height:o}]=W(),[t,{height:n}]=W(),i=e?Se:we,s=(typeof window<"u"?window.innerHeight:Te/i)*i||Te,c=o||s,d=`calc(100dvh - ${c}px)`;return {refHeader:r,refMapContainer:t,targetHeaderFraction:i,effectiveHeaderHeight:c,mapHeight:n||d}};exports.RouteMap=ke;exports.ThemeProvider=M;exports.theme=g;exports.useMapHeader=wt;exports.useTheme=u;//# sourceMappingURL=index.cjs.map
|
|
6
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useOrientation.ts","../src/theme.ts","../src/theme-provider.tsx","../src/components/Loader.module.css","../src/components/Loader.tsx","../src/components/RouteMap/ElevationChart/DistanceTick.tsx","../src/components/RouteMap/HoverContext.tsx","../src/components/RouteMap/ElevationChart/ElevationDot.tsx","../src/components/RouteMap/ElevationChart/ElevationTick.tsx","../src/components/RouteMap/ElevationChart/ElevationTooltip.tsx","../src/assets/icons/marker.svg","../src/components/icons/buildMarkerIcon.ts","../src/components/RouteMap/ElevationChart/MarkerShape.tsx","../src/components/RouteMap/ElevationChart/useTriggerByXValue.tsx","../src/components/RouteMap/ElevationChart/utils.ts","../src/components/RouteMap/ElevationChart/ElevationChart.tsx","../src/constants.ts","../src/components/RouteMap/GoogleMapCanvas.module.css","../src/components/RouteMap/GoogleMapCanvas.tsx","../src/components/RouteMap/RouteMap.module.css","../src/components/RouteMap/RouteMap.tsx","../node_modules/@uidotdev/usehooks/index.js","../src/hooks/useMapHeader.ts"],"names":["getIsLandscape","useOrientation","isHorizontal","setIsHorizontal","useState","useEffect","handleResize","theme","ThemeContext","createContext","mergeTheme","override","ThemeProvider","children","mergedTheme","useMemo","jsx","useTheme","useContext","Loader_default","Loader","message","height","style","DistanceTick","props","x","y","payload","km","jsxs","HoverContext","HoverProvider","hover","setHoverState","setHover","useCallback","state","clearHover","value","useHover","ctx","Dot","cx","cy","fill","ElevationDot","MapDot","ElevationTick","m","MARKER_DISTANCE_TOLERANCE_MATCH","ElevationTooltip","active","label","accent","primary","markers","tooltip","point","marker","marker_default","svgTemplate","base","outer","inner","fillIndex","match","buildMarkerIcon","svg","MarkerShape","name","layout","size","iconHref","words","w","Text","text","word","index","Word","id","useId","useTriggerByXValue","data","activeIndex","setActiveIndex","activePoint","clearActiveIndex","triggerByXValue","distance","lo","hi","mid","bestIndex","prev","curr","getMaxDistance","points","getTicksForDistance","maxDistance","step","ticks","d","getPointsWithElevation","route","f","a","b","getAllPoints","computeMinMax","elevations","p","min","max","pad","computeRoundedDomainAndTicks","minMax","paddedRange","graphMin","graphMax","v","computeMarkerPoints","elevationPoints","geoJson","pointFeatures","coords","lng","lat","nearest","acc","findNearestPointByCoordinates","target","closest","minDist","findNearestPoint","isCloseCheck","marker1","marker2","threshold","ElevationChart","minY","maxY","tickVals","nearestPoint","ResponsiveContainer","ComposedChart","activePayload","activePayloadItem","CartesianGrid","XAxis","YAxis","Tooltip","Line","Fragment","idx","tooClose","ReferenceDot","ReferenceLine","DEFAULT_CENTER","DEFAULT_ZOOM_HORIZONTAL","DEFAULT_ZOOM_VERTICAL","RATIO_HEADER_MOBILE","RATIO_HEADER_DESKTOP","GoogleMapCanvas_default","GoogleMapCanvas","ref","useRef","highlightMarkerRef","mapRef","markerIcons","zoom","map","feature","isFirst","isLast","moveListener","e","latLng","mapInstance","icon","RouteMap_default","messages","Status","RenderLoader","type","render","status","RouteMap","apiKey","className","containerStyle","Wrapper","useMeasure","dimensions","setDimensions","S","previousObserver","node","observer","entry","width","HEADER_FALLBACK_PX","useMapHeader","isVertical","refHeader","headerHeight","refMapContainer","mapContainerHeight","targetHeaderFraction","fallbackHeaderHeightPx","effectiveHeaderHeight","mapHeightStyle"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useEffect, useState } from \"react\";\n\nconst getIsLandscape = () => {\n if (typeof window === \"undefined\") return true;\n return window.innerWidth >= window.innerHeight;\n};\n\nexport const useOrientation = () => {\n const [isHorizontal, setIsHorizontal] = useState<boolean>(getIsLandscape);\n\n useEffect(() => {\n const handleResize = () => setIsHorizontal(getIsLandscape());\n\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n return { isHorizontal, isVertical: !isHorizontal };\n};\n","export interface ThemeColors {\n primary: string;\n primaryMuted: string;\n accent: string;\n surface: string;\n}\n\nexport interface ThemeMarker {\n outer: string;\n inner: string;\n startInner: string;\n finishInner: string;\n}\n\nexport interface ThemeDots {\n mapActive: string;\n chart: string;\n chartActive: string;\n}\n\nexport interface ThemeMap {\n strokeWeight: number;\n markerSize: number;\n markerLabelFontSize: number;\n markerLabelFontWeight: string;\n hoverMarkerScale: number;\n}\n\nexport interface ThemeChart {\n margin: { top: number; right: number; bottom: number; left: number };\n gridStroke: string;\n gridDasharray: string;\n axisStroke: string;\n cursorStroke: string;\n cursorStrokeWidth: number;\n yAxisWidth: number;\n lineStrokeWidth: number;\n dotRadius: number;\n dotOpacity: number;\n activeDotRadius: number;\n referenceDotRadius: number;\n referenceLineOpacity: number;\n gradientStartOpacity: number;\n gradientEndOpacity: number;\n xTickFontSize: number;\n xTickDy: number;\n xTickUnitFontSize: number;\n xTickUnitDx: number;\n yTickFontSize: number;\n yTickDy: number;\n yTickUnitFontSize: number;\n yTickUnitDx: number;\n}\n\nexport interface ThemeTooltip {\n background: string;\n textColor: string;\n padding: string;\n borderRadius: number;\n}\n\nexport interface ThemeMarkerShape {\n size: number;\n lift: number;\n text: {\n fontSize: number;\n fontWeight: number;\n letterSpacing: number;\n xOffset: number;\n lineHeight: number;\n startLiftPerWord: number;\n };\n}\n\nexport interface ThemeShadows {\n map: string;\n}\n\nexport interface Theme {\n colors: ThemeColors;\n marker: ThemeMarker;\n dots: ThemeDots;\n map: ThemeMap;\n chart: ThemeChart;\n tooltip: ThemeTooltip;\n markerShape: ThemeMarkerShape & {\n text: ThemeMarkerShape[\"text\"];\n };\n}\n\nexport interface PartialTheme {\n colors?: Partial<ThemeColors>;\n marker?: Partial<ThemeMarker>;\n dots?: Partial<ThemeDots>;\n map?: Partial<ThemeMap>;\n chart?: Partial<ThemeChart>;\n tooltip?: Partial<ThemeTooltip>;\n markerShape?: Partial<ThemeMarkerShape> & {\n text?: Partial<ThemeMarkerShape[\"text\"]>;\n };\n}\n\nexport const theme: Theme = {\n colors: {\n primary: \"rgba(14, 165, 233, 1)\",\n primaryMuted: \"rgba(14, 165, 233, 0.7)\",\n accent: \"rgba(132, 204, 22, 1)\",\n surface: \"rgba(248, 250, 252, 1)\",\n },\n marker: {\n outer: \"rgba(132, 204, 22, 1)\",\n inner: \"rgba(248, 250, 252, 1)\",\n startInner: \"rgba(34, 197, 94, 1)\",\n finishInner: \"rgba(239, 68, 68, 1)\",\n },\n dots: {\n mapActive: \"rgba(132, 204, 22, 1)\",\n chart: \"rgba(132, 204, 22, 1)\",\n chartActive: \"rgba(132, 204, 22, 1)\",\n },\n map: {\n strokeWeight: 10,\n markerSize: 50,\n markerLabelFontSize: 20,\n markerLabelFontWeight: \"bold\",\n hoverMarkerScale: 6,\n },\n chart: {\n margin: { top: 4, right: 8, bottom: 4, left: 8 },\n gridStroke: \"rgba(255,255,255,0.08)\",\n gridDasharray: \"3 3\",\n axisStroke: \"rgba(226, 232, 240, 0.7)\",\n cursorStroke: \"rgba(226,232,240,0.4)\",\n cursorStrokeWidth: 1,\n yAxisWidth: 60,\n lineStrokeWidth: 1,\n dotRadius: 3,\n dotOpacity: 0.9,\n activeDotRadius: 3,\n referenceDotRadius: 7,\n referenceLineOpacity: 0.5,\n gradientStartOpacity: 0.6,\n gradientEndOpacity: 0.1,\n xTickFontSize: 12,\n xTickDy: 12,\n xTickUnitFontSize: 10,\n xTickUnitDx: 2,\n yTickFontSize: 12,\n yTickDy: 4,\n yTickUnitFontSize: 10,\n yTickUnitDx: 2,\n },\n tooltip: {\n background: \"rgba(15,23,42,0.9)\",\n textColor: \"#e2e8f0\",\n padding: \"6px 8px\",\n borderRadius: 6,\n },\n markerShape: {\n size: 33,\n lift: 20,\n text: {\n fontSize: 12,\n fontWeight: 300,\n letterSpacing: 2,\n xOffset: 15,\n lineHeight: 12,\n startLiftPerWord: 10,\n },\n },\n};\n","import { createContext, type ReactNode, useContext, useMemo } from \"react\";\nimport { theme as defaultTheme, PartialTheme, type Theme } from \"./theme\";\n\nconst ThemeContext = createContext<Theme>(defaultTheme);\n\nconst mergeTheme = (override?: PartialTheme): Theme => ({\n colors: {\n ...defaultTheme.colors,\n ...(override?.colors ?? {}),\n },\n marker: {\n ...defaultTheme.marker,\n ...(override?.marker ?? {}),\n },\n dots: {\n ...defaultTheme.dots,\n ...(override?.dots ?? {}),\n },\n map: {\n ...defaultTheme.map,\n ...(override?.map ?? {}),\n },\n chart: {\n ...defaultTheme.chart,\n ...(override?.chart ?? {}),\n },\n tooltip: {\n ...defaultTheme.tooltip,\n ...(override?.tooltip ?? {}),\n },\n markerShape: {\n ...defaultTheme.markerShape,\n ...(override?.markerShape ?? {}),\n text: {\n ...defaultTheme.markerShape.text,\n ...(override?.markerShape?.text ?? {}),\n },\n },\n});\n\nexport const ThemeProvider = ({\n theme,\n children,\n}: {\n theme?: PartialTheme;\n children: ReactNode;\n}) => {\n const mergedTheme = useMemo(() => mergeTheme(theme), [theme]);\n return (\n <ThemeContext.Provider value={mergedTheme}>\n {children}\n </ThemeContext.Provider>\n );\n};\n\nexport const useTheme = () => useContext(ThemeContext);\n",".loader {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n font-weight: 600;\n}\n","import type { CSSProperties } from \"react\";\nimport { useTheme } from \"../theme-provider\";\nimport styles from \"./Loader.module.css\";\n\ninterface LoaderProps {\n message?: string;\n height?: number | string;\n}\n\nconst Loader = ({\n message = \"Loading map...\",\n height = \"100dvh\",\n}: LoaderProps) => {\n const theme = useTheme();\n\n const style: CSSProperties = {\n height,\n background: theme.colors.surface,\n color: theme.colors.primary,\n };\n\n return (\n <div className={styles.loader} style={style}>\n {message}\n </div>\n );\n};\n\nexport default Loader;\n","import { useTheme } from \"../../../theme-provider\";\n\nexport const DistanceTick = (props: any) => {\n const { x, y, payload } = props;\n const theme = useTheme();\n const km = Math.round((payload?.value ?? 0) / 1000);\n return (\n <text\n x={x}\n y={y}\n fill={theme.chart.axisStroke}\n fontSize={theme.chart.xTickFontSize}\n textAnchor=\"middle\"\n dy={theme.chart.xTickDy}\n >\n <tspan>{km}</tspan>\n <tspan\n fontSize={theme.chart.xTickUnitFontSize}\n dx={theme.chart.xTickUnitDx}\n >\n km\n </tspan>\n </text>\n );\n};\n","import {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\n\nexport enum HoverStateChangeSource {\n Chart = \"chart\",\n Map = \"map\",\n}\nexport interface HoverState {\n lat?: number;\n lng?: number;\n source?: HoverStateChangeSource;\n}\n\ninterface HoverContextValue {\n hover: HoverState;\n setHover: (state: HoverState) => void;\n clearHover: () => void;\n}\n\nconst HoverContext = createContext<HoverContextValue | undefined>(undefined);\n\nexport const HoverProvider = ({ children }: { children: ReactNode }) => {\n const [hover, setHoverState] = useState<HoverState>({});\n\n const setHover = useCallback((state: HoverState) => setHoverState(state), []);\n const clearHover = useCallback(() => setHoverState({}), []);\n\n const value = useMemo(\n () => ({\n hover,\n setHover,\n clearHover,\n }),\n [hover, setHover, clearHover]\n );\n\n return (\n <HoverContext.Provider value={value}>{children}</HoverContext.Provider>\n );\n};\n\nexport const useHover = () => {\n const ctx = useContext(HoverContext);\n if (!ctx) {\n throw new Error(\"useHover must be used within HoverProvider\");\n }\n return ctx;\n};\n","import { useTheme } from \"../../../theme-provider\";\n\nconst Dot = (props: any) => {\n const { cx, cy, fill } = props;\n const theme = useTheme();\n if (cx === undefined || cy === undefined) return null;\n return (\n <circle\n cx={cx}\n cy={cy}\n r={theme.chart.dotRadius}\n fill={fill}\n opacity={theme.chart.dotOpacity}\n />\n );\n};\n\nexport const ElevationDot = (props: any) => {\n const theme = useTheme();\n return <Dot {...props} fill={theme.dots.chart} />;\n};\n\nexport const MapDot = (props: any) => {\n const theme = useTheme();\n return <Dot {...props} fill={theme.dots.mapActive} />;\n};\n","import { useTheme } from \"../../../theme-provider\";\n\nexport const ElevationTick = (props: any) => {\n const { x, y, payload } = props;\n const theme = useTheme();\n const m = Math.round(payload?.value ?? 0);\n return (\n <text\n x={x}\n y={y}\n fill={theme.chart.axisStroke}\n fontSize={theme.chart.yTickFontSize}\n textAnchor=\"end\"\n dy={theme.chart.yTickDy}\n >\n <tspan>{m}</tspan>\n <tspan\n fontSize={theme.chart.yTickUnitFontSize}\n dx={theme.chart.yTickUnitDx}\n >\n m\n </tspan>\n </text>\n );\n};\n","import type { TooltipProps } from \"recharts\";\nimport { useTheme } from \"../../../theme-provider\";\n\ninterface ElevationTooltipProps extends TooltipProps<number, string> {\n accent: string;\n primary: string;\n markers: Array<{ distance: number; name?: string }>;\n}\n\nconst MARKER_DISTANCE_TOLERANCE_MATCH = 300; // meters\n\nexport const ElevationTooltip = ({\n active,\n payload,\n label,\n accent,\n primary,\n markers,\n}: ElevationTooltipProps) => {\n const { tooltip } = useTheme();\n\n if (!active || !payload?.length) return null;\n\n const point = payload[0]?.payload;\n const marker = markers.find(\n (m) =>\n Math.abs((m?.distance ?? -1) - (point?.distance ?? 0)) <=\n MARKER_DISTANCE_TOLERANCE_MATCH\n );\n\n const km = Math.trunc((label as number) / 1000);\n const m = Math.round((label as number) % 1000);\n\n return (\n <div\n style={{\n background: tooltip.background,\n border: \"none\",\n color: tooltip.textColor,\n padding: tooltip.padding,\n borderRadius: tooltip.borderRadius,\n }}\n >\n <div style={{ fontWeight: 600, color: primary }}>\n {km} km {m} m\n </div>\n <div>\n Elevation: <strong>{Math.round(point?.elevation ?? 0)} m</strong>\n </div>\n {marker?.name ? (\n <div style={{ color: accent, fontWeight: 600 }}>{marker.name}</div>\n ) : null}\n </div>\n );\n};\n","<svg stroke=\"currentColor\" fill=\"#84CC16\" stroke-width=\"2\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" height=\"200px\" width=\"200px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"></path>\n <path fill=\"white\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"></path>\n</svg>\n","import markerSvg from \"../../assets/icons/marker.svg?raw\";\nimport markerFinishSvg from \"../../assets/icons/markerFinish.svg?raw\";\nimport markerStartSvg from \"../../assets/icons/markerStart.svg?raw\";\n\nconst svgTemplate = (base: string, outer: string, inner: string) => {\n let fillIndex = 0;\n return base.replace(/fill=\"[^\"]*\"/g, (match) => {\n fillIndex += 1;\n if (fillIndex === 1) return `fill=\"${outer}\"`;\n if (fillIndex === 2) return `fill=\"${inner}\"`;\n return match;\n });\n};\n\nexport const buildMarkerIcon = (outer: string, inner: string) => {\n const svg = svgTemplate(markerSvg, outer, inner).trim();\n const encoded = encodeURIComponent(svg)\n .replace(/'/g, \"%27\")\n .replace(/\"/g, \"%22\");\n return `data:image/svg+xml,${encoded}`;\n};\n\nexport const markers = {\n default: markerSvg,\n start: markerStartSvg,\n finish: markerFinishSvg,\n};","import { useId, useMemo } from \"react\";\nimport { useTheme } from \"../../../theme-provider\";\nimport { buildMarkerIcon } from \"../../icons/buildMarkerIcon\";\nimport { MapDot } from \"./ElevationDot\";\n\nexport const MarkerShape = (props: any) => {\n const { cx, cy, fill, name } = props;\n const theme = useTheme();\n const layout = theme.markerShape;\n const size = layout.size;\n\n const iconHref = useMemo(\n () => buildMarkerIcon(theme.marker.outer, theme.marker.inner),\n [theme.marker.inner, theme.marker.outer]\n );\n\n if (cx === undefined || cy === undefined) return null;\n\n const words =\n typeof name === \"string\"\n ? name\n .split(/\\s+/)\n .map((w) => w.trim())\n .filter(Boolean)\n : [];\n return (\n <g>\n <MapDot cx={cx} cy={cy} />\n <image\n x={cx - size / 2}\n y={cy - size / 2 - layout.lift}\n width={size}\n height={size}\n href={iconHref}\n />\n {name ? <Text words={words} cx={cx} cy={cy} fill={fill} /> : null}\n </g>\n );\n};\n\nconst Text = ({\n words,\n cx,\n cy,\n fill,\n}: {\n words: string[];\n cx: number;\n cy: number;\n fill?: string;\n}) => {\n const theme = useTheme();\n const text = theme.markerShape.text;\n\n return (\n <text\n y={cy - words.length * text.startLiftPerWord}\n fill={fill || \"#fff\"}\n fontSize={text.fontSize}\n fontWeight={text.fontWeight}\n letterSpacing={text.letterSpacing}\n style={{ userSelect: \"none\" }}\n >\n {words.map((word, index) => (\n <Word key={word} word={word} index={index} cx={cx} />\n ))}\n </text>\n );\n};\n\nconst Word = ({\n word,\n index,\n cx,\n}: {\n word: string;\n index: number;\n cx: number;\n}) => {\n const theme = useTheme();\n const text = theme.markerShape.text;\n const id = useId();\n return (\n <tspan\n key={id}\n x={cx + text.xOffset}\n dy={index === 0 ? 0 : text.lineHeight}\n >\n {word}\n </tspan>\n );\n};\n","import { useCallback, useMemo, useState } from \"react\";\n\nexport function useTriggerByXValue<\n T extends { distance: number; elevation: number }\n>(data: T[]) {\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n\n const activePoint = useMemo(\n () => (activeIndex != null ? data[activeIndex] : null),\n [activeIndex, data]\n );\n\n const clearActiveIndex = useCallback(() => {\n setActiveIndex(null);\n }, []);\n\n const triggerByXValue = useCallback(\n (distance: number) => {\n if (!data.length) return;\n\n // assume data sorted by distance ascending\n let lo = 0;\n let hi = data.length - 1;\n\n // binary search for closest distance\n while (lo < hi) {\n const mid = Math.floor((lo + hi) / 2);\n if (data[mid].distance < distance) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n\n // lo is first >= distance; pick closer of lo and lo-1\n let bestIndex = lo;\n if (lo > 0) {\n const prev = data[lo - 1];\n const curr = data[lo];\n if (\n Math.abs(prev.distance - distance) <\n Math.abs(curr.distance - distance)\n ) {\n bestIndex = lo - 1;\n }\n }\n\n setActiveIndex(bestIndex);\n },\n [data]\n );\n\n return {\n activeIndex,\n activePoint,\n triggerByXValue,\n clearActiveIndex,\n };\n}\n","import type { ElevationPoint, Marker } from \"./types\";\n\nexport const getMaxDistance = (points: ElevationPoint[]) =>\n points.length ? points[points.length - 1].distance : 0;\n\nexport const getTicksForDistance = (maxDistance: number, step: number = 2000) => {\n const ticks: number[] = [];\n for (let d = 0; d <= maxDistance; d += step) {\n ticks.push(d);\n }\n if (ticks[ticks.length - 1] < maxDistance) {\n ticks.push(maxDistance);\n }\n return ticks;\n}\n\nexport const getPointsWithElevation = (route: any): ElevationPoint[] => {\n const geo = route.geoJson as any;\n const line = geo?.features?.find(\n (f: any) =>\n f?.geometry?.type === \"LineString\" &&\n f?.properties?.elevationProfile?.points\n );\n const raw = line?.properties?.elevationProfile?.points || [];\n return [...raw].sort(\n (a: ElevationPoint, b: ElevationPoint) => (a?.distance ?? 0) - (b?.distance ?? 0)\n );\n};\n\nexport const getAllPoints = (route: any): [number, number][] => {\n const geo = route.geoJson as any;\n const points = geo?.features?.find(\n (f: any) =>\n f?.geometry?.type === \"LineString\" &&\n f?.geometry?.coordinates\n );\n return points.geometry.coordinates\n}\n\nexport const computeMinMax = (points: Array<{ elevation: number }>) => {\n if (!points.length) return [0, 0] as const;\n const elevations = points.map((p) => p.elevation);\n const min = Math.min(...elevations);\n const max = Math.max(...elevations);\n const pad = Math.max(10, (max - min) * 0.05);\n return [Math.floor(min - pad), Math.ceil(max + pad)];\n};\n\nexport const computeRoundedDomainAndTicks = (minMax: [number, number]) => {\n const [min, max] = minMax;\n const paddedRange = (max - min) * 1.2;\n const step = Math.max(10, Math.round(paddedRange / 6 / 10) * 10 || 50);\n const graphMin =\n Math.floor((min - (paddedRange - (max - min)) / 2) / step) * step;\n const graphMax =\n Math.ceil((max + (paddedRange - (max - min)) / 2) / step) * step;\n const ticks: number[] = [];\n for (let v = graphMin; v <= graphMax + step / 2; v += step) {\n ticks.push(v);\n }\n\n return [graphMin, graphMax, ticks] as const;\n};\n\nexport const computeMarkerPoints = (\n elevationPoints: ElevationPoint[],\n geoJson: any\n) => {\n if (!elevationPoints?.length) return [];\n const features = geoJson?.features ?? [];\n const pointFeatures = features.filter(\n (f: any) => f?.geometry?.type === \"Point\"\n );\n\n const markers: Array<Marker> = [];\n\n pointFeatures.forEach((f: any) => {\n const coords = f?.geometry?.coordinates;\n if (!Array.isArray(coords) || coords.length < 2) return;\n const [lng, lat] = coords;\n if (!Number.isFinite(lat) || !Number.isFinite(lng)) return;\n const nearest = elevationPoints.reduce<{\n point: ElevationPoint | null;\n dist: number;\n }>(\n (acc, p) => {\n const d = Math.pow(p.lat - lat, 2) + Math.pow(p.lng - lng, 2);\n if (d < acc.dist) {\n return { point: p, dist: d };\n }\n return acc;\n },\n { point: null, dist: Number.POSITIVE_INFINITY }\n );\n if (nearest.point) {\n // Skip if this marker matches the very first elevation point (start of route)\n if (nearest.point.distance === 0) {\n return;\n }\n markers.push({\n distance: nearest.point.distance,\n elevation: nearest.point.elevation,\n name: f?.properties?.name,\n });\n }\n });\n\n return markers.sort((a, b) => (a.distance ?? 0) - (b.distance ?? 0));\n};\n\nexport const findNearestPointByCoordinates = (\n points: [number, number][],\n target: { lat: number; lng: number }\n): ElevationPoint | null => {\n if (!points.length) return null;\n let closest: ElevationPoint | null = null;\n let minDist = Number.POSITIVE_INFINITY;\n points.forEach(([lng, lat]) => {\n const d = ((lat - target.lat) ** 2) + ((lng - target.lng) ** 2);\n if (d < minDist) {\n minDist = d;\n closest = { lat, lng } as ElevationPoint;\n }\n });\n return closest;\n};\n\nexport const findNearestPoint = (\n points: ElevationPoint[],\n target: [number, number]\n): ElevationPoint | null => {\n if (!points.length) return null;\n let closest: ElevationPoint | null = null;\n let minDist = Number.POSITIVE_INFINITY;\n points.forEach((p) => {\n const d = ((p.lat - target[0]) ** 2) + ((p.lng - target[1]) ** 2);\n if (d < minDist) {\n minDist = d;\n closest = p;\n }\n });\n return closest;\n};\n\nconst DISTANCE_THRESHOLD = 1000; // meters\n\nexport const isCloseCheck = (\n marker1: Marker,\n marker2?: Marker,\n threshold: number = DISTANCE_THRESHOLD\n) => {\n return (\n marker2 &&\n Math.abs((marker2.distance ?? 0) - (marker1.distance ?? 0)) < threshold\n );\n};","import { useEffect, useMemo } from \"react\";\nimport {\n CartesianGrid,\n ComposedChart,\n Line,\n ReferenceDot,\n ReferenceLine,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { useTheme } from \"../../../theme-provider\";\nimport type { RouteConfig } from \"../../../types\";\nimport { HoverStateChangeSource, useHover } from \"../HoverContext\";\nimport { DistanceTick } from \"./DistanceTick\";\nimport { ElevationDot } from \"./ElevationDot\";\nimport { ElevationTick } from \"./ElevationTick\";\nimport { ElevationTooltip } from \"./ElevationTooltip\";\nimport { MarkerShape } from \"./MarkerShape\";\nimport { useTriggerByXValue } from \"./useTriggerByXValue\";\nimport {\n computeMarkerPoints,\n computeMinMax,\n computeRoundedDomainAndTicks,\n findNearestPoint,\n getMaxDistance,\n getPointsWithElevation,\n getTicksForDistance,\n isCloseCheck,\n} from \"./utils\";\n\ninterface ElevationChartProps {\n route: RouteConfig;\n}\n\nexport const ElevationChart = ({ route }: ElevationChartProps) => {\n const { hover, setHover } = useHover();\n const theme = useTheme();\n const points = useMemo(() => getPointsWithElevation(route), [route]);\n const markers = useMemo(\n () => computeMarkerPoints(points, route.geoJson),\n [points, route.geoJson]\n );\n const maxDistance = getMaxDistance(points);\n const ticks = getTicksForDistance(maxDistance);\n const [min, max] = computeMinMax(points);\n const [minY, maxY, tickVals] = useMemo(\n () => computeRoundedDomainAndTicks([min, max]),\n [min, max]\n );\n\n const { activeIndex, activePoint, triggerByXValue, clearActiveIndex } =\n useTriggerByXValue(points);\n\n useEffect(() => {\n if (\n hover.source === HoverStateChangeSource.Chart ||\n !hover.lat ||\n !hover.lng\n )\n return;\n const nearestPoint = findNearestPoint(points, [hover.lat, hover.lng]);\n if (nearestPoint) {\n triggerByXValue(nearestPoint.distance);\n }\n }, [hover, points, triggerByXValue]);\n\n if (!points.length) {\n return null;\n }\n\n return (\n <ResponsiveContainer\n width=\"100%\"\n height=\"100%\"\n style={{ userSelect: \"none\" }}\n >\n <ComposedChart\n data={points}\n margin={theme.chart.margin}\n onMouseMove={({ activePayload }) => {\n activeIndex && clearActiveIndex();\n const activePayloadItem = activePayload?.[0];\n if (!activePayloadItem) {\n return;\n }\n const { lat, lng } = activePayloadItem.payload;\n setHover({ lat, lng, source: HoverStateChangeSource.Chart });\n }}\n onMouseEnter={() => clearActiveIndex()}\n >\n <defs>\n <linearGradient id=\"elevationGradient\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop\n offset=\"0%\"\n stopColor={theme.colors.primary}\n stopOpacity={theme.chart.gradientStartOpacity}\n />\n <stop\n offset=\"100%\"\n stopColor={theme.colors.primaryMuted}\n stopOpacity={theme.chart.gradientEndOpacity}\n />\n </linearGradient>\n </defs>\n\n <CartesianGrid\n stroke={theme.chart.gridStroke}\n strokeDasharray={theme.chart.gridDasharray}\n />\n <XAxis\n dataKey=\"distance\"\n type=\"number\"\n domain={[0, maxDistance]}\n ticks={ticks}\n tick={<DistanceTick />}\n stroke={theme.chart.axisStroke}\n />\n <YAxis\n dataKey=\"elevation\"\n tick={<ElevationTick />}\n domain={[minY, maxY]}\n ticks={tickVals}\n stroke={theme.chart.axisStroke}\n width={theme.chart.yAxisWidth}\n />\n\n <Tooltip\n cursor={{\n stroke: theme.chart.cursorStroke,\n strokeWidth: theme.chart.cursorStrokeWidth,\n }}\n content={\n <ElevationTooltip\n accent={theme.colors.accent}\n primary={theme.colors.primary}\n markers={markers}\n />\n }\n />\n\n <Line\n type=\"monotone\"\n dataKey=\"elevation\"\n stroke={theme.colors.primary}\n strokeWidth={theme.chart.lineStrokeWidth}\n dot={(props) => {\n const { cx, cy, index } = props;\n const isActive = index === activeIndex;\n if (!isActive || !activePoint) {\n // no dot for inactive points (or use small one if you prefer)\n return <></>;\n }\n return <ElevationDot cx={cx} cy={cy} />;\n }}\n activeDot={{\n r: theme.chart.activeDotRadius,\n fill: theme.dots.chartActive,\n strokeWidth: 0,\n }}\n fill=\"url(#elevationGradient)\"\n isAnimationActive={false}\n />\n\n {markers.length > 0 &&\n markers.map((m, idx) => {\n const tooClose = isCloseCheck(markers[idx], markers[idx + 1]);\n return (\n <ReferenceDot\n key={`${m.distance}-${idx}`}\n x={m.distance}\n y={m.elevation}\n r={theme.chart.referenceDotRadius}\n shape={(props) => (\n <MarkerShape\n {...props}\n name={tooClose ? undefined : m.name}\n fill={theme.colors.accent}\n />\n )}\n />\n );\n })}\n\n {/* Vertical line at active X (same as hover cursor) */}\n {activePoint && (\n <ReferenceLine\n x={activePoint.distance}\n opacity={theme.chart.referenceLineOpacity}\n />\n )}\n </ComposedChart>\n </ResponsiveContainer>\n );\n};\n","import markerSvg from \"./assets/icons/marker.svg\";\nimport markerFinishSvg from \"./assets/icons/markerFinish.svg\";\nimport markerStartSvg from \"./assets/icons/markerStart.svg\";\n\nexport const DEFAULT_CENTER = { lat: 48.9325937, lng: 20.3452306 };\nexport const DEFAULT_ZOOM_HORIZONTAL = 13;\nexport const DEFAULT_ZOOM_VERTICAL = 12;\n\nexport const markers = {\n default: markerSvg,\n start: markerStartSvg,\n finish: markerFinishSvg,\n}\n\nexport const RATIO_HEADER_MOBILE = 0.20\nexport const RATIO_HEADER_DESKTOP = 0.15",".markerLabel {\n position: absolute;\n bottom: 2rem; /* bottom-8 */\n left: 1.25rem; /* left-5 */\n text-align: left;\n filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25)) drop-shadow(0 2px 8px rgba(0, 0, 0, 0.35)); /* drop-shadow-md */\n background: rgba(0, 0, 0, 0.4); /* bg-black/40 */\n border-radius: 9999px; /* rounded-full */\n padding: 0.5rem; /* p-2 */\n}\n\n.mapCanvas {\n width: 100%;\n overflow: hidden;\n}\n","import { useEffect, useMemo, useRef } from \"react\";\nimport {\n DEFAULT_CENTER,\n DEFAULT_ZOOM_HORIZONTAL,\n DEFAULT_ZOOM_VERTICAL,\n} from \"../../constants\";\nimport { useTheme } from \"../../theme-provider\";\nimport type { RouteConfig } from \"../../types\";\nimport { buildMarkerIcon } from \"../icons/buildMarkerIcon\";\nimport {\n findNearestPointByCoordinates,\n getAllPoints,\n} from \"./ElevationChart/utils\";\nimport styles from \"./GoogleMapCanvas.module.css\";\nimport { HoverStateChangeSource, useHover } from \"./HoverContext\";\n\ninterface GoogleMapCanvasProps {\n route: RouteConfig;\n height: number | string;\n isHorizontal: boolean;\n}\n\nexport const GoogleMapCanvas = ({\n route,\n height,\n isHorizontal,\n}: GoogleMapCanvasProps) => {\n const theme = useTheme();\n const { hover, setHover } = useHover();\n const ref = useRef<HTMLDivElement | null>(null);\n const highlightMarkerRef = useRef<google.maps.Marker | null>(null);\n const mapRef = useRef<google.maps.Map | null>(null);\n const points = useMemo(() => getAllPoints(route), [route]);\n const markerIcons = useMemo(\n () => ({\n default: buildMarkerIcon(theme.marker.outer, theme.marker.inner),\n start: buildMarkerIcon(theme.marker.outer, theme.marker.startInner),\n finish: buildMarkerIcon(theme.marker.outer, theme.marker.finishInner),\n }),\n [theme.marker]\n );\n\n useEffect(() => {\n if (!ref.current || !window.google?.maps) {\n return;\n }\n\n const zoom =\n (isHorizontal && (route.zoomHorizontal || DEFAULT_ZOOM_HORIZONTAL)) ||\n route.zoomVertical ||\n DEFAULT_ZOOM_VERTICAL;\n\n const map = new window.google.maps.Map(ref.current, {\n center: route.center || DEFAULT_CENTER,\n zoom,\n mapTypeId: window.google.maps.MapTypeId.SATELLITE,\n streetViewControl: false,\n });\n mapRef.current = map;\n\n map.data.setStyle((feature: google.maps.Data.Feature) => {\n const name = feature.getProperty(\"name\") as string;\n const isFirst = feature.getProperty(\"first\") as boolean;\n const isLast = feature.getProperty(\"last\") as boolean;\n\n return {\n strokeColor: theme.colors.primaryMuted,\n strokeWeight: theme.map.strokeWeight,\n icon: {\n url: isFirst\n ? markerIcons.start\n : isLast\n ? markerIcons.finish\n : markerIcons.default,\n scaledSize: new window.google.maps.Size(\n theme.map.markerSize,\n theme.map.markerSize\n ),\n optimized: false,\n zIndex: isFirst || isLast ? 100 : 10,\n collisionBehavior:\n window.google?.maps?.CollisionBehavior?.REQUIRED_AND_HIDES_OPTIONAL,\n },\n label: {\n className: styles.markerLabel,\n fontSize: `${theme.map.markerLabelFontSize}px`,\n fontWeight: theme.map.markerLabelFontWeight,\n color: theme.colors.accent,\n text: name,\n },\n };\n });\n\n map.data.addGeoJson(route.geoJson);\n\n const moveListener = map.addListener(\n \"mousemove\",\n (e: google.maps.MapMouseEvent) => {\n const latLng = e.latLng;\n if (!latLng) return;\n const nearest = findNearestPointByCoordinates(points, {\n lat: latLng.lat(),\n lng: latLng.lng(),\n });\n if (nearest) {\n setHover({\n lat: nearest.lat,\n lng: nearest.lng,\n source: HoverStateChangeSource.Map,\n });\n }\n }\n );\n\n return () => {\n moveListener.remove();\n map.data.forEach((feature: google.maps.Data.Feature) => {\n map.data.remove(feature);\n });\n mapRef.current = null;\n };\n }, [route, isHorizontal, theme, points, setHover, markerIcons]);\n\n useEffect(() => {\n if (!ref.current || !window.google?.maps) return;\n const mapInstance = mapRef.current;\n if (!mapInstance) return;\n if (!hover.lat || !hover.lng) {\n if (highlightMarkerRef.current) {\n highlightMarkerRef.current.setMap(null);\n highlightMarkerRef.current = null;\n }\n return;\n }\n const icon = {\n path: window.google.maps.SymbolPath.CIRCLE,\n scale: theme.map.hoverMarkerScale,\n fillColor: theme.dots.mapActive,\n fillOpacity: 1,\n strokeWeight: 0,\n };\n if (!highlightMarkerRef.current) {\n highlightMarkerRef.current = new window.google.maps.Marker({\n map: mapInstance,\n icon,\n });\n } else {\n highlightMarkerRef.current.setIcon(icon);\n }\n highlightMarkerRef.current.setPosition({ lat: hover.lat, lng: hover.lng });\n highlightMarkerRef.current.setMap(mapInstance);\n }, [hover, theme]);\n\n return (\n <div\n ref={ref}\n className={styles.mapCanvas}\n style={{\n height,\n }}\n />\n );\n};\n",".chartLayer {\n position: absolute;\n left: 25px;\n right: auto;\n bottom: 25px;\n width: 50%;\n height: 180px;\n background: rgba(15, 23, 42, 0.85);\n border-radius: 12px;\n padding: 5px;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);\n display: flex;\n flex-direction: column;\n gap: 6px;\n pointer-events: auto;\n}\n\n.chartBody {\n flex: 1;\n min-height: 0;\n}\n\n@media (orientation: portrait) {\n .chartLayer {\n width: 80%;\n left: 10px;\n right: auto;\n }\n}\n","import { Status, Wrapper } from \"@googlemaps/react-wrapper\";\nimport type { CSSProperties } from \"react\";\nimport { useOrientation } from \"../../hooks/useOrientation\";\nimport { theme as defaultTheme, type PartialTheme } from \"../../theme\";\nimport { ThemeProvider } from \"../../theme-provider\";\nimport type { RouteConfig } from \"../../types\";\nimport Loader from \"../Loader\";\nimport { ElevationChart } from \"./ElevationChart\";\nimport { GoogleMapCanvas } from \"./GoogleMapCanvas\";\nimport { HoverProvider } from \"./HoverContext\";\nimport styles from \"./RouteMap.module.css\";\n\nexport interface RouteMapProps {\n apiKey: string;\n route: RouteConfig;\n height?: number | string;\n className?: string;\n style?: CSSProperties;\n theme?: PartialTheme;\n}\n\nconst messages = {\n apiKey: \"Oops! Cannot display the map: Google Maps API key missing\",\n [Status.FAILURE]:\n \"Unable to load Google Maps API. Check your API key or network.\",\n [Status.LOADING]: undefined,\n [Status.SUCCESS]: undefined,\n};\n\nconst RenderLoader = ({\n type,\n height,\n}: {\n type: keyof typeof messages;\n height?: number | string;\n}) => (\n <div style={{ height }}>\n <Loader message={messages[type]} height={height} />\n </div>\n);\n\nconst render = (status: Status, height?: number | string) => (\n <RenderLoader type={status} height={height} />\n);\n\nexport const RouteMap = ({\n apiKey,\n route,\n height = \"100dvh\",\n className,\n style,\n theme = defaultTheme,\n}: RouteMapProps) => {\n const { isHorizontal } = useOrientation();\n\n if (!apiKey) {\n return (\n <ThemeProvider theme={theme}>\n <RenderLoader type=\"apiKey\" height={height} />\n </ThemeProvider>\n );\n }\n\n const containerStyle: CSSProperties = {\n height,\n width: \"100%\",\n ...style,\n };\n\n return (\n <ThemeProvider theme={theme}>\n <HoverProvider>\n <div className={className} style={containerStyle}>\n <Wrapper apiKey={apiKey} render={(status) => render(status, height)}>\n <GoogleMapCanvas\n route={route}\n height={height}\n isHorizontal={isHorizontal}\n />\n </Wrapper>\n <div className={styles.chartLayer}>\n <div className={styles.chartBody}>\n <ElevationChart route={route} />\n </div>\n </div>\n </div>\n </HoverProvider>\n </ThemeProvider>\n );\n};\n","import * as React from \"react\";\n\nfunction isShallowEqual(object1, object2) {\n const keys1 = Object.keys(object1);\n const keys2 = Object.keys(object2);\n\n if (keys1.length !== keys2.length) {\n return false;\n }\n\n for (let key of keys1) {\n if (object1[key] !== object2[key]) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction isTouchEvent({ nativeEvent }) {\n return window.TouchEvent\n ? nativeEvent instanceof TouchEvent\n : \"touches\" in nativeEvent;\n}\n\nfunction isMouseEvent(event) {\n return event.nativeEvent instanceof MouseEvent;\n}\n\nfunction throttle(cb, ms) {\n let lastTime = 0;\n return () => {\n const now = Date.now();\n if (now - lastTime >= ms) {\n cb();\n lastTime = now;\n }\n };\n}\n\nfunction isPlainObject(value) {\n return Object.prototype.toString.call(value) === \"[object Object]\";\n}\n\nfunction dispatchStorageEvent(key, newValue) {\n window.dispatchEvent(new StorageEvent(\"storage\", { key, newValue }));\n}\n\nexport function useBattery() {\n const [state, setState] = React.useState({\n supported: true,\n loading: true,\n level: null,\n charging: null,\n chargingTime: null,\n dischargingTime: null,\n });\n\n React.useEffect(() => {\n if (!navigator.getBattery) {\n setState((s) => ({\n ...s,\n supported: false,\n loading: false,\n }));\n return;\n }\n\n let battery = null;\n\n const handleChange = () => {\n setState({\n supported: true,\n loading: false,\n level: battery.level,\n charging: battery.charging,\n chargingTime: battery.chargingTime,\n dischargingTime: battery.dischargingTime,\n });\n };\n\n navigator.getBattery().then((b) => {\n battery = b;\n handleChange();\n\n b.addEventListener(\"levelchange\", handleChange);\n b.addEventListener(\"chargingchange\", handleChange);\n b.addEventListener(\"chargingtimechange\", handleChange);\n b.addEventListener(\"dischargingtimechange\", handleChange);\n });\n\n return () => {\n if (battery) {\n battery.removeEventListener(\"levelchange\", handleChange);\n battery.removeEventListener(\"chargingchange\", handleChange);\n battery.removeEventListener(\"chargingtimechange\", handleChange);\n battery.removeEventListener(\"dischargingtimechange\", handleChange);\n }\n };\n }, []);\n\n return state;\n}\n\nexport function useClickAway(cb) {\n const ref = React.useRef(null);\n const refCb = React.useRef(cb);\n\n React.useLayoutEffect(() => {\n refCb.current = cb;\n });\n\n React.useEffect(() => {\n const handler = (e) => {\n const element = ref.current;\n if (element && !element.contains(e.target)) {\n refCb.current(e);\n }\n };\n\n document.addEventListener(\"mousedown\", handler);\n document.addEventListener(\"touchstart\", handler);\n\n return () => {\n document.removeEventListener(\"mousedown\", handler);\n document.removeEventListener(\"touchstart\", handler);\n };\n }, []);\n\n return ref;\n}\n\nfunction oldSchoolCopy(text) {\n const tempTextArea = document.createElement(\"textarea\");\n tempTextArea.value = text;\n document.body.appendChild(tempTextArea);\n tempTextArea.select();\n document.execCommand(\"copy\");\n document.body.removeChild(tempTextArea);\n}\n\nexport function useCopyToClipboard() {\n const [state, setState] = React.useState(null);\n\n const copyToClipboard = React.useCallback((value) => {\n const handleCopy = async () => {\n try {\n if (navigator?.clipboard?.writeText) {\n await navigator.clipboard.writeText(value);\n setState(value);\n } else {\n throw new Error(\"writeText not supported\");\n }\n } catch (e) {\n oldSchoolCopy(value);\n setState(value);\n }\n };\n\n handleCopy();\n }, []);\n\n return [state, copyToClipboard];\n}\n\nexport function useCounter(startingValue = 0, options = {}) {\n const { min, max } = options;\n\n if (typeof min === \"number\" && startingValue < min) {\n throw new Error(\n `Your starting value of ${startingValue} is less than your min of ${min}.`\n );\n }\n\n if (typeof max === \"number\" && startingValue > max) {\n throw new Error(\n `Your starting value of ${startingValue} is greater than your max of ${max}.`\n );\n }\n\n const [count, setCount] = React.useState(startingValue);\n\n const increment = React.useCallback(() => {\n setCount((c) => {\n const nextCount = c + 1;\n\n if (typeof max === \"number\" && nextCount > max) {\n return c;\n }\n\n return nextCount;\n });\n }, [max]);\n\n const decrement = React.useCallback(() => {\n setCount((c) => {\n const nextCount = c - 1;\n\n if (typeof min === \"number\" && nextCount < min) {\n return c;\n }\n\n return nextCount;\n });\n }, [min]);\n\n const set = React.useCallback(\n (nextCount) => {\n setCount((c) => {\n if (typeof max === \"number\" && nextCount > max) {\n return c;\n }\n\n if (typeof min === \"number\" && nextCount < min) {\n return c;\n }\n\n return nextCount;\n });\n },\n [max, min]\n );\n\n const reset = React.useCallback(() => {\n setCount(startingValue);\n }, [startingValue]);\n\n return [\n count,\n {\n increment,\n decrement,\n set,\n reset,\n },\n ];\n}\n\nexport function useDebounce(value, delay) {\n const [debouncedValue, setDebouncedValue] = React.useState(value);\n\n React.useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n\nexport function useDefault(initialValue, defaultValue) {\n const [state, setState] = React.useState(initialValue);\n\n if (typeof state === \"undefined\" || state === null) {\n return [defaultValue, setState];\n }\n\n return [state, setState];\n}\n\nexport function useDocumentTitle(title) {\n React.useEffect(() => {\n document.title = title;\n }, [title]);\n}\n\nexport function useFavicon(url) {\n React.useEffect(() => {\n let link = document.querySelector(`link[rel~=\"icon\"]`);\n\n if (!link) {\n link = document.createElement(\"link\");\n link.type = \"image/x-icon\";\n link.rel = \"icon\";\n link.href = url;\n document.head.appendChild(link);\n } else {\n link.href = url;\n }\n }, [url]);\n}\n\nexport function useGeolocation(options = {}) {\n const [state, setState] = React.useState({\n loading: true,\n accuracy: null,\n altitude: null,\n altitudeAccuracy: null,\n heading: null,\n latitude: null,\n longitude: null,\n speed: null,\n timestamp: null,\n error: null,\n });\n\n const optionsRef = React.useRef(options);\n\n React.useEffect(() => {\n const onEvent = ({ coords, timestamp }) => {\n setState({\n loading: false,\n timestamp,\n latitude: coords.latitude,\n longitude: coords.longitude,\n altitude: coords.altitude,\n accuracy: coords.accuracy,\n altitudeAccuracy: coords.altitudeAccuracy,\n heading: coords.heading,\n speed: coords.speed,\n });\n };\n\n const onEventError = (error) => {\n setState((s) => ({\n ...s,\n loading: false,\n error,\n }));\n };\n\n navigator.geolocation.getCurrentPosition(\n onEvent,\n onEventError,\n optionsRef.current\n );\n\n const watchId = navigator.geolocation.watchPosition(\n onEvent,\n onEventError,\n optionsRef.current\n );\n\n return () => {\n navigator.geolocation.clearWatch(watchId);\n };\n }, []);\n\n return state;\n}\n\nconst initialUseHistoryStateState = {\n past: [],\n present: null,\n future: [],\n};\n\nconst useHistoryStateReducer = (state, action) => {\n const { past, present, future } = state;\n\n if (action.type === \"UNDO\") {\n return {\n past: past.slice(0, past.length - 1),\n present: past[past.length - 1],\n future: [present, ...future],\n };\n } else if (action.type === \"REDO\") {\n return {\n past: [...past, present],\n present: future[0],\n future: future.slice(1),\n };\n } else if (action.type === \"SET\") {\n const { newPresent } = action;\n\n if (action.newPresent === present) {\n return state;\n }\n\n return {\n past: [...past, present],\n present: newPresent,\n future: [],\n };\n } else if (action.type === \"CLEAR\") {\n return {\n ...initialUseHistoryStateState,\n present: action.initialPresent,\n };\n } else {\n throw new Error(\"Unsupported action type\");\n }\n};\n\nexport function useHistoryState(initialPresent = {}) {\n const initialPresentRef = React.useRef(initialPresent);\n\n const [state, dispatch] = React.useReducer(useHistoryStateReducer, {\n ...initialUseHistoryStateState,\n present: initialPresentRef.current,\n });\n\n const canUndo = state.past.length !== 0;\n const canRedo = state.future.length !== 0;\n\n const undo = React.useCallback(() => {\n if (canUndo) {\n dispatch({ type: \"UNDO\" });\n }\n }, [canUndo]);\n\n const redo = React.useCallback(() => {\n if (canRedo) {\n dispatch({ type: \"REDO\" });\n }\n }, [canRedo]);\n\n const set = React.useCallback(\n (newPresent) => dispatch({ type: \"SET\", newPresent }),\n []\n );\n\n const clear = React.useCallback(\n () =>\n dispatch({ type: \"CLEAR\", initialPresent: initialPresentRef.current }),\n []\n );\n\n return { state: state.present, set, undo, redo, clear, canUndo, canRedo };\n}\n\nexport function useHover() {\n const [hovering, setHovering] = React.useState(false);\n const previousNode = React.useRef(null);\n\n const handleMouseEnter = React.useCallback(() => {\n setHovering(true);\n }, []);\n\n const handleMouseLeave = React.useCallback(() => {\n setHovering(false);\n }, []);\n\n const customRef = React.useCallback(\n (node) => {\n if (previousNode.current?.nodeType === Node.ELEMENT_NODE) {\n previousNode.current.removeEventListener(\n \"mouseenter\",\n handleMouseEnter\n );\n previousNode.current.removeEventListener(\n \"mouseleave\",\n handleMouseLeave\n );\n }\n\n if (node?.nodeType === Node.ELEMENT_NODE) {\n node.addEventListener(\"mouseenter\", handleMouseEnter);\n node.addEventListener(\"mouseleave\", handleMouseLeave);\n }\n\n previousNode.current = node;\n },\n [handleMouseEnter, handleMouseLeave]\n );\n\n return [customRef, hovering];\n}\n\nexport function useIdle(ms = 1000 * 60) {\n const [idle, setIdle] = React.useState(false);\n\n React.useEffect(() => {\n let timeoutId;\n\n const handleTimeout = () => {\n setIdle(true);\n };\n\n const handleEvent = throttle((e) => {\n setIdle(false);\n\n window.clearTimeout(timeoutId);\n timeoutId = window.setTimeout(handleTimeout, ms);\n }, 500);\n\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n handleEvent();\n }\n };\n\n timeoutId = window.setTimeout(handleTimeout, ms);\n\n window.addEventListener(\"mousemove\", handleEvent);\n window.addEventListener(\"mousedown\", handleEvent);\n window.addEventListener(\"resize\", handleEvent);\n window.addEventListener(\"keydown\", handleEvent);\n window.addEventListener(\"touchstart\", handleEvent);\n window.addEventListener(\"wheel\", handleEvent);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n window.removeEventListener(\"mousemove\", handleEvent);\n window.removeEventListener(\"mousedown\", handleEvent);\n window.removeEventListener(\"resize\", handleEvent);\n window.removeEventListener(\"keydown\", handleEvent);\n window.removeEventListener(\"touchstart\", handleEvent);\n window.removeEventListener(\"wheel\", handleEvent);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n window.clearTimeout(timeoutId);\n };\n }, [ms]);\n\n return idle;\n}\n\nexport function useIntersectionObserver(options = {}) {\n const { threshold = 1, root = null, rootMargin = \"0px\" } = options;\n const [entry, setEntry] = React.useState(null);\n\n const previousObserver = React.useRef(null);\n\n const customRef = React.useCallback(\n (node) => {\n if (previousObserver.current) {\n previousObserver.current.disconnect();\n previousObserver.current = null;\n }\n\n if (node?.nodeType === Node.ELEMENT_NODE) {\n const observer = new IntersectionObserver(\n ([entry]) => {\n setEntry(entry);\n },\n { threshold, root, rootMargin }\n );\n\n observer.observe(node);\n previousObserver.current = observer;\n }\n },\n [threshold, root, rootMargin]\n );\n\n return [customRef, entry];\n}\n\nexport function useIsClient() {\n const [isClient, setIsClient] = React.useState(false);\n\n React.useEffect(() => {\n setIsClient(true);\n }, []);\n\n return isClient;\n}\n\nexport function useIsFirstRender() {\n const renderRef = React.useRef(true);\n\n if (renderRef.current === true) {\n renderRef.current = false;\n return true;\n }\n\n return renderRef.current;\n}\n\nexport function useList(defaultList = []) {\n const [list, setList] = React.useState(defaultList);\n\n const set = React.useCallback((l) => {\n setList(l);\n }, []);\n\n const push = React.useCallback((element) => {\n setList((l) => [...l, element]);\n }, []);\n\n const removeAt = React.useCallback((index) => {\n setList((l) => [...l.slice(0, index), ...l.slice(index + 1)]);\n }, []);\n\n const insertAt = React.useCallback((index, element) => {\n setList((l) => [...l.slice(0, index), element, ...l.slice(index)]);\n }, []);\n\n const updateAt = React.useCallback((index, element) => {\n setList((l) => l.map((e, i) => (i === index ? element : e)));\n }, []);\n\n const clear = React.useCallback(() => setList([]), []);\n\n return [list, { set, push, removeAt, insertAt, updateAt, clear }];\n}\n\nconst setLocalStorageItem = (key, value) => {\n const stringifiedValue = JSON.stringify(value);\n window.localStorage.setItem(key, stringifiedValue);\n dispatchStorageEvent(key, stringifiedValue);\n};\n\nconst removeLocalStorageItem = (key) => {\n window.localStorage.removeItem(key);\n dispatchStorageEvent(key, null);\n};\n\nconst getLocalStorageItem = (key) => {\n return window.localStorage.getItem(key);\n};\n\nconst useLocalStorageSubscribe = (callback) => {\n window.addEventListener(\"storage\", callback);\n return () => window.removeEventListener(\"storage\", callback);\n};\n\nconst getLocalStorageServerSnapshot = () => {\n throw Error(\"useLocalStorage is a client-only hook\");\n};\n\nexport function useLocalStorage(key, initialValue) {\n const getSnapshot = () => getLocalStorageItem(key);\n\n const store = React.useSyncExternalStore(\n useLocalStorageSubscribe,\n getSnapshot,\n getLocalStorageServerSnapshot\n );\n\n const setState = React.useCallback(\n (v) => {\n try {\n const nextState = typeof v === \"function\" ? v(JSON.parse(store)) : v;\n\n if (nextState === undefined || nextState === null) {\n removeLocalStorageItem(key);\n } else {\n setLocalStorageItem(key, nextState);\n }\n } catch (e) {\n console.warn(e);\n }\n },\n [key, store]\n );\n\n React.useEffect(() => {\n if (\n getLocalStorageItem(key) === null &&\n typeof initialValue !== \"undefined\"\n ) {\n setLocalStorageItem(key, initialValue);\n }\n }, [key, initialValue]);\n\n return [store ? JSON.parse(store) : initialValue, setState];\n}\n\nexport function useLockBodyScroll() {\n React.useLayoutEffect(() => {\n const originalStyle = window.getComputedStyle(document.body).overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = originalStyle;\n };\n }, []);\n}\n\nexport function useLongPress(callback, options = {}) {\n const { threshold = 400, onStart, onFinish, onCancel } = options;\n const isLongPressActive = React.useRef(false);\n const isPressed = React.useRef(false);\n const timerId = React.useRef();\n\n return React.useMemo(() => {\n if (typeof callback !== \"function\") {\n return {};\n }\n\n const start = (event) => {\n if (!isMouseEvent(event) && !isTouchEvent(event)) return;\n\n if (onStart) {\n onStart(event);\n }\n\n isPressed.current = true;\n timerId.current = setTimeout(() => {\n callback(event);\n isLongPressActive.current = true;\n }, threshold);\n };\n\n const cancel = (event) => {\n if (!isMouseEvent(event) && !isTouchEvent(event)) return;\n\n if (isLongPressActive.current) {\n if (onFinish) {\n onFinish(event);\n }\n } else if (isPressed.current) {\n if (onCancel) {\n onCancel(event);\n }\n }\n\n isLongPressActive.current = false;\n isPressed.current = false;\n\n if (timerId.current) {\n window.clearTimeout(timerId.current);\n }\n };\n\n const mouseHandlers = {\n onMouseDown: start,\n onMouseUp: cancel,\n onMouseLeave: cancel,\n };\n\n const touchHandlers = {\n onTouchStart: start,\n onTouchEnd: cancel,\n };\n\n return {\n ...mouseHandlers,\n ...touchHandlers,\n };\n }, [callback, threshold, onCancel, onFinish, onStart]);\n}\n\nexport function useMap(initialState) {\n const mapRef = React.useRef(new Map(initialState));\n const [, reRender] = React.useReducer((x) => x + 1, 0);\n\n mapRef.current.set = (...args) => {\n Map.prototype.set.apply(mapRef.current, args);\n reRender();\n return mapRef.current;\n };\n\n mapRef.current.clear = (...args) => {\n Map.prototype.clear.apply(mapRef.current, args);\n reRender();\n };\n\n mapRef.current.delete = (...args) => {\n const res = Map.prototype.delete.apply(mapRef.current, args);\n reRender();\n\n return res;\n };\n\n return mapRef.current;\n}\n\nexport function useMeasure() {\n const [dimensions, setDimensions] = React.useState({\n width: null,\n height: null,\n });\n\n const previousObserver = React.useRef(null);\n\n const customRef = React.useCallback((node) => {\n if (previousObserver.current) {\n previousObserver.current.disconnect();\n previousObserver.current = null;\n }\n\n if (node?.nodeType === Node.ELEMENT_NODE) {\n const observer = new ResizeObserver(([entry]) => {\n if (entry && entry.borderBoxSize) {\n const { inlineSize: width, blockSize: height } =\n entry.borderBoxSize[0];\n\n setDimensions({ width, height });\n }\n });\n\n observer.observe(node);\n previousObserver.current = observer;\n }\n }, []);\n\n return [customRef, dimensions];\n}\n\nexport function useMediaQuery(query) {\n const subscribe = React.useCallback(\n (callback) => {\n const matchMedia = window.matchMedia(query);\n\n matchMedia.addEventListener(\"change\", callback);\n return () => {\n matchMedia.removeEventListener(\"change\", callback);\n };\n },\n [query]\n );\n\n const getSnapshot = () => {\n return window.matchMedia(query).matches;\n };\n\n const getServerSnapshot = () => {\n throw Error(\"useMediaQuery is a client-only hook\");\n };\n\n return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\nexport function useMouse() {\n const [state, setState] = React.useState({\n x: 0,\n y: 0,\n elementX: 0,\n elementY: 0,\n elementPositionX: 0,\n elementPositionY: 0,\n });\n\n const ref = React.useRef(null);\n\n React.useLayoutEffect(() => {\n const handleMouseMove = (event) => {\n let newState = {\n x: event.pageX,\n y: event.pageY,\n };\n\n if (ref.current?.nodeType === Node.ELEMENT_NODE) {\n const { left, top } = ref.current.getBoundingClientRect();\n const elementPositionX = left + window.scrollX;\n const elementPositionY = top + window.scrollY;\n const elementX = event.pageX - elementPositionX;\n const elementY = event.pageY - elementPositionY;\n\n newState.elementX = elementX;\n newState.elementY = elementY;\n newState.elementPositionX = elementPositionX;\n newState.elementPositionY = elementPositionY;\n }\n\n setState((s) => {\n return {\n ...s,\n ...newState,\n };\n });\n };\n\n document.addEventListener(\"mousemove\", handleMouseMove);\n\n return () => {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n };\n }, []);\n\n return [state, ref];\n}\n\nconst getConnection = () => {\n return (\n navigator?.connection ||\n navigator?.mozConnection ||\n navigator?.webkitConnection\n );\n};\n\nconst useNetworkStateSubscribe = (callback) => {\n window.addEventListener(\"online\", callback, { passive: true });\n window.addEventListener(\"offline\", callback, { passive: true });\n\n const connection = getConnection();\n\n if (connection) {\n connection.addEventListener(\"change\", callback, { passive: true });\n }\n\n return () => {\n window.removeEventListener(\"online\", callback);\n window.removeEventListener(\"offline\", callback);\n\n if (connection) {\n connection.removeEventListener(\"change\", callback);\n }\n };\n};\n\nconst getNetworkStateServerSnapshot = () => {\n throw Error(\"useNetworkState is a client-only hook\");\n};\n\nexport function useNetworkState() {\n const cache = React.useRef({});\n\n const getSnapshot = () => {\n const online = navigator.onLine;\n const connection = getConnection();\n\n const nextState = {\n online,\n downlink: connection?.downlink,\n downlinkMax: connection?.downlinkMax,\n effectiveType: connection?.effectiveType,\n rtt: connection?.rtt,\n saveData: connection?.saveData,\n type: connection?.type,\n };\n\n if (isShallowEqual(cache.current, nextState)) {\n return cache.current;\n } else {\n cache.current = nextState;\n return nextState;\n }\n };\n\n return React.useSyncExternalStore(\n useNetworkStateSubscribe,\n getSnapshot,\n getNetworkStateServerSnapshot\n );\n}\n\nexport function useObjectState(initialValue) {\n const [state, setState] = React.useState(initialValue);\n\n const handleUpdate = React.useCallback((arg) => {\n if (typeof arg === \"function\") {\n setState((s) => {\n const newState = arg(s);\n\n if (isPlainObject(newState)) {\n return {\n ...s,\n ...newState,\n };\n }\n });\n }\n\n if (isPlainObject(arg)) {\n setState((s) => ({\n ...s,\n ...arg,\n }));\n }\n }, []);\n\n return [state, handleUpdate];\n}\n\nexport function useOrientation() {\n const [orientation, setOrientation] = React.useState({\n angle: 0,\n type: \"landscape-primary\",\n });\n\n React.useLayoutEffect(() => {\n const handleChange = () => {\n const { angle, type } = window.screen.orientation;\n setOrientation({\n angle,\n type,\n });\n };\n\n const handle_orientationchange = () => {\n setOrientation({\n type: \"UNKNOWN\",\n angle: window.orientation,\n });\n };\n\n if (window.screen?.orientation) {\n handleChange();\n window.screen.orientation.addEventListener(\"change\", handleChange);\n } else {\n handle_orientationchange();\n window.addEventListener(\"orientationchange\", handle_orientationchange);\n }\n\n return () => {\n if (window.screen?.orientation) {\n window.screen.orientation.removeEventListener(\"change\", handleChange);\n } else {\n window.removeEventListener(\n \"orientationchange\",\n handle_orientationchange\n );\n }\n };\n }, []);\n\n return orientation;\n}\n\nconst usePreferredLanguageSubscribe = (cb) => {\n window.addEventListener(\"languagechange\", cb);\n return () => window.removeEventListener(\"languagechange\", cb);\n};\n\nconst getPreferredLanguageSnapshot = () => {\n return navigator.language;\n};\n\nconst getPreferredLanguageServerSnapshot = () => {\n throw Error(\"usePreferredLanguage is a client-only hook\");\n};\n\nexport function usePreferredLanguage() {\n return React.useSyncExternalStore(\n usePreferredLanguageSubscribe,\n getPreferredLanguageSnapshot,\n getPreferredLanguageServerSnapshot\n );\n}\n\nexport function usePrevious(value) {\n const [current, setCurrent] = React.useState(value);\n const [previous, setPrevious] = React.useState(null);\n\n if (value !== current) {\n setPrevious(current);\n setCurrent(value);\n }\n\n return previous;\n}\n\nexport function useQueue(initialValue = []) {\n const [queue, setQueue] = React.useState(initialValue);\n\n const add = React.useCallback((element) => {\n setQueue((q) => [...q, element]);\n }, []);\n\n const remove = React.useCallback(() => {\n let removedElement;\n\n setQueue(([first, ...q]) => {\n removedElement = first;\n return q;\n });\n\n return removedElement;\n }, []);\n\n const clear = React.useCallback(() => {\n setQueue([]);\n }, []);\n\n return {\n add,\n remove,\n clear,\n first: queue[0],\n last: queue[queue.length - 1],\n size: queue.length,\n queue,\n };\n}\n\nexport function useRenderCount() {\n const count = React.useRef(0);\n\n count.current++;\n\n return count.current;\n}\n\nexport function useRenderInfo(name = \"Unknown\") {\n const count = React.useRef(0);\n const lastRender = React.useRef();\n const now = Date.now();\n\n count.current++;\n\n React.useEffect(() => {\n lastRender.current = Date.now();\n });\n\n const sinceLastRender = lastRender.current ? now - lastRender.current : 0;\n\n if (process.env.NODE_ENV !== \"production\") {\n const info = {\n name,\n renders: count.current,\n sinceLastRender,\n timestamp: now,\n };\n\n console.log(info);\n\n return info;\n }\n}\n\nexport function useScript(src, options = {}) {\n const [status, setStatus] = React.useState(\"loading\");\n const optionsRef = React.useRef(options);\n\n React.useEffect(() => {\n let script = document.querySelector(`script[src=\"${src}\"]`);\n\n const domStatus = script?.getAttribute(\"data-status\");\n if (domStatus) {\n setStatus(domStatus);\n return;\n }\n\n if (script === null) {\n script = document.createElement(\"script\");\n script.src = src;\n script.async = true;\n script.setAttribute(\"data-status\", \"loading\");\n document.body.appendChild(script);\n\n const handleScriptLoad = () => {\n script.setAttribute(\"data-status\", \"ready\");\n setStatus(\"ready\");\n removeEventListeners();\n };\n\n const handleScriptError = () => {\n script.setAttribute(\"data-status\", \"error\");\n setStatus(\"error\");\n removeEventListeners();\n };\n\n const removeEventListeners = () => {\n script.removeEventListener(\"load\", handleScriptLoad);\n script.removeEventListener(\"error\", handleScriptError);\n };\n\n script.addEventListener(\"load\", handleScriptLoad);\n script.addEventListener(\"error\", handleScriptError);\n\n const removeOnUnmount = optionsRef.current.removeOnUnmount;\n\n return () => {\n if (removeOnUnmount === true) {\n script.remove();\n removeEventListeners();\n }\n };\n } else {\n setStatus(\"unknown\");\n }\n }, [src]);\n\n return status;\n}\n\nconst setSessionStorageItem = (key, value) => {\n const stringifiedValue = JSON.stringify(value);\n window.sessionStorage.setItem(key, stringifiedValue);\n dispatchStorageEvent(key, stringifiedValue);\n};\n\nconst removeSessionStorageItem = (key) => {\n window.sessionStorage.removeItem(key);\n dispatchStorageEvent(key, null);\n};\n\nconst getSessionStorageItem = (key) => {\n return window.sessionStorage.getItem(key);\n};\n\nconst useSessionStorageSubscribe = (callback) => {\n window.addEventListener(\"storage\", callback);\n return () => window.removeEventListener(\"storage\", callback);\n};\n\nconst getSessionStorageServerSnapshot = () => {\n throw Error(\"useSessionStorage is a client-only hook\");\n};\n\nexport function useSessionStorage(key, initialValue) {\n const getSnapshot = () => getSessionStorageItem(key);\n\n const store = React.useSyncExternalStore(\n useSessionStorageSubscribe,\n getSnapshot,\n getSessionStorageServerSnapshot\n );\n\n const setState = React.useCallback(\n (v) => {\n try {\n const nextState = typeof v === \"function\" ? v(JSON.parse(store)) : v;\n\n if (nextState === undefined || nextState === null) {\n removeSessionStorageItem(key);\n } else {\n setSessionStorageItem(key, nextState);\n }\n } catch (e) {\n console.warn(e);\n }\n },\n [key, store]\n );\n\n React.useEffect(() => {\n if (\n getSessionStorageItem(key) === null &&\n typeof initialValue !== \"undefined\"\n ) {\n setSessionStorageItem(key, initialValue);\n }\n }, [key, initialValue]);\n\n return [store ? JSON.parse(store) : initialValue, setState];\n}\n\nexport function useSet(values) {\n const setRef = React.useRef(new Set(values));\n const [, reRender] = React.useReducer((x) => x + 1, 0);\n\n setRef.current.add = (...args) => {\n const res = Set.prototype.add.apply(setRef.current, args);\n reRender();\n\n return res;\n };\n\n setRef.current.clear = (...args) => {\n Set.prototype.clear.apply(setRef.current, args);\n reRender();\n };\n\n setRef.current.delete = (...args) => {\n const res = Set.prototype.delete.apply(setRef.current, args);\n reRender();\n\n return res;\n };\n\n return setRef.current;\n}\n\nexport function useThrottle(value, interval = 500) {\n const [throttledValue, setThrottledValue] = React.useState(value);\n const lastUpdated = React.useRef(null);\n\n React.useEffect(() => {\n const now = Date.now();\n\n if (lastUpdated.current && now >= lastUpdated.current + interval) {\n lastUpdated.current = now;\n setThrottledValue(value);\n } else {\n const id = window.setTimeout(() => {\n lastUpdated.current = now;\n setThrottledValue(value);\n }, interval);\n\n return () => window.clearTimeout(id);\n }\n }, [value, interval]);\n\n return throttledValue;\n}\n\nexport function useToggle(initialValue) {\n const [on, setOn] = React.useState(() => {\n if (typeof initialValue === \"boolean\") {\n return initialValue;\n }\n\n return Boolean(initialValue);\n });\n\n const handleToggle = React.useCallback((value) => {\n if (typeof value === \"boolean\") {\n return setOn(value);\n }\n\n return setOn((v) => !v);\n }, []);\n\n return [on, handleToggle];\n}\n\nconst useVisibilityChangeSubscribe = (callback) => {\n document.addEventListener(\"visibilitychange\", callback);\n\n return () => {\n document.removeEventListener(\"visibilitychange\", callback);\n };\n};\n\nconst getVisibilityChangeSnapshot = () => {\n return document.visibilityState;\n};\n\nconst getVisibilityChangeServerSnapshot = () => {\n throw Error(\"useVisibilityChange is a client-only hook\");\n};\n\nexport function useVisibilityChange() {\n const visibilityState = React.useSyncExternalStore(\n useVisibilityChangeSubscribe,\n getVisibilityChangeSnapshot,\n getVisibilityChangeServerSnapshot\n );\n\n return visibilityState === \"visible\";\n}\n\nexport function useWindowScroll() {\n const [state, setState] = React.useState({\n x: null,\n y: null,\n });\n\n const scrollTo = React.useCallback((...args) => {\n if (typeof args[0] === \"object\") {\n window.scrollTo(args[0]);\n } else if (typeof args[0] === \"number\" && typeof args[1] === \"number\") {\n window.scrollTo(args[0], args[1]);\n } else {\n throw new Error(\n `Invalid arguments passed to scrollTo. See here for more info. https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo`\n );\n }\n }, []);\n\n React.useLayoutEffect(() => {\n const handleScroll = () => {\n setState({ x: window.scrollX, y: window.scrollY });\n };\n\n handleScroll();\n window.addEventListener(\"scroll\", handleScroll);\n\n return () => {\n window.removeEventListener(\"scroll\", handleScroll);\n };\n }, []);\n\n return [state, scrollTo];\n}\n\nexport function useWindowSize() {\n const [size, setSize] = React.useState({\n width: null,\n height: null,\n });\n\n React.useLayoutEffect(() => {\n const handleResize = () => {\n setSize({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n };\n\n handleResize();\n window.addEventListener(\"resize\", handleResize);\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n };\n }, []);\n\n return size;\n}\n","import { useMeasure } from \"@uidotdev/usehooks\";\nimport { RATIO_HEADER_DESKTOP, RATIO_HEADER_MOBILE } from \"../constants\";\nimport { useOrientation } from \"./useOrientation\";\n\nconst HEADER_FALLBACK_PX = 100;\n\nexport const useMapHeader = () => {\n const { isVertical } = useOrientation();\n\n const [refHeader, { height: headerHeight }] = useMeasure();\n const [refMapContainer, { height: mapContainerHeight }] = useMeasure();\n\n const targetHeaderFraction = isVertical ? RATIO_HEADER_MOBILE : RATIO_HEADER_DESKTOP;\n const viewportHeight = typeof window !== \"undefined\" ? window.innerHeight : HEADER_FALLBACK_PX / targetHeaderFraction;\n const fallbackHeaderHeightPx = viewportHeight * targetHeaderFraction || HEADER_FALLBACK_PX;\n const effectiveHeaderHeight = headerHeight || fallbackHeaderHeightPx;\n const mapHeightStyle = `calc(100dvh - ${effectiveHeaderHeight}px)`;\n const mapHeight = mapContainerHeight || mapHeightStyle;\n\n return {\n refHeader,\n refMapContainer,\n targetHeaderFraction,\n effectiveHeaderHeight,\n mapHeight,\n };\n};\n"]}
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.loader{display:flex;align-items:center;justify-content:center;width:100%;font-weight:600}.markerLabel{position:absolute;bottom:2rem;left:1.25rem;text-align:left;filter:drop-shadow(0 1px 2px rgba(0,0,0,.25)) drop-shadow(0 2px 8px rgba(0,0,0,.35));background:#0006;border-radius:9999px;padding:.5rem}.mapCanvas{width:100%;overflow:hidden}.chartLayer{position:absolute;left:25px;right:auto;bottom:25px;width:50%;height:180px;background:#0f172ad9;border-radius:12px;padding:5px;box-shadow:0 10px 30px #00000059;display:flex;flex-direction:column;gap:6px;pointer-events:auto}.chartBody{flex:1;min-height:0}@media(orientation:portrait){.chartLayer{width:80%;left:10px;right:auto}}
|
|
2
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Loader.module.css","../src/components/RouteMap/GoogleMapCanvas.module.css","../src/components/RouteMap/RouteMap.module.css"],"sourcesContent":[".loader {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n font-weight: 600;\n}\n",".markerLabel {\n position: absolute;\n bottom: 2rem; /* bottom-8 */\n left: 1.25rem; /* left-5 */\n text-align: left;\n filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25)) drop-shadow(0 2px 8px rgba(0, 0, 0, 0.35)); /* drop-shadow-md */\n background: rgba(0, 0, 0, 0.4); /* bg-black/40 */\n border-radius: 9999px; /* rounded-full */\n padding: 0.5rem; /* p-2 */\n}\n\n.mapCanvas {\n width: 100%;\n overflow: hidden;\n}\n",".chartLayer {\n position: absolute;\n left: 25px;\n right: auto;\n bottom: 25px;\n width: 50%;\n height: 180px;\n background: rgba(15, 23, 42, 0.85);\n border-radius: 12px;\n padding: 5px;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);\n display: flex;\n flex-direction: column;\n gap: 6px;\n pointer-events: auto;\n}\n\n.chartBody {\n flex: 1;\n min-height: 0;\n}\n\n@media (orientation: portrait) {\n .chartLayer {\n width: 80%;\n left: 10px;\n right: auto;\n }\n}\n"],"mappings":"AAAA,CAAC,OACC,QAAS,KACT,YAAa,OACb,gBAAiB,OACjB,MAAO,KACP,YAAa,GACf,CCNA,CAAC,YACC,SAAU,SACV,OAAQ,KACR,KAAM,QACN,WAAY,KACZ,OAAQ,YAAY,EAAE,IAAI,IAAI,KAAK,CAAC,CAAE,CAAC,CAAE,CAAC,CAAE,MAAO,YAAY,EAAE,IAAI,IAAI,KAAK,CAAC,CAAE,CAAC,CAAE,CAAC,CAAE,MACvF,WAAY,MANd,cAOiB,OAPjB,QAQW,KACX,CAEA,CAAC,UACC,MAAO,KACP,SAAU,MACZ,CCdA,CAAC,WACC,SAAU,SACV,KAAM,KACN,MAAO,KACP,OAAQ,KACR,MAAO,IACP,OAAQ,MACR,WAAY,UAPd,cAQiB,KARjB,QASW,IACT,WAAY,EAAE,KAAK,KAAK,UACxB,QAAS,KACT,eAAgB,OAChB,IAAK,IACL,eAAgB,IAClB,CAEA,CAAC,UACC,KAAM,EACN,WAAY,CACd,CAEA,OAAO,YAAc,UACnB,CAvBD,WAwBG,MAAO,IACP,KAAM,KACN,MAAO,IACT,CACF","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
interface ThemeColors {
|
|
6
|
+
primary: string;
|
|
7
|
+
primaryMuted: string;
|
|
8
|
+
accent: string;
|
|
9
|
+
surface: string;
|
|
10
|
+
}
|
|
11
|
+
interface ThemeMarker {
|
|
12
|
+
outer: string;
|
|
13
|
+
inner: string;
|
|
14
|
+
startInner: string;
|
|
15
|
+
finishInner: string;
|
|
16
|
+
}
|
|
17
|
+
interface ThemeDots {
|
|
18
|
+
mapActive: string;
|
|
19
|
+
chart: string;
|
|
20
|
+
chartActive: string;
|
|
21
|
+
}
|
|
22
|
+
interface ThemeMap {
|
|
23
|
+
strokeWeight: number;
|
|
24
|
+
markerSize: number;
|
|
25
|
+
markerLabelFontSize: number;
|
|
26
|
+
markerLabelFontWeight: string;
|
|
27
|
+
hoverMarkerScale: number;
|
|
28
|
+
}
|
|
29
|
+
interface ThemeChart {
|
|
30
|
+
margin: {
|
|
31
|
+
top: number;
|
|
32
|
+
right: number;
|
|
33
|
+
bottom: number;
|
|
34
|
+
left: number;
|
|
35
|
+
};
|
|
36
|
+
gridStroke: string;
|
|
37
|
+
gridDasharray: string;
|
|
38
|
+
axisStroke: string;
|
|
39
|
+
cursorStroke: string;
|
|
40
|
+
cursorStrokeWidth: number;
|
|
41
|
+
yAxisWidth: number;
|
|
42
|
+
lineStrokeWidth: number;
|
|
43
|
+
dotRadius: number;
|
|
44
|
+
dotOpacity: number;
|
|
45
|
+
activeDotRadius: number;
|
|
46
|
+
referenceDotRadius: number;
|
|
47
|
+
referenceLineOpacity: number;
|
|
48
|
+
gradientStartOpacity: number;
|
|
49
|
+
gradientEndOpacity: number;
|
|
50
|
+
xTickFontSize: number;
|
|
51
|
+
xTickDy: number;
|
|
52
|
+
xTickUnitFontSize: number;
|
|
53
|
+
xTickUnitDx: number;
|
|
54
|
+
yTickFontSize: number;
|
|
55
|
+
yTickDy: number;
|
|
56
|
+
yTickUnitFontSize: number;
|
|
57
|
+
yTickUnitDx: number;
|
|
58
|
+
}
|
|
59
|
+
interface ThemeTooltip {
|
|
60
|
+
background: string;
|
|
61
|
+
textColor: string;
|
|
62
|
+
padding: string;
|
|
63
|
+
borderRadius: number;
|
|
64
|
+
}
|
|
65
|
+
interface ThemeMarkerShape {
|
|
66
|
+
size: number;
|
|
67
|
+
lift: number;
|
|
68
|
+
text: {
|
|
69
|
+
fontSize: number;
|
|
70
|
+
fontWeight: number;
|
|
71
|
+
letterSpacing: number;
|
|
72
|
+
xOffset: number;
|
|
73
|
+
lineHeight: number;
|
|
74
|
+
startLiftPerWord: number;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
interface Theme {
|
|
78
|
+
colors: ThemeColors;
|
|
79
|
+
marker: ThemeMarker;
|
|
80
|
+
dots: ThemeDots;
|
|
81
|
+
map: ThemeMap;
|
|
82
|
+
chart: ThemeChart;
|
|
83
|
+
tooltip: ThemeTooltip;
|
|
84
|
+
markerShape: ThemeMarkerShape & {
|
|
85
|
+
text: ThemeMarkerShape["text"];
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
interface PartialTheme {
|
|
89
|
+
colors?: Partial<ThemeColors>;
|
|
90
|
+
marker?: Partial<ThemeMarker>;
|
|
91
|
+
dots?: Partial<ThemeDots>;
|
|
92
|
+
map?: Partial<ThemeMap>;
|
|
93
|
+
chart?: Partial<ThemeChart>;
|
|
94
|
+
tooltip?: Partial<ThemeTooltip>;
|
|
95
|
+
markerShape?: Partial<ThemeMarkerShape> & {
|
|
96
|
+
text?: Partial<ThemeMarkerShape["text"]>;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
declare const theme: Theme;
|
|
100
|
+
|
|
101
|
+
interface RouteConfig {
|
|
102
|
+
id: string;
|
|
103
|
+
name: string;
|
|
104
|
+
center: {
|
|
105
|
+
lat: number;
|
|
106
|
+
lng: number;
|
|
107
|
+
};
|
|
108
|
+
zoomHorizontal?: number;
|
|
109
|
+
zoomVertical?: number;
|
|
110
|
+
geoJson: object;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface RouteMapProps {
|
|
114
|
+
apiKey: string;
|
|
115
|
+
route: RouteConfig;
|
|
116
|
+
height?: number | string;
|
|
117
|
+
className?: string;
|
|
118
|
+
style?: CSSProperties;
|
|
119
|
+
theme?: PartialTheme;
|
|
120
|
+
}
|
|
121
|
+
declare const RouteMap: ({ apiKey, route, height, className, style, theme, }: RouteMapProps) => react_jsx_runtime.JSX.Element;
|
|
122
|
+
|
|
123
|
+
declare const useMapHeader: () => {
|
|
124
|
+
refHeader: (instance: Element | null) => void | react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES];
|
|
125
|
+
refMapContainer: (instance: Element | null) => void | react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES];
|
|
126
|
+
targetHeaderFraction: number;
|
|
127
|
+
effectiveHeaderHeight: number;
|
|
128
|
+
mapHeight: string | number;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
declare const ThemeProvider: ({ theme, children, }: {
|
|
132
|
+
theme?: PartialTheme;
|
|
133
|
+
children: ReactNode;
|
|
134
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
135
|
+
declare const useTheme: () => Theme;
|
|
136
|
+
|
|
137
|
+
export { type RouteConfig, RouteMap, type RouteMapProps, type Theme, ThemeProvider, theme, useMapHeader, useTheme };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
interface ThemeColors {
|
|
6
|
+
primary: string;
|
|
7
|
+
primaryMuted: string;
|
|
8
|
+
accent: string;
|
|
9
|
+
surface: string;
|
|
10
|
+
}
|
|
11
|
+
interface ThemeMarker {
|
|
12
|
+
outer: string;
|
|
13
|
+
inner: string;
|
|
14
|
+
startInner: string;
|
|
15
|
+
finishInner: string;
|
|
16
|
+
}
|
|
17
|
+
interface ThemeDots {
|
|
18
|
+
mapActive: string;
|
|
19
|
+
chart: string;
|
|
20
|
+
chartActive: string;
|
|
21
|
+
}
|
|
22
|
+
interface ThemeMap {
|
|
23
|
+
strokeWeight: number;
|
|
24
|
+
markerSize: number;
|
|
25
|
+
markerLabelFontSize: number;
|
|
26
|
+
markerLabelFontWeight: string;
|
|
27
|
+
hoverMarkerScale: number;
|
|
28
|
+
}
|
|
29
|
+
interface ThemeChart {
|
|
30
|
+
margin: {
|
|
31
|
+
top: number;
|
|
32
|
+
right: number;
|
|
33
|
+
bottom: number;
|
|
34
|
+
left: number;
|
|
35
|
+
};
|
|
36
|
+
gridStroke: string;
|
|
37
|
+
gridDasharray: string;
|
|
38
|
+
axisStroke: string;
|
|
39
|
+
cursorStroke: string;
|
|
40
|
+
cursorStrokeWidth: number;
|
|
41
|
+
yAxisWidth: number;
|
|
42
|
+
lineStrokeWidth: number;
|
|
43
|
+
dotRadius: number;
|
|
44
|
+
dotOpacity: number;
|
|
45
|
+
activeDotRadius: number;
|
|
46
|
+
referenceDotRadius: number;
|
|
47
|
+
referenceLineOpacity: number;
|
|
48
|
+
gradientStartOpacity: number;
|
|
49
|
+
gradientEndOpacity: number;
|
|
50
|
+
xTickFontSize: number;
|
|
51
|
+
xTickDy: number;
|
|
52
|
+
xTickUnitFontSize: number;
|
|
53
|
+
xTickUnitDx: number;
|
|
54
|
+
yTickFontSize: number;
|
|
55
|
+
yTickDy: number;
|
|
56
|
+
yTickUnitFontSize: number;
|
|
57
|
+
yTickUnitDx: number;
|
|
58
|
+
}
|
|
59
|
+
interface ThemeTooltip {
|
|
60
|
+
background: string;
|
|
61
|
+
textColor: string;
|
|
62
|
+
padding: string;
|
|
63
|
+
borderRadius: number;
|
|
64
|
+
}
|
|
65
|
+
interface ThemeMarkerShape {
|
|
66
|
+
size: number;
|
|
67
|
+
lift: number;
|
|
68
|
+
text: {
|
|
69
|
+
fontSize: number;
|
|
70
|
+
fontWeight: number;
|
|
71
|
+
letterSpacing: number;
|
|
72
|
+
xOffset: number;
|
|
73
|
+
lineHeight: number;
|
|
74
|
+
startLiftPerWord: number;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
interface Theme {
|
|
78
|
+
colors: ThemeColors;
|
|
79
|
+
marker: ThemeMarker;
|
|
80
|
+
dots: ThemeDots;
|
|
81
|
+
map: ThemeMap;
|
|
82
|
+
chart: ThemeChart;
|
|
83
|
+
tooltip: ThemeTooltip;
|
|
84
|
+
markerShape: ThemeMarkerShape & {
|
|
85
|
+
text: ThemeMarkerShape["text"];
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
interface PartialTheme {
|
|
89
|
+
colors?: Partial<ThemeColors>;
|
|
90
|
+
marker?: Partial<ThemeMarker>;
|
|
91
|
+
dots?: Partial<ThemeDots>;
|
|
92
|
+
map?: Partial<ThemeMap>;
|
|
93
|
+
chart?: Partial<ThemeChart>;
|
|
94
|
+
tooltip?: Partial<ThemeTooltip>;
|
|
95
|
+
markerShape?: Partial<ThemeMarkerShape> & {
|
|
96
|
+
text?: Partial<ThemeMarkerShape["text"]>;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
declare const theme: Theme;
|
|
100
|
+
|
|
101
|
+
interface RouteConfig {
|
|
102
|
+
id: string;
|
|
103
|
+
name: string;
|
|
104
|
+
center: {
|
|
105
|
+
lat: number;
|
|
106
|
+
lng: number;
|
|
107
|
+
};
|
|
108
|
+
zoomHorizontal?: number;
|
|
109
|
+
zoomVertical?: number;
|
|
110
|
+
geoJson: object;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface RouteMapProps {
|
|
114
|
+
apiKey: string;
|
|
115
|
+
route: RouteConfig;
|
|
116
|
+
height?: number | string;
|
|
117
|
+
className?: string;
|
|
118
|
+
style?: CSSProperties;
|
|
119
|
+
theme?: PartialTheme;
|
|
120
|
+
}
|
|
121
|
+
declare const RouteMap: ({ apiKey, route, height, className, style, theme, }: RouteMapProps) => react_jsx_runtime.JSX.Element;
|
|
122
|
+
|
|
123
|
+
declare const useMapHeader: () => {
|
|
124
|
+
refHeader: (instance: Element | null) => void | react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES];
|
|
125
|
+
refMapContainer: (instance: Element | null) => void | react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof react.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES];
|
|
126
|
+
targetHeaderFraction: number;
|
|
127
|
+
effectiveHeaderHeight: number;
|
|
128
|
+
mapHeight: string | number;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
declare const ThemeProvider: ({ theme, children, }: {
|
|
132
|
+
theme?: PartialTheme;
|
|
133
|
+
children: ReactNode;
|
|
134
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
135
|
+
declare const useTheme: () => Theme;
|
|
136
|
+
|
|
137
|
+
export { type RouteConfig, RouteMap, type RouteMapProps, type Theme, ThemeProvider, theme, useMapHeader, useTheme };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {Status,Wrapper}from'@googlemaps/react-wrapper';import*as S from'react';import {createContext,useContext,useMemo,useState,useEffect,useCallback,useRef,useId}from'react';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {ResponsiveContainer,ComposedChart,CartesianGrid,XAxis,YAxis,Tooltip,Line,ReferenceDot,ReferenceLine}from'recharts';var _=()=>typeof window>"u"?true:window.innerWidth>=window.innerHeight,L=()=>{let[e,r]=useState(_);return useEffect(()=>{let o=()=>r(_());return window.addEventListener("resize",o),()=>window.removeEventListener("resize",o)},[]),{isHorizontal:e,isVertical:!e}};var g={colors:{primary:"rgba(14, 165, 233, 1)",primaryMuted:"rgba(14, 165, 233, 0.7)",accent:"rgba(132, 204, 22, 1)",surface:"rgba(248, 250, 252, 1)"},marker:{outer:"rgba(132, 204, 22, 1)",inner:"rgba(248, 250, 252, 1)",startInner:"rgba(34, 197, 94, 1)",finishInner:"rgba(239, 68, 68, 1)"},dots:{mapActive:"rgba(132, 204, 22, 1)",chart:"rgba(132, 204, 22, 1)",chartActive:"rgba(132, 204, 22, 1)"},map:{strokeWeight:10,markerSize:50,markerLabelFontSize:20,markerLabelFontWeight:"bold",hoverMarkerScale:6},chart:{margin:{top:4,right:8,bottom:4,left:8},gridStroke:"rgba(255,255,255,0.08)",gridDasharray:"3 3",axisStroke:"rgba(226, 232, 240, 0.7)",cursorStroke:"rgba(226,232,240,0.4)",cursorStrokeWidth:1,yAxisWidth:60,lineStrokeWidth:1,dotRadius:3,dotOpacity:.9,activeDotRadius:3,referenceDotRadius:7,referenceLineOpacity:.5,gradientStartOpacity:.6,gradientEndOpacity:.1,xTickFontSize:12,xTickDy:12,xTickUnitFontSize:10,xTickUnitDx:2,yTickFontSize:12,yTickDy:4,yTickUnitFontSize:10,yTickUnitDx:2},tooltip:{background:"rgba(15,23,42,0.9)",textColor:"#e2e8f0",padding:"6px 8px",borderRadius:6},markerShape:{size:33,lift:20,text:{fontSize:12,fontWeight:300,letterSpacing:2,xOffset:15,lineHeight:12,startLiftPerWord:10}}};var U=createContext(g),Ie=e=>({colors:{...g.colors,...e?.colors??{}},marker:{...g.marker,...e?.marker??{}},dots:{...g.dots,...e?.dots??{}},map:{...g.map,...e?.map??{}},chart:{...g.chart,...e?.chart??{}},tooltip:{...g.tooltip,...e?.tooltip??{}},markerShape:{...g.markerShape,...e?.markerShape??{},text:{...g.markerShape.text,...e?.markerShape?.text??{}}}}),M=({theme:e,children:r})=>{let o=useMemo(()=>Ie(e),[e]);return jsx(U.Provider,{value:o,children:r})},u=()=>useContext(U);var B={};var He=({message:e="Loading map...",height:r="100dvh"})=>{let o=u(),t={height:r,background:o.colors.surface,color:o.colors.primary};return jsx("div",{className:B.loader,style:t,children:e})},Y=He;var V=e=>{let{x:r,y:o,payload:t}=e,n=u(),i=Math.round((t?.value??0)/1e3);return jsxs("text",{x:r,y:o,fill:n.chart.axisStroke,fontSize:n.chart.xTickFontSize,textAnchor:"middle",dy:n.chart.xTickDy,children:[jsx("tspan",{children:i}),jsx("tspan",{fontSize:n.chart.xTickUnitFontSize,dx:n.chart.xTickUnitDx,children:"km"})]})};var J=createContext(void 0),$=({children:e})=>{let[r,o]=useState({}),t=useCallback(a=>o(a),[]),n=useCallback(()=>o({}),[]),i=useMemo(()=>({hover:r,setHover:t,clearHover:n}),[r,t,n]);return jsx(J.Provider,{value:i,children:e})},P=()=>{let e=useContext(J);if(!e)throw new Error("useHover must be used within HoverProvider");return e};var K=e=>{let{cx:r,cy:o,fill:t}=e,n=u();return r===void 0||o===void 0?null:jsx("circle",{cx:r,cy:o,r:n.chart.dotRadius,fill:t,opacity:n.chart.dotOpacity})},Z=e=>{let r=u();return jsx(K,{...e,fill:r.dots.chart})},Q=e=>{let r=u();return jsx(K,{...e,fill:r.dots.mapActive})};var j=e=>{let{x:r,y:o,payload:t}=e,n=u(),i=Math.round(t?.value??0);return jsxs("text",{x:r,y:o,fill:n.chart.axisStroke,fontSize:n.chart.yTickFontSize,textAnchor:"end",dy:n.chart.yTickDy,children:[jsx("tspan",{children:i}),jsx("tspan",{fontSize:n.chart.yTickUnitFontSize,dx:n.chart.yTickUnitDx,children:"m"})]})};var Xe=300,ee=({active:e,payload:r,label:o,accent:t,primary:n,markers:i})=>{let{tooltip:a}=u();if(!e||!r?.length)return null;let s=r[0]?.payload,c=i.find(p=>Math.abs((p?.distance??-1)-(s?.distance??0))<=Xe),d=Math.trunc(o/1e3),l=Math.round(o%1e3);return jsxs("div",{style:{background:a.background,border:"none",color:a.textColor,padding:a.padding,borderRadius:a.borderRadius},children:[jsxs("div",{style:{fontWeight:600,color:n},children:[d," km ",l," m"]}),jsxs("div",{children:["Elevation: ",jsxs("strong",{children:[Math.round(s?.elevation??0)," m"]})]}),c?.name?jsx("div",{style:{color:t,fontWeight:600},children:c.name}):null]})};var te=`<svg stroke="currentColor" fill="#84CC16" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true" height="200px" width="200px" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
|
3
|
+
<path fill="white" stroke-linecap="round" stroke-linejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
4
|
+
</svg>
|
|
5
|
+
`;var Je=(e,r,o)=>{let t=0;return e.replace(/fill="[^"]*"/g,n=>(t+=1,t===1?`fill="${r}"`:t===2?`fill="${o}"`:n))},b=(e,r)=>{let o=Je(te,e,r).trim();return `data:image/svg+xml,${encodeURIComponent(o).replace(/'/g,"%27").replace(/"/g,"%22")}`};var re=e=>{let{cx:r,cy:o,fill:t,name:n}=e,i=u(),a=i.markerShape,s=a.size,c=useMemo(()=>b(i.marker.outer,i.marker.inner),[i.marker.inner,i.marker.outer]);if(r===void 0||o===void 0)return null;let d=typeof n=="string"?n.split(/\s+/).map(l=>l.trim()).filter(Boolean):[];return jsxs("g",{children:[jsx(Q,{cx:r,cy:o}),jsx("image",{x:r-s/2,y:o-s/2-a.lift,width:s,height:s,href:c}),n?jsx(Ze,{words:d,cx:r,cy:o,fill:t}):null]})},Ze=({words:e,cx:r,cy:o,fill:t})=>{let i=u().markerShape.text;return jsx("text",{y:o-e.length*i.startLiftPerWord,fill:t||"#fff",fontSize:i.fontSize,fontWeight:i.fontWeight,letterSpacing:i.letterSpacing,style:{userSelect:"none"},children:e.map((a,s)=>jsx(Qe,{word:a,index:s,cx:r},a))})},Qe=({word:e,index:r,cx:o})=>{let n=u().markerShape.text,i=useId();return jsx("tspan",{x:o+n.xOffset,dy:r===0?0:n.lineHeight,children:e},i)};function oe(e){let[r,o]=useState(null),t=useMemo(()=>r!=null?e[r]:null,[r,e]),n=useCallback(()=>{o(null);},[]),i=useCallback(a=>{if(!e.length)return;let s=0,c=e.length-1;for(;s<c;){let l=Math.floor((s+c)/2);e[l].distance<a?s=l+1:c=l;}let d=s;if(s>0){let l=e[s-1],p=e[s];Math.abs(l.distance-a)<Math.abs(p.distance-a)&&(d=s-1);}o(d);},[e]);return {activeIndex:r,activePoint:t,triggerByXValue:i,clearActiveIndex:n}}var ie=e=>e.length?e[e.length-1].distance:0,ae=(e,r=2e3)=>{let o=[];for(let t=0;t<=e;t+=r)o.push(t);return o[o.length-1]<e&&o.push(e),o},se=e=>[...e.geoJson?.features?.find(n=>n?.geometry?.type==="LineString"&&n?.properties?.elevationProfile?.points)?.properties?.elevationProfile?.points||[]].sort((n,i)=>(n?.distance??0)-(i?.distance??0)),ce=e=>(e.geoJson?.features?.find(t=>t?.geometry?.type==="LineString"&&t?.geometry?.coordinates)).geometry.coordinates,ue=e=>{if(!e.length)return [0,0];let r=e.map(i=>i.elevation),o=Math.min(...r),t=Math.max(...r),n=Math.max(10,(t-o)*.05);return [Math.floor(o-n),Math.ceil(t+n)]},le=e=>{let[r,o]=e,t=(o-r)*1.2,n=Math.max(10,Math.round(t/6/10)*10||50),i=Math.floor((r-(t-(o-r))/2)/n)*n,a=Math.ceil((o+(t-(o-r))/2)/n)*n,s=[];for(let c=i;c<=a+n/2;c+=n)s.push(c);return [i,a,s]},de=(e,r)=>{if(!e?.length)return [];let t=(r?.features??[]).filter(i=>i?.geometry?.type==="Point"),n=[];return t.forEach(i=>{let a=i?.geometry?.coordinates;if(!Array.isArray(a)||a.length<2)return;let[s,c]=a;if(!Number.isFinite(c)||!Number.isFinite(s))return;let d=e.reduce((l,p)=>{let f=Math.pow(p.lat-c,2)+Math.pow(p.lng-s,2);return f<l.dist?{point:p,dist:f}:l},{point:null,dist:Number.POSITIVE_INFINITY});if(d.point){if(d.point.distance===0)return;n.push({distance:d.point.distance,elevation:d.point.elevation,name:i?.properties?.name});}}),n.sort((i,a)=>(i.distance??0)-(a.distance??0))},me=(e,r)=>{if(!e.length)return null;let o=null,t=Number.POSITIVE_INFINITY;return e.forEach(([n,i])=>{let a=(i-r.lat)**2+(n-r.lng)**2;a<t&&(t=a,o={lat:i,lng:n});}),o},pe=(e,r)=>{if(!e.length)return null;let o=null,t=Number.POSITIVE_INFINITY;return e.forEach(n=>{let i=(n.lat-r[0])**2+(n.lng-r[1])**2;i<t&&(t=i,o=n);}),o};var fe=(e,r,o=1e3)=>r&&Math.abs((r.distance??0)-(e.distance??0))<o;var A=({route:e})=>{let{hover:r,setHover:o}=P(),t=u(),n=useMemo(()=>se(e),[e]),i=useMemo(()=>de(n,e.geoJson),[n,e.geoJson]),a=ie(n),s=ae(a),[c,d]=ue(n),[l,p,f]=useMemo(()=>le([c,d]),[c,d]),{activeIndex:C,activePoint:y,triggerByXValue:x,clearActiveIndex:w}=oe(n);return useEffect(()=>{if(r.source==="chart"||!r.lat||!r.lng)return;let h=pe(n,[r.lat,r.lng]);h&&x(h.distance);},[r,n,x]),n.length?jsx(ResponsiveContainer,{width:"100%",height:"100%",style:{userSelect:"none"},children:jsxs(ComposedChart,{data:n,margin:t.chart.margin,onMouseMove:({activePayload:h})=>{C&&w();let E=h?.[0];if(!E)return;let{lat:k,lng:T}=E.payload;o({lat:k,lng:T,source:"chart"});},onMouseEnter:()=>w(),children:[jsx("defs",{children:jsxs("linearGradient",{id:"elevationGradient",x1:"0",y1:"0",x2:"0",y2:"1",children:[jsx("stop",{offset:"0%",stopColor:t.colors.primary,stopOpacity:t.chart.gradientStartOpacity}),jsx("stop",{offset:"100%",stopColor:t.colors.primaryMuted,stopOpacity:t.chart.gradientEndOpacity})]})}),jsx(CartesianGrid,{stroke:t.chart.gridStroke,strokeDasharray:t.chart.gridDasharray}),jsx(XAxis,{dataKey:"distance",type:"number",domain:[0,a],ticks:s,tick:jsx(V,{}),stroke:t.chart.axisStroke}),jsx(YAxis,{dataKey:"elevation",tick:jsx(j,{}),domain:[l,p],ticks:f,stroke:t.chart.axisStroke,width:t.chart.yAxisWidth}),jsx(Tooltip,{cursor:{stroke:t.chart.cursorStroke,strokeWidth:t.chart.cursorStrokeWidth},content:jsx(ee,{accent:t.colors.accent,primary:t.colors.primary,markers:i})}),jsx(Line,{type:"monotone",dataKey:"elevation",stroke:t.colors.primary,strokeWidth:t.chart.lineStrokeWidth,dot:h=>{let{cx:E,cy:k,index:T}=h;return !(T===C)||!y?jsx(Fragment,{}):jsx(Z,{cx:E,cy:k})},activeDot:{r:t.chart.activeDotRadius,fill:t.dots.chartActive,strokeWidth:0},fill:"url(#elevationGradient)",isAnimationActive:false}),i.length>0&&i.map((h,E)=>{let k=fe(i[E],i[E+1]);return jsx(ReferenceDot,{x:h.distance,y:h.elevation,r:t.chart.referenceDotRadius,shape:T=>jsx(re,{...T,name:k?void 0:h.name,fill:t.colors.accent})},`${h.distance}-${E}`)}),y&&jsx(ReferenceLine,{x:y.distance,opacity:t.chart.referenceLineOpacity})]})}):null};var ge={lat:48.9325937,lng:20.3452306},ve=13,ye=12;var Se=.2,we=.15;var H={};var be=({route:e,height:r,isHorizontal:o})=>{let t=u(),{hover:n,setHover:i}=P(),a=useRef(null),s=useRef(null),c=useRef(null),d=useMemo(()=>ce(e),[e]),l=useMemo(()=>({default:b(t.marker.outer,t.marker.inner),start:b(t.marker.outer,t.marker.startInner),finish:b(t.marker.outer,t.marker.finishInner)}),[t.marker]);return useEffect(()=>{if(!a.current||!window.google?.maps)return;let p=o&&(e.zoomHorizontal||ve)||e.zoomVertical||ye,f=new window.google.maps.Map(a.current,{center:e.center||ge,zoom:p,mapTypeId:window.google.maps.MapTypeId.SATELLITE,streetViewControl:false});c.current=f,f.data.setStyle(y=>{let x=y.getProperty("name"),w=y.getProperty("first"),h=y.getProperty("last");return {strokeColor:t.colors.primaryMuted,strokeWeight:t.map.strokeWeight,icon:{url:w?l.start:h?l.finish:l.default,scaledSize:new window.google.maps.Size(t.map.markerSize,t.map.markerSize),optimized:false,zIndex:w||h?100:10,collisionBehavior:window.google?.maps?.CollisionBehavior?.REQUIRED_AND_HIDES_OPTIONAL},label:{className:H.markerLabel,fontSize:`${t.map.markerLabelFontSize}px`,fontWeight:t.map.markerLabelFontWeight,color:t.colors.accent,text:x}}}),f.data.addGeoJson(e.geoJson);let C=f.addListener("mousemove",y=>{let x=y.latLng;if(!x)return;let w=me(d,{lat:x.lat(),lng:x.lng()});w&&i({lat:w.lat,lng:w.lng,source:"map"});});return ()=>{C.remove(),f.data.forEach(y=>{f.data.remove(y);}),c.current=null;}},[e,o,t,d,i,l]),useEffect(()=>{if(!a.current||!window.google?.maps)return;let p=c.current;if(!p)return;if(!n.lat||!n.lng){s.current&&(s.current.setMap(null),s.current=null);return}let f={path:window.google.maps.SymbolPath.CIRCLE,scale:t.map.hoverMarkerScale,fillColor:t.dots.mapActive,fillOpacity:1,strokeWeight:0};s.current?s.current.setIcon(f):s.current=new window.google.maps.Marker({map:p,icon:f}),s.current.setPosition({lat:n.lat,lng:n.lng}),s.current.setMap(p);},[n,t]),jsx("div",{ref:a,className:H.mapCanvas,style:{height:r}})};var z={};var vt={apiKey:"Oops! Cannot display the map: Google Maps API key missing",[Status.FAILURE]:"Unable to load Google Maps API. Check your API key or network.",[Status.LOADING]:void 0,[Status.SUCCESS]:void 0},Re=({type:e,height:r})=>jsx("div",{style:{height:r},children:jsx(Y,{message:vt[e],height:r})}),yt=(e,r)=>jsx(Re,{type:e,height:r}),ke=({apiKey:e,route:r,height:o="100dvh",className:t,style:n,theme:i=g})=>{let{isHorizontal:a}=L();if(!e)return jsx(M,{theme:i,children:jsx(Re,{type:"apiKey",height:o})});let s={height:o,width:"100%",...n};return jsx(M,{theme:i,children:jsx($,{children:jsxs("div",{className:t,style:s,children:[jsx(Wrapper,{apiKey:e,render:c=>yt(c,o),children:jsx(be,{route:r,height:o,isHorizontal:a})}),jsx("div",{className:z.chartLayer,children:jsx("div",{className:z.chartBody,children:jsx(A,{route:r})})})]})})})};function W(){let[e,r]=S.useState({width:null,height:null}),o=S.useRef(null);return [S.useCallback(n=>{if(o.current&&(o.current.disconnect(),o.current=null),n?.nodeType===Node.ELEMENT_NODE){let i=new ResizeObserver(([a])=>{if(a&&a.borderBoxSize){let{inlineSize:s,blockSize:c}=a.borderBoxSize[0];r({width:s,height:c});}});i.observe(n),o.current=i;}},[]),e]}var Te=100,wt=()=>{let{isVertical:e}=L(),[r,{height:o}]=W(),[t,{height:n}]=W(),i=e?Se:we,s=(typeof window<"u"?window.innerHeight:Te/i)*i||Te,c=o||s,d=`calc(100dvh - ${c}px)`;return {refHeader:r,refMapContainer:t,targetHeaderFraction:i,effectiveHeaderHeight:c,mapHeight:n||d}};export{ke as RouteMap,M as ThemeProvider,g as theme,wt as useMapHeader,u as useTheme};//# sourceMappingURL=index.js.map
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useOrientation.ts","../src/theme.ts","../src/theme-provider.tsx","../src/components/Loader.module.css","../src/components/Loader.tsx","../src/components/RouteMap/ElevationChart/DistanceTick.tsx","../src/components/RouteMap/HoverContext.tsx","../src/components/RouteMap/ElevationChart/ElevationDot.tsx","../src/components/RouteMap/ElevationChart/ElevationTick.tsx","../src/components/RouteMap/ElevationChart/ElevationTooltip.tsx","../src/assets/icons/marker.svg","../src/components/icons/buildMarkerIcon.ts","../src/components/RouteMap/ElevationChart/MarkerShape.tsx","../src/components/RouteMap/ElevationChart/useTriggerByXValue.tsx","../src/components/RouteMap/ElevationChart/utils.ts","../src/components/RouteMap/ElevationChart/ElevationChart.tsx","../src/constants.ts","../src/components/RouteMap/GoogleMapCanvas.module.css","../src/components/RouteMap/GoogleMapCanvas.tsx","../src/components/RouteMap/RouteMap.module.css","../src/components/RouteMap/RouteMap.tsx","../node_modules/@uidotdev/usehooks/index.js","../src/hooks/useMapHeader.ts"],"names":["getIsLandscape","useOrientation","isHorizontal","setIsHorizontal","useState","useEffect","handleResize","theme","ThemeContext","createContext","mergeTheme","override","ThemeProvider","children","mergedTheme","useMemo","jsx","useTheme","useContext","Loader_default","Loader","message","height","style","DistanceTick","props","x","y","payload","km","jsxs","HoverContext","HoverProvider","hover","setHoverState","setHover","useCallback","state","clearHover","value","useHover","ctx","Dot","cx","cy","fill","ElevationDot","MapDot","ElevationTick","m","MARKER_DISTANCE_TOLERANCE_MATCH","ElevationTooltip","active","label","accent","primary","markers","tooltip","point","marker","marker_default","svgTemplate","base","outer","inner","fillIndex","match","buildMarkerIcon","svg","MarkerShape","name","layout","size","iconHref","words","w","Text","text","word","index","Word","id","useId","useTriggerByXValue","data","activeIndex","setActiveIndex","activePoint","clearActiveIndex","triggerByXValue","distance","lo","hi","mid","bestIndex","prev","curr","getMaxDistance","points","getTicksForDistance","maxDistance","step","ticks","d","getPointsWithElevation","route","f","a","b","getAllPoints","computeMinMax","elevations","p","min","max","pad","computeRoundedDomainAndTicks","minMax","paddedRange","graphMin","graphMax","v","computeMarkerPoints","elevationPoints","geoJson","pointFeatures","coords","lng","lat","nearest","acc","findNearestPointByCoordinates","target","closest","minDist","findNearestPoint","isCloseCheck","marker1","marker2","threshold","ElevationChart","minY","maxY","tickVals","nearestPoint","ResponsiveContainer","ComposedChart","activePayload","activePayloadItem","CartesianGrid","XAxis","YAxis","Tooltip","Line","Fragment","idx","tooClose","ReferenceDot","ReferenceLine","DEFAULT_CENTER","DEFAULT_ZOOM_HORIZONTAL","DEFAULT_ZOOM_VERTICAL","RATIO_HEADER_MOBILE","RATIO_HEADER_DESKTOP","GoogleMapCanvas_default","GoogleMapCanvas","ref","useRef","highlightMarkerRef","mapRef","markerIcons","zoom","map","feature","isFirst","isLast","moveListener","e","latLng","mapInstance","icon","RouteMap_default","messages","Status","RenderLoader","type","render","status","RouteMap","apiKey","className","containerStyle","Wrapper","useMeasure","dimensions","setDimensions","previousObserver","node","observer","entry","width","HEADER_FALLBACK_PX","useMapHeader","isVertical","refHeader","headerHeight","refMapContainer","mapContainerHeight","targetHeaderFraction","fallbackHeaderHeightPx","effectiveHeaderHeight","mapHeightStyle"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useState } from \"react\";\n\nconst getIsLandscape = () => {\n if (typeof window === \"undefined\") return true;\n return window.innerWidth >= window.innerHeight;\n};\n\nexport const useOrientation = () => {\n const [isHorizontal, setIsHorizontal] = useState<boolean>(getIsLandscape);\n\n useEffect(() => {\n const handleResize = () => setIsHorizontal(getIsLandscape());\n\n window.addEventListener(\"resize\", handleResize);\n return () => window.removeEventListener(\"resize\", handleResize);\n }, []);\n\n return { isHorizontal, isVertical: !isHorizontal };\n};\n","export interface ThemeColors {\n primary: string;\n primaryMuted: string;\n accent: string;\n surface: string;\n}\n\nexport interface ThemeMarker {\n outer: string;\n inner: string;\n startInner: string;\n finishInner: string;\n}\n\nexport interface ThemeDots {\n mapActive: string;\n chart: string;\n chartActive: string;\n}\n\nexport interface ThemeMap {\n strokeWeight: number;\n markerSize: number;\n markerLabelFontSize: number;\n markerLabelFontWeight: string;\n hoverMarkerScale: number;\n}\n\nexport interface ThemeChart {\n margin: { top: number; right: number; bottom: number; left: number };\n gridStroke: string;\n gridDasharray: string;\n axisStroke: string;\n cursorStroke: string;\n cursorStrokeWidth: number;\n yAxisWidth: number;\n lineStrokeWidth: number;\n dotRadius: number;\n dotOpacity: number;\n activeDotRadius: number;\n referenceDotRadius: number;\n referenceLineOpacity: number;\n gradientStartOpacity: number;\n gradientEndOpacity: number;\n xTickFontSize: number;\n xTickDy: number;\n xTickUnitFontSize: number;\n xTickUnitDx: number;\n yTickFontSize: number;\n yTickDy: number;\n yTickUnitFontSize: number;\n yTickUnitDx: number;\n}\n\nexport interface ThemeTooltip {\n background: string;\n textColor: string;\n padding: string;\n borderRadius: number;\n}\n\nexport interface ThemeMarkerShape {\n size: number;\n lift: number;\n text: {\n fontSize: number;\n fontWeight: number;\n letterSpacing: number;\n xOffset: number;\n lineHeight: number;\n startLiftPerWord: number;\n };\n}\n\nexport interface ThemeShadows {\n map: string;\n}\n\nexport interface Theme {\n colors: ThemeColors;\n marker: ThemeMarker;\n dots: ThemeDots;\n map: ThemeMap;\n chart: ThemeChart;\n tooltip: ThemeTooltip;\n markerShape: ThemeMarkerShape & {\n text: ThemeMarkerShape[\"text\"];\n };\n}\n\nexport interface PartialTheme {\n colors?: Partial<ThemeColors>;\n marker?: Partial<ThemeMarker>;\n dots?: Partial<ThemeDots>;\n map?: Partial<ThemeMap>;\n chart?: Partial<ThemeChart>;\n tooltip?: Partial<ThemeTooltip>;\n markerShape?: Partial<ThemeMarkerShape> & {\n text?: Partial<ThemeMarkerShape[\"text\"]>;\n };\n}\n\nexport const theme: Theme = {\n colors: {\n primary: \"rgba(14, 165, 233, 1)\",\n primaryMuted: \"rgba(14, 165, 233, 0.7)\",\n accent: \"rgba(132, 204, 22, 1)\",\n surface: \"rgba(248, 250, 252, 1)\",\n },\n marker: {\n outer: \"rgba(132, 204, 22, 1)\",\n inner: \"rgba(248, 250, 252, 1)\",\n startInner: \"rgba(34, 197, 94, 1)\",\n finishInner: \"rgba(239, 68, 68, 1)\",\n },\n dots: {\n mapActive: \"rgba(132, 204, 22, 1)\",\n chart: \"rgba(132, 204, 22, 1)\",\n chartActive: \"rgba(132, 204, 22, 1)\",\n },\n map: {\n strokeWeight: 10,\n markerSize: 50,\n markerLabelFontSize: 20,\n markerLabelFontWeight: \"bold\",\n hoverMarkerScale: 6,\n },\n chart: {\n margin: { top: 4, right: 8, bottom: 4, left: 8 },\n gridStroke: \"rgba(255,255,255,0.08)\",\n gridDasharray: \"3 3\",\n axisStroke: \"rgba(226, 232, 240, 0.7)\",\n cursorStroke: \"rgba(226,232,240,0.4)\",\n cursorStrokeWidth: 1,\n yAxisWidth: 60,\n lineStrokeWidth: 1,\n dotRadius: 3,\n dotOpacity: 0.9,\n activeDotRadius: 3,\n referenceDotRadius: 7,\n referenceLineOpacity: 0.5,\n gradientStartOpacity: 0.6,\n gradientEndOpacity: 0.1,\n xTickFontSize: 12,\n xTickDy: 12,\n xTickUnitFontSize: 10,\n xTickUnitDx: 2,\n yTickFontSize: 12,\n yTickDy: 4,\n yTickUnitFontSize: 10,\n yTickUnitDx: 2,\n },\n tooltip: {\n background: \"rgba(15,23,42,0.9)\",\n textColor: \"#e2e8f0\",\n padding: \"6px 8px\",\n borderRadius: 6,\n },\n markerShape: {\n size: 33,\n lift: 20,\n text: {\n fontSize: 12,\n fontWeight: 300,\n letterSpacing: 2,\n xOffset: 15,\n lineHeight: 12,\n startLiftPerWord: 10,\n },\n },\n};\n","import { createContext, type ReactNode, useContext, useMemo } from \"react\";\nimport { theme as defaultTheme, PartialTheme, type Theme } from \"./theme\";\n\nconst ThemeContext = createContext<Theme>(defaultTheme);\n\nconst mergeTheme = (override?: PartialTheme): Theme => ({\n colors: {\n ...defaultTheme.colors,\n ...(override?.colors ?? {}),\n },\n marker: {\n ...defaultTheme.marker,\n ...(override?.marker ?? {}),\n },\n dots: {\n ...defaultTheme.dots,\n ...(override?.dots ?? {}),\n },\n map: {\n ...defaultTheme.map,\n ...(override?.map ?? {}),\n },\n chart: {\n ...defaultTheme.chart,\n ...(override?.chart ?? {}),\n },\n tooltip: {\n ...defaultTheme.tooltip,\n ...(override?.tooltip ?? {}),\n },\n markerShape: {\n ...defaultTheme.markerShape,\n ...(override?.markerShape ?? {}),\n text: {\n ...defaultTheme.markerShape.text,\n ...(override?.markerShape?.text ?? {}),\n },\n },\n});\n\nexport const ThemeProvider = ({\n theme,\n children,\n}: {\n theme?: PartialTheme;\n children: ReactNode;\n}) => {\n const mergedTheme = useMemo(() => mergeTheme(theme), [theme]);\n return (\n <ThemeContext.Provider value={mergedTheme}>\n {children}\n </ThemeContext.Provider>\n );\n};\n\nexport const useTheme = () => useContext(ThemeContext);\n",".loader {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n font-weight: 600;\n}\n","import type { CSSProperties } from \"react\";\nimport { useTheme } from \"../theme-provider\";\nimport styles from \"./Loader.module.css\";\n\ninterface LoaderProps {\n message?: string;\n height?: number | string;\n}\n\nconst Loader = ({\n message = \"Loading map...\",\n height = \"100dvh\",\n}: LoaderProps) => {\n const theme = useTheme();\n\n const style: CSSProperties = {\n height,\n background: theme.colors.surface,\n color: theme.colors.primary,\n };\n\n return (\n <div className={styles.loader} style={style}>\n {message}\n </div>\n );\n};\n\nexport default Loader;\n","import { useTheme } from \"../../../theme-provider\";\n\nexport const DistanceTick = (props: any) => {\n const { x, y, payload } = props;\n const theme = useTheme();\n const km = Math.round((payload?.value ?? 0) / 1000);\n return (\n <text\n x={x}\n y={y}\n fill={theme.chart.axisStroke}\n fontSize={theme.chart.xTickFontSize}\n textAnchor=\"middle\"\n dy={theme.chart.xTickDy}\n >\n <tspan>{km}</tspan>\n <tspan\n fontSize={theme.chart.xTickUnitFontSize}\n dx={theme.chart.xTickUnitDx}\n >\n km\n </tspan>\n </text>\n );\n};\n","import {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n useState,\n} from \"react\";\n\nexport enum HoverStateChangeSource {\n Chart = \"chart\",\n Map = \"map\",\n}\nexport interface HoverState {\n lat?: number;\n lng?: number;\n source?: HoverStateChangeSource;\n}\n\ninterface HoverContextValue {\n hover: HoverState;\n setHover: (state: HoverState) => void;\n clearHover: () => void;\n}\n\nconst HoverContext = createContext<HoverContextValue | undefined>(undefined);\n\nexport const HoverProvider = ({ children }: { children: ReactNode }) => {\n const [hover, setHoverState] = useState<HoverState>({});\n\n const setHover = useCallback((state: HoverState) => setHoverState(state), []);\n const clearHover = useCallback(() => setHoverState({}), []);\n\n const value = useMemo(\n () => ({\n hover,\n setHover,\n clearHover,\n }),\n [hover, setHover, clearHover]\n );\n\n return (\n <HoverContext.Provider value={value}>{children}</HoverContext.Provider>\n );\n};\n\nexport const useHover = () => {\n const ctx = useContext(HoverContext);\n if (!ctx) {\n throw new Error(\"useHover must be used within HoverProvider\");\n }\n return ctx;\n};\n","import { useTheme } from \"../../../theme-provider\";\n\nconst Dot = (props: any) => {\n const { cx, cy, fill } = props;\n const theme = useTheme();\n if (cx === undefined || cy === undefined) return null;\n return (\n <circle\n cx={cx}\n cy={cy}\n r={theme.chart.dotRadius}\n fill={fill}\n opacity={theme.chart.dotOpacity}\n />\n );\n};\n\nexport const ElevationDot = (props: any) => {\n const theme = useTheme();\n return <Dot {...props} fill={theme.dots.chart} />;\n};\n\nexport const MapDot = (props: any) => {\n const theme = useTheme();\n return <Dot {...props} fill={theme.dots.mapActive} />;\n};\n","import { useTheme } from \"../../../theme-provider\";\n\nexport const ElevationTick = (props: any) => {\n const { x, y, payload } = props;\n const theme = useTheme();\n const m = Math.round(payload?.value ?? 0);\n return (\n <text\n x={x}\n y={y}\n fill={theme.chart.axisStroke}\n fontSize={theme.chart.yTickFontSize}\n textAnchor=\"end\"\n dy={theme.chart.yTickDy}\n >\n <tspan>{m}</tspan>\n <tspan\n fontSize={theme.chart.yTickUnitFontSize}\n dx={theme.chart.yTickUnitDx}\n >\n m\n </tspan>\n </text>\n );\n};\n","import type { TooltipProps } from \"recharts\";\nimport { useTheme } from \"../../../theme-provider\";\n\ninterface ElevationTooltipProps extends TooltipProps<number, string> {\n accent: string;\n primary: string;\n markers: Array<{ distance: number; name?: string }>;\n}\n\nconst MARKER_DISTANCE_TOLERANCE_MATCH = 300; // meters\n\nexport const ElevationTooltip = ({\n active,\n payload,\n label,\n accent,\n primary,\n markers,\n}: ElevationTooltipProps) => {\n const { tooltip } = useTheme();\n\n if (!active || !payload?.length) return null;\n\n const point = payload[0]?.payload;\n const marker = markers.find(\n (m) =>\n Math.abs((m?.distance ?? -1) - (point?.distance ?? 0)) <=\n MARKER_DISTANCE_TOLERANCE_MATCH\n );\n\n const km = Math.trunc((label as number) / 1000);\n const m = Math.round((label as number) % 1000);\n\n return (\n <div\n style={{\n background: tooltip.background,\n border: \"none\",\n color: tooltip.textColor,\n padding: tooltip.padding,\n borderRadius: tooltip.borderRadius,\n }}\n >\n <div style={{ fontWeight: 600, color: primary }}>\n {km} km {m} m\n </div>\n <div>\n Elevation: <strong>{Math.round(point?.elevation ?? 0)} m</strong>\n </div>\n {marker?.name ? (\n <div style={{ color: accent, fontWeight: 600 }}>{marker.name}</div>\n ) : null}\n </div>\n );\n};\n","<svg stroke=\"currentColor\" fill=\"#84CC16\" stroke-width=\"2\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" height=\"200px\" width=\"200px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"></path>\n <path fill=\"white\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"></path>\n</svg>\n","import markerSvg from \"../../assets/icons/marker.svg?raw\";\nimport markerFinishSvg from \"../../assets/icons/markerFinish.svg?raw\";\nimport markerStartSvg from \"../../assets/icons/markerStart.svg?raw\";\n\nconst svgTemplate = (base: string, outer: string, inner: string) => {\n let fillIndex = 0;\n return base.replace(/fill=\"[^\"]*\"/g, (match) => {\n fillIndex += 1;\n if (fillIndex === 1) return `fill=\"${outer}\"`;\n if (fillIndex === 2) return `fill=\"${inner}\"`;\n return match;\n });\n};\n\nexport const buildMarkerIcon = (outer: string, inner: string) => {\n const svg = svgTemplate(markerSvg, outer, inner).trim();\n const encoded = encodeURIComponent(svg)\n .replace(/'/g, \"%27\")\n .replace(/\"/g, \"%22\");\n return `data:image/svg+xml,${encoded}`;\n};\n\nexport const markers = {\n default: markerSvg,\n start: markerStartSvg,\n finish: markerFinishSvg,\n};","import { useId, useMemo } from \"react\";\nimport { useTheme } from \"../../../theme-provider\";\nimport { buildMarkerIcon } from \"../../icons/buildMarkerIcon\";\nimport { MapDot } from \"./ElevationDot\";\n\nexport const MarkerShape = (props: any) => {\n const { cx, cy, fill, name } = props;\n const theme = useTheme();\n const layout = theme.markerShape;\n const size = layout.size;\n\n const iconHref = useMemo(\n () => buildMarkerIcon(theme.marker.outer, theme.marker.inner),\n [theme.marker.inner, theme.marker.outer]\n );\n\n if (cx === undefined || cy === undefined) return null;\n\n const words =\n typeof name === \"string\"\n ? name\n .split(/\\s+/)\n .map((w) => w.trim())\n .filter(Boolean)\n : [];\n return (\n <g>\n <MapDot cx={cx} cy={cy} />\n <image\n x={cx - size / 2}\n y={cy - size / 2 - layout.lift}\n width={size}\n height={size}\n href={iconHref}\n />\n {name ? <Text words={words} cx={cx} cy={cy} fill={fill} /> : null}\n </g>\n );\n};\n\nconst Text = ({\n words,\n cx,\n cy,\n fill,\n}: {\n words: string[];\n cx: number;\n cy: number;\n fill?: string;\n}) => {\n const theme = useTheme();\n const text = theme.markerShape.text;\n\n return (\n <text\n y={cy - words.length * text.startLiftPerWord}\n fill={fill || \"#fff\"}\n fontSize={text.fontSize}\n fontWeight={text.fontWeight}\n letterSpacing={text.letterSpacing}\n style={{ userSelect: \"none\" }}\n >\n {words.map((word, index) => (\n <Word key={word} word={word} index={index} cx={cx} />\n ))}\n </text>\n );\n};\n\nconst Word = ({\n word,\n index,\n cx,\n}: {\n word: string;\n index: number;\n cx: number;\n}) => {\n const theme = useTheme();\n const text = theme.markerShape.text;\n const id = useId();\n return (\n <tspan\n key={id}\n x={cx + text.xOffset}\n dy={index === 0 ? 0 : text.lineHeight}\n >\n {word}\n </tspan>\n );\n};\n","import { useCallback, useMemo, useState } from \"react\";\n\nexport function useTriggerByXValue<\n T extends { distance: number; elevation: number }\n>(data: T[]) {\n const [activeIndex, setActiveIndex] = useState<number | null>(null);\n\n const activePoint = useMemo(\n () => (activeIndex != null ? data[activeIndex] : null),\n [activeIndex, data]\n );\n\n const clearActiveIndex = useCallback(() => {\n setActiveIndex(null);\n }, []);\n\n const triggerByXValue = useCallback(\n (distance: number) => {\n if (!data.length) return;\n\n // assume data sorted by distance ascending\n let lo = 0;\n let hi = data.length - 1;\n\n // binary search for closest distance\n while (lo < hi) {\n const mid = Math.floor((lo + hi) / 2);\n if (data[mid].distance < distance) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n\n // lo is first >= distance; pick closer of lo and lo-1\n let bestIndex = lo;\n if (lo > 0) {\n const prev = data[lo - 1];\n const curr = data[lo];\n if (\n Math.abs(prev.distance - distance) <\n Math.abs(curr.distance - distance)\n ) {\n bestIndex = lo - 1;\n }\n }\n\n setActiveIndex(bestIndex);\n },\n [data]\n );\n\n return {\n activeIndex,\n activePoint,\n triggerByXValue,\n clearActiveIndex,\n };\n}\n","import type { ElevationPoint, Marker } from \"./types\";\n\nexport const getMaxDistance = (points: ElevationPoint[]) =>\n points.length ? points[points.length - 1].distance : 0;\n\nexport const getTicksForDistance = (maxDistance: number, step: number = 2000) => {\n const ticks: number[] = [];\n for (let d = 0; d <= maxDistance; d += step) {\n ticks.push(d);\n }\n if (ticks[ticks.length - 1] < maxDistance) {\n ticks.push(maxDistance);\n }\n return ticks;\n}\n\nexport const getPointsWithElevation = (route: any): ElevationPoint[] => {\n const geo = route.geoJson as any;\n const line = geo?.features?.find(\n (f: any) =>\n f?.geometry?.type === \"LineString\" &&\n f?.properties?.elevationProfile?.points\n );\n const raw = line?.properties?.elevationProfile?.points || [];\n return [...raw].sort(\n (a: ElevationPoint, b: ElevationPoint) => (a?.distance ?? 0) - (b?.distance ?? 0)\n );\n};\n\nexport const getAllPoints = (route: any): [number, number][] => {\n const geo = route.geoJson as any;\n const points = geo?.features?.find(\n (f: any) =>\n f?.geometry?.type === \"LineString\" &&\n f?.geometry?.coordinates\n );\n return points.geometry.coordinates\n}\n\nexport const computeMinMax = (points: Array<{ elevation: number }>) => {\n if (!points.length) return [0, 0] as const;\n const elevations = points.map((p) => p.elevation);\n const min = Math.min(...elevations);\n const max = Math.max(...elevations);\n const pad = Math.max(10, (max - min) * 0.05);\n return [Math.floor(min - pad), Math.ceil(max + pad)];\n};\n\nexport const computeRoundedDomainAndTicks = (minMax: [number, number]) => {\n const [min, max] = minMax;\n const paddedRange = (max - min) * 1.2;\n const step = Math.max(10, Math.round(paddedRange / 6 / 10) * 10 || 50);\n const graphMin =\n Math.floor((min - (paddedRange - (max - min)) / 2) / step) * step;\n const graphMax =\n Math.ceil((max + (paddedRange - (max - min)) / 2) / step) * step;\n const ticks: number[] = [];\n for (let v = graphMin; v <= graphMax + step / 2; v += step) {\n ticks.push(v);\n }\n\n return [graphMin, graphMax, ticks] as const;\n};\n\nexport const computeMarkerPoints = (\n elevationPoints: ElevationPoint[],\n geoJson: any\n) => {\n if (!elevationPoints?.length) return [];\n const features = geoJson?.features ?? [];\n const pointFeatures = features.filter(\n (f: any) => f?.geometry?.type === \"Point\"\n );\n\n const markers: Array<Marker> = [];\n\n pointFeatures.forEach((f: any) => {\n const coords = f?.geometry?.coordinates;\n if (!Array.isArray(coords) || coords.length < 2) return;\n const [lng, lat] = coords;\n if (!Number.isFinite(lat) || !Number.isFinite(lng)) return;\n const nearest = elevationPoints.reduce<{\n point: ElevationPoint | null;\n dist: number;\n }>(\n (acc, p) => {\n const d = Math.pow(p.lat - lat, 2) + Math.pow(p.lng - lng, 2);\n if (d < acc.dist) {\n return { point: p, dist: d };\n }\n return acc;\n },\n { point: null, dist: Number.POSITIVE_INFINITY }\n );\n if (nearest.point) {\n // Skip if this marker matches the very first elevation point (start of route)\n if (nearest.point.distance === 0) {\n return;\n }\n markers.push({\n distance: nearest.point.distance,\n elevation: nearest.point.elevation,\n name: f?.properties?.name,\n });\n }\n });\n\n return markers.sort((a, b) => (a.distance ?? 0) - (b.distance ?? 0));\n};\n\nexport const findNearestPointByCoordinates = (\n points: [number, number][],\n target: { lat: number; lng: number }\n): ElevationPoint | null => {\n if (!points.length) return null;\n let closest: ElevationPoint | null = null;\n let minDist = Number.POSITIVE_INFINITY;\n points.forEach(([lng, lat]) => {\n const d = ((lat - target.lat) ** 2) + ((lng - target.lng) ** 2);\n if (d < minDist) {\n minDist = d;\n closest = { lat, lng } as ElevationPoint;\n }\n });\n return closest;\n};\n\nexport const findNearestPoint = (\n points: ElevationPoint[],\n target: [number, number]\n): ElevationPoint | null => {\n if (!points.length) return null;\n let closest: ElevationPoint | null = null;\n let minDist = Number.POSITIVE_INFINITY;\n points.forEach((p) => {\n const d = ((p.lat - target[0]) ** 2) + ((p.lng - target[1]) ** 2);\n if (d < minDist) {\n minDist = d;\n closest = p;\n }\n });\n return closest;\n};\n\nconst DISTANCE_THRESHOLD = 1000; // meters\n\nexport const isCloseCheck = (\n marker1: Marker,\n marker2?: Marker,\n threshold: number = DISTANCE_THRESHOLD\n) => {\n return (\n marker2 &&\n Math.abs((marker2.distance ?? 0) - (marker1.distance ?? 0)) < threshold\n );\n};","import { useEffect, useMemo } from \"react\";\nimport {\n CartesianGrid,\n ComposedChart,\n Line,\n ReferenceDot,\n ReferenceLine,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { useTheme } from \"../../../theme-provider\";\nimport type { RouteConfig } from \"../../../types\";\nimport { HoverStateChangeSource, useHover } from \"../HoverContext\";\nimport { DistanceTick } from \"./DistanceTick\";\nimport { ElevationDot } from \"./ElevationDot\";\nimport { ElevationTick } from \"./ElevationTick\";\nimport { ElevationTooltip } from \"./ElevationTooltip\";\nimport { MarkerShape } from \"./MarkerShape\";\nimport { useTriggerByXValue } from \"./useTriggerByXValue\";\nimport {\n computeMarkerPoints,\n computeMinMax,\n computeRoundedDomainAndTicks,\n findNearestPoint,\n getMaxDistance,\n getPointsWithElevation,\n getTicksForDistance,\n isCloseCheck,\n} from \"./utils\";\n\ninterface ElevationChartProps {\n route: RouteConfig;\n}\n\nexport const ElevationChart = ({ route }: ElevationChartProps) => {\n const { hover, setHover } = useHover();\n const theme = useTheme();\n const points = useMemo(() => getPointsWithElevation(route), [route]);\n const markers = useMemo(\n () => computeMarkerPoints(points, route.geoJson),\n [points, route.geoJson]\n );\n const maxDistance = getMaxDistance(points);\n const ticks = getTicksForDistance(maxDistance);\n const [min, max] = computeMinMax(points);\n const [minY, maxY, tickVals] = useMemo(\n () => computeRoundedDomainAndTicks([min, max]),\n [min, max]\n );\n\n const { activeIndex, activePoint, triggerByXValue, clearActiveIndex } =\n useTriggerByXValue(points);\n\n useEffect(() => {\n if (\n hover.source === HoverStateChangeSource.Chart ||\n !hover.lat ||\n !hover.lng\n )\n return;\n const nearestPoint = findNearestPoint(points, [hover.lat, hover.lng]);\n if (nearestPoint) {\n triggerByXValue(nearestPoint.distance);\n }\n }, [hover, points, triggerByXValue]);\n\n if (!points.length) {\n return null;\n }\n\n return (\n <ResponsiveContainer\n width=\"100%\"\n height=\"100%\"\n style={{ userSelect: \"none\" }}\n >\n <ComposedChart\n data={points}\n margin={theme.chart.margin}\n onMouseMove={({ activePayload }) => {\n activeIndex && clearActiveIndex();\n const activePayloadItem = activePayload?.[0];\n if (!activePayloadItem) {\n return;\n }\n const { lat, lng } = activePayloadItem.payload;\n setHover({ lat, lng, source: HoverStateChangeSource.Chart });\n }}\n onMouseEnter={() => clearActiveIndex()}\n >\n <defs>\n <linearGradient id=\"elevationGradient\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop\n offset=\"0%\"\n stopColor={theme.colors.primary}\n stopOpacity={theme.chart.gradientStartOpacity}\n />\n <stop\n offset=\"100%\"\n stopColor={theme.colors.primaryMuted}\n stopOpacity={theme.chart.gradientEndOpacity}\n />\n </linearGradient>\n </defs>\n\n <CartesianGrid\n stroke={theme.chart.gridStroke}\n strokeDasharray={theme.chart.gridDasharray}\n />\n <XAxis\n dataKey=\"distance\"\n type=\"number\"\n domain={[0, maxDistance]}\n ticks={ticks}\n tick={<DistanceTick />}\n stroke={theme.chart.axisStroke}\n />\n <YAxis\n dataKey=\"elevation\"\n tick={<ElevationTick />}\n domain={[minY, maxY]}\n ticks={tickVals}\n stroke={theme.chart.axisStroke}\n width={theme.chart.yAxisWidth}\n />\n\n <Tooltip\n cursor={{\n stroke: theme.chart.cursorStroke,\n strokeWidth: theme.chart.cursorStrokeWidth,\n }}\n content={\n <ElevationTooltip\n accent={theme.colors.accent}\n primary={theme.colors.primary}\n markers={markers}\n />\n }\n />\n\n <Line\n type=\"monotone\"\n dataKey=\"elevation\"\n stroke={theme.colors.primary}\n strokeWidth={theme.chart.lineStrokeWidth}\n dot={(props) => {\n const { cx, cy, index } = props;\n const isActive = index === activeIndex;\n if (!isActive || !activePoint) {\n // no dot for inactive points (or use small one if you prefer)\n return <></>;\n }\n return <ElevationDot cx={cx} cy={cy} />;\n }}\n activeDot={{\n r: theme.chart.activeDotRadius,\n fill: theme.dots.chartActive,\n strokeWidth: 0,\n }}\n fill=\"url(#elevationGradient)\"\n isAnimationActive={false}\n />\n\n {markers.length > 0 &&\n markers.map((m, idx) => {\n const tooClose = isCloseCheck(markers[idx], markers[idx + 1]);\n return (\n <ReferenceDot\n key={`${m.distance}-${idx}`}\n x={m.distance}\n y={m.elevation}\n r={theme.chart.referenceDotRadius}\n shape={(props) => (\n <MarkerShape\n {...props}\n name={tooClose ? undefined : m.name}\n fill={theme.colors.accent}\n />\n )}\n />\n );\n })}\n\n {/* Vertical line at active X (same as hover cursor) */}\n {activePoint && (\n <ReferenceLine\n x={activePoint.distance}\n opacity={theme.chart.referenceLineOpacity}\n />\n )}\n </ComposedChart>\n </ResponsiveContainer>\n );\n};\n","import markerSvg from \"./assets/icons/marker.svg\";\nimport markerFinishSvg from \"./assets/icons/markerFinish.svg\";\nimport markerStartSvg from \"./assets/icons/markerStart.svg\";\n\nexport const DEFAULT_CENTER = { lat: 48.9325937, lng: 20.3452306 };\nexport const DEFAULT_ZOOM_HORIZONTAL = 13;\nexport const DEFAULT_ZOOM_VERTICAL = 12;\n\nexport const markers = {\n default: markerSvg,\n start: markerStartSvg,\n finish: markerFinishSvg,\n}\n\nexport const RATIO_HEADER_MOBILE = 0.20\nexport const RATIO_HEADER_DESKTOP = 0.15",".markerLabel {\n position: absolute;\n bottom: 2rem; /* bottom-8 */\n left: 1.25rem; /* left-5 */\n text-align: left;\n filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25)) drop-shadow(0 2px 8px rgba(0, 0, 0, 0.35)); /* drop-shadow-md */\n background: rgba(0, 0, 0, 0.4); /* bg-black/40 */\n border-radius: 9999px; /* rounded-full */\n padding: 0.5rem; /* p-2 */\n}\n\n.mapCanvas {\n width: 100%;\n overflow: hidden;\n}\n","import { useEffect, useMemo, useRef } from \"react\";\nimport {\n DEFAULT_CENTER,\n DEFAULT_ZOOM_HORIZONTAL,\n DEFAULT_ZOOM_VERTICAL,\n} from \"../../constants\";\nimport { useTheme } from \"../../theme-provider\";\nimport type { RouteConfig } from \"../../types\";\nimport { buildMarkerIcon } from \"../icons/buildMarkerIcon\";\nimport {\n findNearestPointByCoordinates,\n getAllPoints,\n} from \"./ElevationChart/utils\";\nimport styles from \"./GoogleMapCanvas.module.css\";\nimport { HoverStateChangeSource, useHover } from \"./HoverContext\";\n\ninterface GoogleMapCanvasProps {\n route: RouteConfig;\n height: number | string;\n isHorizontal: boolean;\n}\n\nexport const GoogleMapCanvas = ({\n route,\n height,\n isHorizontal,\n}: GoogleMapCanvasProps) => {\n const theme = useTheme();\n const { hover, setHover } = useHover();\n const ref = useRef<HTMLDivElement | null>(null);\n const highlightMarkerRef = useRef<google.maps.Marker | null>(null);\n const mapRef = useRef<google.maps.Map | null>(null);\n const points = useMemo(() => getAllPoints(route), [route]);\n const markerIcons = useMemo(\n () => ({\n default: buildMarkerIcon(theme.marker.outer, theme.marker.inner),\n start: buildMarkerIcon(theme.marker.outer, theme.marker.startInner),\n finish: buildMarkerIcon(theme.marker.outer, theme.marker.finishInner),\n }),\n [theme.marker]\n );\n\n useEffect(() => {\n if (!ref.current || !window.google?.maps) {\n return;\n }\n\n const zoom =\n (isHorizontal && (route.zoomHorizontal || DEFAULT_ZOOM_HORIZONTAL)) ||\n route.zoomVertical ||\n DEFAULT_ZOOM_VERTICAL;\n\n const map = new window.google.maps.Map(ref.current, {\n center: route.center || DEFAULT_CENTER,\n zoom,\n mapTypeId: window.google.maps.MapTypeId.SATELLITE,\n streetViewControl: false,\n });\n mapRef.current = map;\n\n map.data.setStyle((feature: google.maps.Data.Feature) => {\n const name = feature.getProperty(\"name\") as string;\n const isFirst = feature.getProperty(\"first\") as boolean;\n const isLast = feature.getProperty(\"last\") as boolean;\n\n return {\n strokeColor: theme.colors.primaryMuted,\n strokeWeight: theme.map.strokeWeight,\n icon: {\n url: isFirst\n ? markerIcons.start\n : isLast\n ? markerIcons.finish\n : markerIcons.default,\n scaledSize: new window.google.maps.Size(\n theme.map.markerSize,\n theme.map.markerSize\n ),\n optimized: false,\n zIndex: isFirst || isLast ? 100 : 10,\n collisionBehavior:\n window.google?.maps?.CollisionBehavior?.REQUIRED_AND_HIDES_OPTIONAL,\n },\n label: {\n className: styles.markerLabel,\n fontSize: `${theme.map.markerLabelFontSize}px`,\n fontWeight: theme.map.markerLabelFontWeight,\n color: theme.colors.accent,\n text: name,\n },\n };\n });\n\n map.data.addGeoJson(route.geoJson);\n\n const moveListener = map.addListener(\n \"mousemove\",\n (e: google.maps.MapMouseEvent) => {\n const latLng = e.latLng;\n if (!latLng) return;\n const nearest = findNearestPointByCoordinates(points, {\n lat: latLng.lat(),\n lng: latLng.lng(),\n });\n if (nearest) {\n setHover({\n lat: nearest.lat,\n lng: nearest.lng,\n source: HoverStateChangeSource.Map,\n });\n }\n }\n );\n\n return () => {\n moveListener.remove();\n map.data.forEach((feature: google.maps.Data.Feature) => {\n map.data.remove(feature);\n });\n mapRef.current = null;\n };\n }, [route, isHorizontal, theme, points, setHover, markerIcons]);\n\n useEffect(() => {\n if (!ref.current || !window.google?.maps) return;\n const mapInstance = mapRef.current;\n if (!mapInstance) return;\n if (!hover.lat || !hover.lng) {\n if (highlightMarkerRef.current) {\n highlightMarkerRef.current.setMap(null);\n highlightMarkerRef.current = null;\n }\n return;\n }\n const icon = {\n path: window.google.maps.SymbolPath.CIRCLE,\n scale: theme.map.hoverMarkerScale,\n fillColor: theme.dots.mapActive,\n fillOpacity: 1,\n strokeWeight: 0,\n };\n if (!highlightMarkerRef.current) {\n highlightMarkerRef.current = new window.google.maps.Marker({\n map: mapInstance,\n icon,\n });\n } else {\n highlightMarkerRef.current.setIcon(icon);\n }\n highlightMarkerRef.current.setPosition({ lat: hover.lat, lng: hover.lng });\n highlightMarkerRef.current.setMap(mapInstance);\n }, [hover, theme]);\n\n return (\n <div\n ref={ref}\n className={styles.mapCanvas}\n style={{\n height,\n }}\n />\n );\n};\n",".chartLayer {\n position: absolute;\n left: 25px;\n right: auto;\n bottom: 25px;\n width: 50%;\n height: 180px;\n background: rgba(15, 23, 42, 0.85);\n border-radius: 12px;\n padding: 5px;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);\n display: flex;\n flex-direction: column;\n gap: 6px;\n pointer-events: auto;\n}\n\n.chartBody {\n flex: 1;\n min-height: 0;\n}\n\n@media (orientation: portrait) {\n .chartLayer {\n width: 80%;\n left: 10px;\n right: auto;\n }\n}\n","import { Status, Wrapper } from \"@googlemaps/react-wrapper\";\nimport type { CSSProperties } from \"react\";\nimport { useOrientation } from \"../../hooks/useOrientation\";\nimport { theme as defaultTheme, type PartialTheme } from \"../../theme\";\nimport { ThemeProvider } from \"../../theme-provider\";\nimport type { RouteConfig } from \"../../types\";\nimport Loader from \"../Loader\";\nimport { ElevationChart } from \"./ElevationChart\";\nimport { GoogleMapCanvas } from \"./GoogleMapCanvas\";\nimport { HoverProvider } from \"./HoverContext\";\nimport styles from \"./RouteMap.module.css\";\n\nexport interface RouteMapProps {\n apiKey: string;\n route: RouteConfig;\n height?: number | string;\n className?: string;\n style?: CSSProperties;\n theme?: PartialTheme;\n}\n\nconst messages = {\n apiKey: \"Oops! Cannot display the map: Google Maps API key missing\",\n [Status.FAILURE]:\n \"Unable to load Google Maps API. Check your API key or network.\",\n [Status.LOADING]: undefined,\n [Status.SUCCESS]: undefined,\n};\n\nconst RenderLoader = ({\n type,\n height,\n}: {\n type: keyof typeof messages;\n height?: number | string;\n}) => (\n <div style={{ height }}>\n <Loader message={messages[type]} height={height} />\n </div>\n);\n\nconst render = (status: Status, height?: number | string) => (\n <RenderLoader type={status} height={height} />\n);\n\nexport const RouteMap = ({\n apiKey,\n route,\n height = \"100dvh\",\n className,\n style,\n theme = defaultTheme,\n}: RouteMapProps) => {\n const { isHorizontal } = useOrientation();\n\n if (!apiKey) {\n return (\n <ThemeProvider theme={theme}>\n <RenderLoader type=\"apiKey\" height={height} />\n </ThemeProvider>\n );\n }\n\n const containerStyle: CSSProperties = {\n height,\n width: \"100%\",\n ...style,\n };\n\n return (\n <ThemeProvider theme={theme}>\n <HoverProvider>\n <div className={className} style={containerStyle}>\n <Wrapper apiKey={apiKey} render={(status) => render(status, height)}>\n <GoogleMapCanvas\n route={route}\n height={height}\n isHorizontal={isHorizontal}\n />\n </Wrapper>\n <div className={styles.chartLayer}>\n <div className={styles.chartBody}>\n <ElevationChart route={route} />\n </div>\n </div>\n </div>\n </HoverProvider>\n </ThemeProvider>\n );\n};\n","import * as React from \"react\";\n\nfunction isShallowEqual(object1, object2) {\n const keys1 = Object.keys(object1);\n const keys2 = Object.keys(object2);\n\n if (keys1.length !== keys2.length) {\n return false;\n }\n\n for (let key of keys1) {\n if (object1[key] !== object2[key]) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction isTouchEvent({ nativeEvent }) {\n return window.TouchEvent\n ? nativeEvent instanceof TouchEvent\n : \"touches\" in nativeEvent;\n}\n\nfunction isMouseEvent(event) {\n return event.nativeEvent instanceof MouseEvent;\n}\n\nfunction throttle(cb, ms) {\n let lastTime = 0;\n return () => {\n const now = Date.now();\n if (now - lastTime >= ms) {\n cb();\n lastTime = now;\n }\n };\n}\n\nfunction isPlainObject(value) {\n return Object.prototype.toString.call(value) === \"[object Object]\";\n}\n\nfunction dispatchStorageEvent(key, newValue) {\n window.dispatchEvent(new StorageEvent(\"storage\", { key, newValue }));\n}\n\nexport function useBattery() {\n const [state, setState] = React.useState({\n supported: true,\n loading: true,\n level: null,\n charging: null,\n chargingTime: null,\n dischargingTime: null,\n });\n\n React.useEffect(() => {\n if (!navigator.getBattery) {\n setState((s) => ({\n ...s,\n supported: false,\n loading: false,\n }));\n return;\n }\n\n let battery = null;\n\n const handleChange = () => {\n setState({\n supported: true,\n loading: false,\n level: battery.level,\n charging: battery.charging,\n chargingTime: battery.chargingTime,\n dischargingTime: battery.dischargingTime,\n });\n };\n\n navigator.getBattery().then((b) => {\n battery = b;\n handleChange();\n\n b.addEventListener(\"levelchange\", handleChange);\n b.addEventListener(\"chargingchange\", handleChange);\n b.addEventListener(\"chargingtimechange\", handleChange);\n b.addEventListener(\"dischargingtimechange\", handleChange);\n });\n\n return () => {\n if (battery) {\n battery.removeEventListener(\"levelchange\", handleChange);\n battery.removeEventListener(\"chargingchange\", handleChange);\n battery.removeEventListener(\"chargingtimechange\", handleChange);\n battery.removeEventListener(\"dischargingtimechange\", handleChange);\n }\n };\n }, []);\n\n return state;\n}\n\nexport function useClickAway(cb) {\n const ref = React.useRef(null);\n const refCb = React.useRef(cb);\n\n React.useLayoutEffect(() => {\n refCb.current = cb;\n });\n\n React.useEffect(() => {\n const handler = (e) => {\n const element = ref.current;\n if (element && !element.contains(e.target)) {\n refCb.current(e);\n }\n };\n\n document.addEventListener(\"mousedown\", handler);\n document.addEventListener(\"touchstart\", handler);\n\n return () => {\n document.removeEventListener(\"mousedown\", handler);\n document.removeEventListener(\"touchstart\", handler);\n };\n }, []);\n\n return ref;\n}\n\nfunction oldSchoolCopy(text) {\n const tempTextArea = document.createElement(\"textarea\");\n tempTextArea.value = text;\n document.body.appendChild(tempTextArea);\n tempTextArea.select();\n document.execCommand(\"copy\");\n document.body.removeChild(tempTextArea);\n}\n\nexport function useCopyToClipboard() {\n const [state, setState] = React.useState(null);\n\n const copyToClipboard = React.useCallback((value) => {\n const handleCopy = async () => {\n try {\n if (navigator?.clipboard?.writeText) {\n await navigator.clipboard.writeText(value);\n setState(value);\n } else {\n throw new Error(\"writeText not supported\");\n }\n } catch (e) {\n oldSchoolCopy(value);\n setState(value);\n }\n };\n\n handleCopy();\n }, []);\n\n return [state, copyToClipboard];\n}\n\nexport function useCounter(startingValue = 0, options = {}) {\n const { min, max } = options;\n\n if (typeof min === \"number\" && startingValue < min) {\n throw new Error(\n `Your starting value of ${startingValue} is less than your min of ${min}.`\n );\n }\n\n if (typeof max === \"number\" && startingValue > max) {\n throw new Error(\n `Your starting value of ${startingValue} is greater than your max of ${max}.`\n );\n }\n\n const [count, setCount] = React.useState(startingValue);\n\n const increment = React.useCallback(() => {\n setCount((c) => {\n const nextCount = c + 1;\n\n if (typeof max === \"number\" && nextCount > max) {\n return c;\n }\n\n return nextCount;\n });\n }, [max]);\n\n const decrement = React.useCallback(() => {\n setCount((c) => {\n const nextCount = c - 1;\n\n if (typeof min === \"number\" && nextCount < min) {\n return c;\n }\n\n return nextCount;\n });\n }, [min]);\n\n const set = React.useCallback(\n (nextCount) => {\n setCount((c) => {\n if (typeof max === \"number\" && nextCount > max) {\n return c;\n }\n\n if (typeof min === \"number\" && nextCount < min) {\n return c;\n }\n\n return nextCount;\n });\n },\n [max, min]\n );\n\n const reset = React.useCallback(() => {\n setCount(startingValue);\n }, [startingValue]);\n\n return [\n count,\n {\n increment,\n decrement,\n set,\n reset,\n },\n ];\n}\n\nexport function useDebounce(value, delay) {\n const [debouncedValue, setDebouncedValue] = React.useState(value);\n\n React.useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n\nexport function useDefault(initialValue, defaultValue) {\n const [state, setState] = React.useState(initialValue);\n\n if (typeof state === \"undefined\" || state === null) {\n return [defaultValue, setState];\n }\n\n return [state, setState];\n}\n\nexport function useDocumentTitle(title) {\n React.useEffect(() => {\n document.title = title;\n }, [title]);\n}\n\nexport function useFavicon(url) {\n React.useEffect(() => {\n let link = document.querySelector(`link[rel~=\"icon\"]`);\n\n if (!link) {\n link = document.createElement(\"link\");\n link.type = \"image/x-icon\";\n link.rel = \"icon\";\n link.href = url;\n document.head.appendChild(link);\n } else {\n link.href = url;\n }\n }, [url]);\n}\n\nexport function useGeolocation(options = {}) {\n const [state, setState] = React.useState({\n loading: true,\n accuracy: null,\n altitude: null,\n altitudeAccuracy: null,\n heading: null,\n latitude: null,\n longitude: null,\n speed: null,\n timestamp: null,\n error: null,\n });\n\n const optionsRef = React.useRef(options);\n\n React.useEffect(() => {\n const onEvent = ({ coords, timestamp }) => {\n setState({\n loading: false,\n timestamp,\n latitude: coords.latitude,\n longitude: coords.longitude,\n altitude: coords.altitude,\n accuracy: coords.accuracy,\n altitudeAccuracy: coords.altitudeAccuracy,\n heading: coords.heading,\n speed: coords.speed,\n });\n };\n\n const onEventError = (error) => {\n setState((s) => ({\n ...s,\n loading: false,\n error,\n }));\n };\n\n navigator.geolocation.getCurrentPosition(\n onEvent,\n onEventError,\n optionsRef.current\n );\n\n const watchId = navigator.geolocation.watchPosition(\n onEvent,\n onEventError,\n optionsRef.current\n );\n\n return () => {\n navigator.geolocation.clearWatch(watchId);\n };\n }, []);\n\n return state;\n}\n\nconst initialUseHistoryStateState = {\n past: [],\n present: null,\n future: [],\n};\n\nconst useHistoryStateReducer = (state, action) => {\n const { past, present, future } = state;\n\n if (action.type === \"UNDO\") {\n return {\n past: past.slice(0, past.length - 1),\n present: past[past.length - 1],\n future: [present, ...future],\n };\n } else if (action.type === \"REDO\") {\n return {\n past: [...past, present],\n present: future[0],\n future: future.slice(1),\n };\n } else if (action.type === \"SET\") {\n const { newPresent } = action;\n\n if (action.newPresent === present) {\n return state;\n }\n\n return {\n past: [...past, present],\n present: newPresent,\n future: [],\n };\n } else if (action.type === \"CLEAR\") {\n return {\n ...initialUseHistoryStateState,\n present: action.initialPresent,\n };\n } else {\n throw new Error(\"Unsupported action type\");\n }\n};\n\nexport function useHistoryState(initialPresent = {}) {\n const initialPresentRef = React.useRef(initialPresent);\n\n const [state, dispatch] = React.useReducer(useHistoryStateReducer, {\n ...initialUseHistoryStateState,\n present: initialPresentRef.current,\n });\n\n const canUndo = state.past.length !== 0;\n const canRedo = state.future.length !== 0;\n\n const undo = React.useCallback(() => {\n if (canUndo) {\n dispatch({ type: \"UNDO\" });\n }\n }, [canUndo]);\n\n const redo = React.useCallback(() => {\n if (canRedo) {\n dispatch({ type: \"REDO\" });\n }\n }, [canRedo]);\n\n const set = React.useCallback(\n (newPresent) => dispatch({ type: \"SET\", newPresent }),\n []\n );\n\n const clear = React.useCallback(\n () =>\n dispatch({ type: \"CLEAR\", initialPresent: initialPresentRef.current }),\n []\n );\n\n return { state: state.present, set, undo, redo, clear, canUndo, canRedo };\n}\n\nexport function useHover() {\n const [hovering, setHovering] = React.useState(false);\n const previousNode = React.useRef(null);\n\n const handleMouseEnter = React.useCallback(() => {\n setHovering(true);\n }, []);\n\n const handleMouseLeave = React.useCallback(() => {\n setHovering(false);\n }, []);\n\n const customRef = React.useCallback(\n (node) => {\n if (previousNode.current?.nodeType === Node.ELEMENT_NODE) {\n previousNode.current.removeEventListener(\n \"mouseenter\",\n handleMouseEnter\n );\n previousNode.current.removeEventListener(\n \"mouseleave\",\n handleMouseLeave\n );\n }\n\n if (node?.nodeType === Node.ELEMENT_NODE) {\n node.addEventListener(\"mouseenter\", handleMouseEnter);\n node.addEventListener(\"mouseleave\", handleMouseLeave);\n }\n\n previousNode.current = node;\n },\n [handleMouseEnter, handleMouseLeave]\n );\n\n return [customRef, hovering];\n}\n\nexport function useIdle(ms = 1000 * 60) {\n const [idle, setIdle] = React.useState(false);\n\n React.useEffect(() => {\n let timeoutId;\n\n const handleTimeout = () => {\n setIdle(true);\n };\n\n const handleEvent = throttle((e) => {\n setIdle(false);\n\n window.clearTimeout(timeoutId);\n timeoutId = window.setTimeout(handleTimeout, ms);\n }, 500);\n\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n handleEvent();\n }\n };\n\n timeoutId = window.setTimeout(handleTimeout, ms);\n\n window.addEventListener(\"mousemove\", handleEvent);\n window.addEventListener(\"mousedown\", handleEvent);\n window.addEventListener(\"resize\", handleEvent);\n window.addEventListener(\"keydown\", handleEvent);\n window.addEventListener(\"touchstart\", handleEvent);\n window.addEventListener(\"wheel\", handleEvent);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n window.removeEventListener(\"mousemove\", handleEvent);\n window.removeEventListener(\"mousedown\", handleEvent);\n window.removeEventListener(\"resize\", handleEvent);\n window.removeEventListener(\"keydown\", handleEvent);\n window.removeEventListener(\"touchstart\", handleEvent);\n window.removeEventListener(\"wheel\", handleEvent);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n window.clearTimeout(timeoutId);\n };\n }, [ms]);\n\n return idle;\n}\n\nexport function useIntersectionObserver(options = {}) {\n const { threshold = 1, root = null, rootMargin = \"0px\" } = options;\n const [entry, setEntry] = React.useState(null);\n\n const previousObserver = React.useRef(null);\n\n const customRef = React.useCallback(\n (node) => {\n if (previousObserver.current) {\n previousObserver.current.disconnect();\n previousObserver.current = null;\n }\n\n if (node?.nodeType === Node.ELEMENT_NODE) {\n const observer = new IntersectionObserver(\n ([entry]) => {\n setEntry(entry);\n },\n { threshold, root, rootMargin }\n );\n\n observer.observe(node);\n previousObserver.current = observer;\n }\n },\n [threshold, root, rootMargin]\n );\n\n return [customRef, entry];\n}\n\nexport function useIsClient() {\n const [isClient, setIsClient] = React.useState(false);\n\n React.useEffect(() => {\n setIsClient(true);\n }, []);\n\n return isClient;\n}\n\nexport function useIsFirstRender() {\n const renderRef = React.useRef(true);\n\n if (renderRef.current === true) {\n renderRef.current = false;\n return true;\n }\n\n return renderRef.current;\n}\n\nexport function useList(defaultList = []) {\n const [list, setList] = React.useState(defaultList);\n\n const set = React.useCallback((l) => {\n setList(l);\n }, []);\n\n const push = React.useCallback((element) => {\n setList((l) => [...l, element]);\n }, []);\n\n const removeAt = React.useCallback((index) => {\n setList((l) => [...l.slice(0, index), ...l.slice(index + 1)]);\n }, []);\n\n const insertAt = React.useCallback((index, element) => {\n setList((l) => [...l.slice(0, index), element, ...l.slice(index)]);\n }, []);\n\n const updateAt = React.useCallback((index, element) => {\n setList((l) => l.map((e, i) => (i === index ? element : e)));\n }, []);\n\n const clear = React.useCallback(() => setList([]), []);\n\n return [list, { set, push, removeAt, insertAt, updateAt, clear }];\n}\n\nconst setLocalStorageItem = (key, value) => {\n const stringifiedValue = JSON.stringify(value);\n window.localStorage.setItem(key, stringifiedValue);\n dispatchStorageEvent(key, stringifiedValue);\n};\n\nconst removeLocalStorageItem = (key) => {\n window.localStorage.removeItem(key);\n dispatchStorageEvent(key, null);\n};\n\nconst getLocalStorageItem = (key) => {\n return window.localStorage.getItem(key);\n};\n\nconst useLocalStorageSubscribe = (callback) => {\n window.addEventListener(\"storage\", callback);\n return () => window.removeEventListener(\"storage\", callback);\n};\n\nconst getLocalStorageServerSnapshot = () => {\n throw Error(\"useLocalStorage is a client-only hook\");\n};\n\nexport function useLocalStorage(key, initialValue) {\n const getSnapshot = () => getLocalStorageItem(key);\n\n const store = React.useSyncExternalStore(\n useLocalStorageSubscribe,\n getSnapshot,\n getLocalStorageServerSnapshot\n );\n\n const setState = React.useCallback(\n (v) => {\n try {\n const nextState = typeof v === \"function\" ? v(JSON.parse(store)) : v;\n\n if (nextState === undefined || nextState === null) {\n removeLocalStorageItem(key);\n } else {\n setLocalStorageItem(key, nextState);\n }\n } catch (e) {\n console.warn(e);\n }\n },\n [key, store]\n );\n\n React.useEffect(() => {\n if (\n getLocalStorageItem(key) === null &&\n typeof initialValue !== \"undefined\"\n ) {\n setLocalStorageItem(key, initialValue);\n }\n }, [key, initialValue]);\n\n return [store ? JSON.parse(store) : initialValue, setState];\n}\n\nexport function useLockBodyScroll() {\n React.useLayoutEffect(() => {\n const originalStyle = window.getComputedStyle(document.body).overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = originalStyle;\n };\n }, []);\n}\n\nexport function useLongPress(callback, options = {}) {\n const { threshold = 400, onStart, onFinish, onCancel } = options;\n const isLongPressActive = React.useRef(false);\n const isPressed = React.useRef(false);\n const timerId = React.useRef();\n\n return React.useMemo(() => {\n if (typeof callback !== \"function\") {\n return {};\n }\n\n const start = (event) => {\n if (!isMouseEvent(event) && !isTouchEvent(event)) return;\n\n if (onStart) {\n onStart(event);\n }\n\n isPressed.current = true;\n timerId.current = setTimeout(() => {\n callback(event);\n isLongPressActive.current = true;\n }, threshold);\n };\n\n const cancel = (event) => {\n if (!isMouseEvent(event) && !isTouchEvent(event)) return;\n\n if (isLongPressActive.current) {\n if (onFinish) {\n onFinish(event);\n }\n } else if (isPressed.current) {\n if (onCancel) {\n onCancel(event);\n }\n }\n\n isLongPressActive.current = false;\n isPressed.current = false;\n\n if (timerId.current) {\n window.clearTimeout(timerId.current);\n }\n };\n\n const mouseHandlers = {\n onMouseDown: start,\n onMouseUp: cancel,\n onMouseLeave: cancel,\n };\n\n const touchHandlers = {\n onTouchStart: start,\n onTouchEnd: cancel,\n };\n\n return {\n ...mouseHandlers,\n ...touchHandlers,\n };\n }, [callback, threshold, onCancel, onFinish, onStart]);\n}\n\nexport function useMap(initialState) {\n const mapRef = React.useRef(new Map(initialState));\n const [, reRender] = React.useReducer((x) => x + 1, 0);\n\n mapRef.current.set = (...args) => {\n Map.prototype.set.apply(mapRef.current, args);\n reRender();\n return mapRef.current;\n };\n\n mapRef.current.clear = (...args) => {\n Map.prototype.clear.apply(mapRef.current, args);\n reRender();\n };\n\n mapRef.current.delete = (...args) => {\n const res = Map.prototype.delete.apply(mapRef.current, args);\n reRender();\n\n return res;\n };\n\n return mapRef.current;\n}\n\nexport function useMeasure() {\n const [dimensions, setDimensions] = React.useState({\n width: null,\n height: null,\n });\n\n const previousObserver = React.useRef(null);\n\n const customRef = React.useCallback((node) => {\n if (previousObserver.current) {\n previousObserver.current.disconnect();\n previousObserver.current = null;\n }\n\n if (node?.nodeType === Node.ELEMENT_NODE) {\n const observer = new ResizeObserver(([entry]) => {\n if (entry && entry.borderBoxSize) {\n const { inlineSize: width, blockSize: height } =\n entry.borderBoxSize[0];\n\n setDimensions({ width, height });\n }\n });\n\n observer.observe(node);\n previousObserver.current = observer;\n }\n }, []);\n\n return [customRef, dimensions];\n}\n\nexport function useMediaQuery(query) {\n const subscribe = React.useCallback(\n (callback) => {\n const matchMedia = window.matchMedia(query);\n\n matchMedia.addEventListener(\"change\", callback);\n return () => {\n matchMedia.removeEventListener(\"change\", callback);\n };\n },\n [query]\n );\n\n const getSnapshot = () => {\n return window.matchMedia(query).matches;\n };\n\n const getServerSnapshot = () => {\n throw Error(\"useMediaQuery is a client-only hook\");\n };\n\n return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\nexport function useMouse() {\n const [state, setState] = React.useState({\n x: 0,\n y: 0,\n elementX: 0,\n elementY: 0,\n elementPositionX: 0,\n elementPositionY: 0,\n });\n\n const ref = React.useRef(null);\n\n React.useLayoutEffect(() => {\n const handleMouseMove = (event) => {\n let newState = {\n x: event.pageX,\n y: event.pageY,\n };\n\n if (ref.current?.nodeType === Node.ELEMENT_NODE) {\n const { left, top } = ref.current.getBoundingClientRect();\n const elementPositionX = left + window.scrollX;\n const elementPositionY = top + window.scrollY;\n const elementX = event.pageX - elementPositionX;\n const elementY = event.pageY - elementPositionY;\n\n newState.elementX = elementX;\n newState.elementY = elementY;\n newState.elementPositionX = elementPositionX;\n newState.elementPositionY = elementPositionY;\n }\n\n setState((s) => {\n return {\n ...s,\n ...newState,\n };\n });\n };\n\n document.addEventListener(\"mousemove\", handleMouseMove);\n\n return () => {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n };\n }, []);\n\n return [state, ref];\n}\n\nconst getConnection = () => {\n return (\n navigator?.connection ||\n navigator?.mozConnection ||\n navigator?.webkitConnection\n );\n};\n\nconst useNetworkStateSubscribe = (callback) => {\n window.addEventListener(\"online\", callback, { passive: true });\n window.addEventListener(\"offline\", callback, { passive: true });\n\n const connection = getConnection();\n\n if (connection) {\n connection.addEventListener(\"change\", callback, { passive: true });\n }\n\n return () => {\n window.removeEventListener(\"online\", callback);\n window.removeEventListener(\"offline\", callback);\n\n if (connection) {\n connection.removeEventListener(\"change\", callback);\n }\n };\n};\n\nconst getNetworkStateServerSnapshot = () => {\n throw Error(\"useNetworkState is a client-only hook\");\n};\n\nexport function useNetworkState() {\n const cache = React.useRef({});\n\n const getSnapshot = () => {\n const online = navigator.onLine;\n const connection = getConnection();\n\n const nextState = {\n online,\n downlink: connection?.downlink,\n downlinkMax: connection?.downlinkMax,\n effectiveType: connection?.effectiveType,\n rtt: connection?.rtt,\n saveData: connection?.saveData,\n type: connection?.type,\n };\n\n if (isShallowEqual(cache.current, nextState)) {\n return cache.current;\n } else {\n cache.current = nextState;\n return nextState;\n }\n };\n\n return React.useSyncExternalStore(\n useNetworkStateSubscribe,\n getSnapshot,\n getNetworkStateServerSnapshot\n );\n}\n\nexport function useObjectState(initialValue) {\n const [state, setState] = React.useState(initialValue);\n\n const handleUpdate = React.useCallback((arg) => {\n if (typeof arg === \"function\") {\n setState((s) => {\n const newState = arg(s);\n\n if (isPlainObject(newState)) {\n return {\n ...s,\n ...newState,\n };\n }\n });\n }\n\n if (isPlainObject(arg)) {\n setState((s) => ({\n ...s,\n ...arg,\n }));\n }\n }, []);\n\n return [state, handleUpdate];\n}\n\nexport function useOrientation() {\n const [orientation, setOrientation] = React.useState({\n angle: 0,\n type: \"landscape-primary\",\n });\n\n React.useLayoutEffect(() => {\n const handleChange = () => {\n const { angle, type } = window.screen.orientation;\n setOrientation({\n angle,\n type,\n });\n };\n\n const handle_orientationchange = () => {\n setOrientation({\n type: \"UNKNOWN\",\n angle: window.orientation,\n });\n };\n\n if (window.screen?.orientation) {\n handleChange();\n window.screen.orientation.addEventListener(\"change\", handleChange);\n } else {\n handle_orientationchange();\n window.addEventListener(\"orientationchange\", handle_orientationchange);\n }\n\n return () => {\n if (window.screen?.orientation) {\n window.screen.orientation.removeEventListener(\"change\", handleChange);\n } else {\n window.removeEventListener(\n \"orientationchange\",\n handle_orientationchange\n );\n }\n };\n }, []);\n\n return orientation;\n}\n\nconst usePreferredLanguageSubscribe = (cb) => {\n window.addEventListener(\"languagechange\", cb);\n return () => window.removeEventListener(\"languagechange\", cb);\n};\n\nconst getPreferredLanguageSnapshot = () => {\n return navigator.language;\n};\n\nconst getPreferredLanguageServerSnapshot = () => {\n throw Error(\"usePreferredLanguage is a client-only hook\");\n};\n\nexport function usePreferredLanguage() {\n return React.useSyncExternalStore(\n usePreferredLanguageSubscribe,\n getPreferredLanguageSnapshot,\n getPreferredLanguageServerSnapshot\n );\n}\n\nexport function usePrevious(value) {\n const [current, setCurrent] = React.useState(value);\n const [previous, setPrevious] = React.useState(null);\n\n if (value !== current) {\n setPrevious(current);\n setCurrent(value);\n }\n\n return previous;\n}\n\nexport function useQueue(initialValue = []) {\n const [queue, setQueue] = React.useState(initialValue);\n\n const add = React.useCallback((element) => {\n setQueue((q) => [...q, element]);\n }, []);\n\n const remove = React.useCallback(() => {\n let removedElement;\n\n setQueue(([first, ...q]) => {\n removedElement = first;\n return q;\n });\n\n return removedElement;\n }, []);\n\n const clear = React.useCallback(() => {\n setQueue([]);\n }, []);\n\n return {\n add,\n remove,\n clear,\n first: queue[0],\n last: queue[queue.length - 1],\n size: queue.length,\n queue,\n };\n}\n\nexport function useRenderCount() {\n const count = React.useRef(0);\n\n count.current++;\n\n return count.current;\n}\n\nexport function useRenderInfo(name = \"Unknown\") {\n const count = React.useRef(0);\n const lastRender = React.useRef();\n const now = Date.now();\n\n count.current++;\n\n React.useEffect(() => {\n lastRender.current = Date.now();\n });\n\n const sinceLastRender = lastRender.current ? now - lastRender.current : 0;\n\n if (process.env.NODE_ENV !== \"production\") {\n const info = {\n name,\n renders: count.current,\n sinceLastRender,\n timestamp: now,\n };\n\n console.log(info);\n\n return info;\n }\n}\n\nexport function useScript(src, options = {}) {\n const [status, setStatus] = React.useState(\"loading\");\n const optionsRef = React.useRef(options);\n\n React.useEffect(() => {\n let script = document.querySelector(`script[src=\"${src}\"]`);\n\n const domStatus = script?.getAttribute(\"data-status\");\n if (domStatus) {\n setStatus(domStatus);\n return;\n }\n\n if (script === null) {\n script = document.createElement(\"script\");\n script.src = src;\n script.async = true;\n script.setAttribute(\"data-status\", \"loading\");\n document.body.appendChild(script);\n\n const handleScriptLoad = () => {\n script.setAttribute(\"data-status\", \"ready\");\n setStatus(\"ready\");\n removeEventListeners();\n };\n\n const handleScriptError = () => {\n script.setAttribute(\"data-status\", \"error\");\n setStatus(\"error\");\n removeEventListeners();\n };\n\n const removeEventListeners = () => {\n script.removeEventListener(\"load\", handleScriptLoad);\n script.removeEventListener(\"error\", handleScriptError);\n };\n\n script.addEventListener(\"load\", handleScriptLoad);\n script.addEventListener(\"error\", handleScriptError);\n\n const removeOnUnmount = optionsRef.current.removeOnUnmount;\n\n return () => {\n if (removeOnUnmount === true) {\n script.remove();\n removeEventListeners();\n }\n };\n } else {\n setStatus(\"unknown\");\n }\n }, [src]);\n\n return status;\n}\n\nconst setSessionStorageItem = (key, value) => {\n const stringifiedValue = JSON.stringify(value);\n window.sessionStorage.setItem(key, stringifiedValue);\n dispatchStorageEvent(key, stringifiedValue);\n};\n\nconst removeSessionStorageItem = (key) => {\n window.sessionStorage.removeItem(key);\n dispatchStorageEvent(key, null);\n};\n\nconst getSessionStorageItem = (key) => {\n return window.sessionStorage.getItem(key);\n};\n\nconst useSessionStorageSubscribe = (callback) => {\n window.addEventListener(\"storage\", callback);\n return () => window.removeEventListener(\"storage\", callback);\n};\n\nconst getSessionStorageServerSnapshot = () => {\n throw Error(\"useSessionStorage is a client-only hook\");\n};\n\nexport function useSessionStorage(key, initialValue) {\n const getSnapshot = () => getSessionStorageItem(key);\n\n const store = React.useSyncExternalStore(\n useSessionStorageSubscribe,\n getSnapshot,\n getSessionStorageServerSnapshot\n );\n\n const setState = React.useCallback(\n (v) => {\n try {\n const nextState = typeof v === \"function\" ? v(JSON.parse(store)) : v;\n\n if (nextState === undefined || nextState === null) {\n removeSessionStorageItem(key);\n } else {\n setSessionStorageItem(key, nextState);\n }\n } catch (e) {\n console.warn(e);\n }\n },\n [key, store]\n );\n\n React.useEffect(() => {\n if (\n getSessionStorageItem(key) === null &&\n typeof initialValue !== \"undefined\"\n ) {\n setSessionStorageItem(key, initialValue);\n }\n }, [key, initialValue]);\n\n return [store ? JSON.parse(store) : initialValue, setState];\n}\n\nexport function useSet(values) {\n const setRef = React.useRef(new Set(values));\n const [, reRender] = React.useReducer((x) => x + 1, 0);\n\n setRef.current.add = (...args) => {\n const res = Set.prototype.add.apply(setRef.current, args);\n reRender();\n\n return res;\n };\n\n setRef.current.clear = (...args) => {\n Set.prototype.clear.apply(setRef.current, args);\n reRender();\n };\n\n setRef.current.delete = (...args) => {\n const res = Set.prototype.delete.apply(setRef.current, args);\n reRender();\n\n return res;\n };\n\n return setRef.current;\n}\n\nexport function useThrottle(value, interval = 500) {\n const [throttledValue, setThrottledValue] = React.useState(value);\n const lastUpdated = React.useRef(null);\n\n React.useEffect(() => {\n const now = Date.now();\n\n if (lastUpdated.current && now >= lastUpdated.current + interval) {\n lastUpdated.current = now;\n setThrottledValue(value);\n } else {\n const id = window.setTimeout(() => {\n lastUpdated.current = now;\n setThrottledValue(value);\n }, interval);\n\n return () => window.clearTimeout(id);\n }\n }, [value, interval]);\n\n return throttledValue;\n}\n\nexport function useToggle(initialValue) {\n const [on, setOn] = React.useState(() => {\n if (typeof initialValue === \"boolean\") {\n return initialValue;\n }\n\n return Boolean(initialValue);\n });\n\n const handleToggle = React.useCallback((value) => {\n if (typeof value === \"boolean\") {\n return setOn(value);\n }\n\n return setOn((v) => !v);\n }, []);\n\n return [on, handleToggle];\n}\n\nconst useVisibilityChangeSubscribe = (callback) => {\n document.addEventListener(\"visibilitychange\", callback);\n\n return () => {\n document.removeEventListener(\"visibilitychange\", callback);\n };\n};\n\nconst getVisibilityChangeSnapshot = () => {\n return document.visibilityState;\n};\n\nconst getVisibilityChangeServerSnapshot = () => {\n throw Error(\"useVisibilityChange is a client-only hook\");\n};\n\nexport function useVisibilityChange() {\n const visibilityState = React.useSyncExternalStore(\n useVisibilityChangeSubscribe,\n getVisibilityChangeSnapshot,\n getVisibilityChangeServerSnapshot\n );\n\n return visibilityState === \"visible\";\n}\n\nexport function useWindowScroll() {\n const [state, setState] = React.useState({\n x: null,\n y: null,\n });\n\n const scrollTo = React.useCallback((...args) => {\n if (typeof args[0] === \"object\") {\n window.scrollTo(args[0]);\n } else if (typeof args[0] === \"number\" && typeof args[1] === \"number\") {\n window.scrollTo(args[0], args[1]);\n } else {\n throw new Error(\n `Invalid arguments passed to scrollTo. See here for more info. https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo`\n );\n }\n }, []);\n\n React.useLayoutEffect(() => {\n const handleScroll = () => {\n setState({ x: window.scrollX, y: window.scrollY });\n };\n\n handleScroll();\n window.addEventListener(\"scroll\", handleScroll);\n\n return () => {\n window.removeEventListener(\"scroll\", handleScroll);\n };\n }, []);\n\n return [state, scrollTo];\n}\n\nexport function useWindowSize() {\n const [size, setSize] = React.useState({\n width: null,\n height: null,\n });\n\n React.useLayoutEffect(() => {\n const handleResize = () => {\n setSize({\n width: window.innerWidth,\n height: window.innerHeight,\n });\n };\n\n handleResize();\n window.addEventListener(\"resize\", handleResize);\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n };\n }, []);\n\n return size;\n}\n","import { useMeasure } from \"@uidotdev/usehooks\";\nimport { RATIO_HEADER_DESKTOP, RATIO_HEADER_MOBILE } from \"../constants\";\nimport { useOrientation } from \"./useOrientation\";\n\nconst HEADER_FALLBACK_PX = 100;\n\nexport const useMapHeader = () => {\n const { isVertical } = useOrientation();\n\n const [refHeader, { height: headerHeight }] = useMeasure();\n const [refMapContainer, { height: mapContainerHeight }] = useMeasure();\n\n const targetHeaderFraction = isVertical ? RATIO_HEADER_MOBILE : RATIO_HEADER_DESKTOP;\n const viewportHeight = typeof window !== \"undefined\" ? window.innerHeight : HEADER_FALLBACK_PX / targetHeaderFraction;\n const fallbackHeaderHeightPx = viewportHeight * targetHeaderFraction || HEADER_FALLBACK_PX;\n const effectiveHeaderHeight = headerHeight || fallbackHeaderHeightPx;\n const mapHeightStyle = `calc(100dvh - ${effectiveHeaderHeight}px)`;\n const mapHeight = mapContainerHeight || mapHeightStyle;\n\n return {\n refHeader,\n refMapContainer,\n targetHeaderFraction,\n effectiveHeaderHeight,\n mapHeight,\n };\n};\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-route-profile",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "bun@1.3.3",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": ">=18",
|
|
21
|
+
"react-dom": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"dev": "tsup --watch",
|
|
26
|
+
"clean": "rm -rf dist",
|
|
27
|
+
"prepare": "bun run build",
|
|
28
|
+
"fetch-elevation": "bun run bin/fetch-elevation.js"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"fetch-elevation": "./bin/fetch-elevation.js"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/google.maps": "^3.58.1",
|
|
35
|
+
"@types/node": "^24.10.1",
|
|
36
|
+
"@types/react": "^18.3.0",
|
|
37
|
+
"@types/react-dom": "^18.3.0",
|
|
38
|
+
"@uidotdev/usehooks": "^2.3.1",
|
|
39
|
+
"tsup": "^8.0.0",
|
|
40
|
+
"typescript": "^5.6.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@googlemaps/react-wrapper": "^1.2.0",
|
|
44
|
+
"recharts": "^2.12.7"
|
|
45
|
+
}
|
|
46
|
+
}
|