@djangocfg/ui-tools 2.1.110 → 2.1.111
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 +242 -49
- package/dist/JsonSchemaForm-65NLLK56.mjs +4 -0
- package/dist/JsonSchemaForm-65NLLK56.mjs.map +1 -0
- package/dist/JsonSchemaForm-PY6DH3HE.cjs +13 -0
- package/dist/JsonSchemaForm-PY6DH3HE.cjs.map +1 -0
- package/dist/JsonTree-6RYAOPSS.mjs +4 -0
- package/dist/JsonTree-6RYAOPSS.mjs.map +1 -0
- package/dist/JsonTree-7OH6CIHT.cjs +10 -0
- package/dist/JsonTree-7OH6CIHT.cjs.map +1 -0
- package/dist/MapContainer-GXQLP5WY.mjs +214 -0
- package/dist/MapContainer-GXQLP5WY.mjs.map +1 -0
- package/dist/MapContainer-RYG4HPH4.cjs +221 -0
- package/dist/MapContainer-RYG4HPH4.cjs.map +1 -0
- package/dist/{Mermaid.client-4OCKJ6QD.mjs → Mermaid.client-OKACITCW.mjs} +16 -7
- package/dist/Mermaid.client-OKACITCW.mjs.map +1 -0
- package/dist/{Mermaid.client-ZP6OE46Z.cjs → Mermaid.client-PNXEC6YL.cjs} +16 -7
- package/dist/Mermaid.client-PNXEC6YL.cjs.map +1 -0
- package/dist/{PlaygroundLayout-XXVBU4WZ.cjs → PlaygroundLayout-SYMEAG3J.cjs} +25 -24
- package/dist/PlaygroundLayout-SYMEAG3J.cjs.map +1 -0
- package/dist/{PlaygroundLayout-LMQTVXSP.mjs → PlaygroundLayout-UQRBU5RH.mjs} +4 -3
- package/dist/PlaygroundLayout-UQRBU5RH.mjs.map +1 -0
- package/dist/{PrettyCode.client-2CLSV2VD.cjs → PrettyCode.client-DANYYQYO.cjs} +11 -4
- package/dist/PrettyCode.client-DANYYQYO.cjs.map +1 -0
- package/dist/{PrettyCode.client-Y2BVON7R.mjs → PrettyCode.client-RS5ZTNBT.mjs} +11 -4
- package/dist/PrettyCode.client-RS5ZTNBT.mjs.map +1 -0
- package/dist/chunk-2DSR7V2L.mjs +561 -0
- package/dist/chunk-2DSR7V2L.mjs.map +1 -0
- package/dist/chunk-47T5ECYV.cjs +1357 -0
- package/dist/chunk-47T5ECYV.cjs.map +1 -0
- package/dist/chunk-5QT3QYFZ.cjs +189 -0
- package/dist/chunk-5QT3QYFZ.cjs.map +1 -0
- package/dist/chunk-7IIRYG4S.mjs +1057 -0
- package/dist/chunk-7IIRYG4S.mjs.map +1 -0
- package/dist/{chunk-FB5QBSI3.cjs → chunk-DI3HUXHK.cjs} +15 -195
- package/dist/chunk-DI3HUXHK.cjs.map +1 -0
- package/dist/chunk-EVGWYASL.cjs +1528 -0
- package/dist/chunk-EVGWYASL.cjs.map +1 -0
- package/dist/chunk-F2N7P5XU.cjs +30 -0
- package/dist/chunk-F2N7P5XU.cjs.map +1 -0
- package/dist/{chunk-L6UHASYQ.mjs → chunk-G6PRZP5I.mjs} +7 -186
- package/dist/chunk-G6PRZP5I.mjs.map +1 -0
- package/dist/chunk-JWB2EWQO.mjs +5 -0
- package/dist/chunk-JWB2EWQO.mjs.map +1 -0
- package/dist/chunk-LTJX2JXE.mjs +338 -0
- package/dist/chunk-LTJX2JXE.mjs.map +1 -0
- package/dist/chunk-OVNC4KW6.mjs +1494 -0
- package/dist/chunk-OVNC4KW6.mjs.map +1 -0
- package/dist/chunk-PNZSJN6T.cjs +1086 -0
- package/dist/chunk-PNZSJN6T.cjs.map +1 -0
- package/dist/chunk-TEFRA7GW.cjs +565 -0
- package/dist/chunk-TEFRA7GW.cjs.map +1 -0
- package/dist/chunk-UOMPPIED.mjs +1343 -0
- package/dist/chunk-UOMPPIED.mjs.map +1 -0
- package/dist/chunk-W6YHQI4F.mjs +187 -0
- package/dist/chunk-W6YHQI4F.mjs.map +1 -0
- package/dist/chunk-XTBRWVIV.cjs +346 -0
- package/dist/chunk-XTBRWVIV.cjs.map +1 -0
- package/dist/components-C7ZL7OMY.mjs +5 -0
- package/dist/components-C7ZL7OMY.mjs.map +1 -0
- package/dist/components-CJ2IB65O.cjs +27 -0
- package/dist/components-CJ2IB65O.cjs.map +1 -0
- package/dist/components-EASJYK45.mjs +6 -0
- package/dist/components-EASJYK45.mjs.map +1 -0
- package/dist/components-LDRFDV4A.cjs +22 -0
- package/dist/components-LDRFDV4A.cjs.map +1 -0
- package/dist/components-VZKUTDJK.mjs +5 -0
- package/dist/components-VZKUTDJK.mjs.map +1 -0
- package/dist/components-Y64GTIMQ.cjs +42 -0
- package/dist/components-Y64GTIMQ.cjs.map +1 -0
- package/dist/index.cjs +701 -4813
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1274 -1026
- package/dist/index.d.ts +1274 -1026
- package/dist/index.mjs +358 -4730
- package/dist/index.mjs.map +1 -1
- package/package.json +27 -4
- package/src/components/index.ts +17 -0
- package/src/components/lazy-wrapper.tsx +281 -0
- package/src/index.ts +92 -7
- package/src/tools/AudioPlayer/components/HybridAudioPlayer.tsx +14 -5
- package/src/tools/AudioPlayer/lazy.tsx +85 -0
- package/src/tools/Gallery/components/Gallery.tsx +182 -0
- package/src/tools/Gallery/components/GalleryCarousel.tsx +251 -0
- package/src/tools/Gallery/components/GalleryCompact.tsx +173 -0
- package/src/tools/Gallery/components/GalleryGrid.tsx +493 -0
- package/src/tools/Gallery/components/GalleryImage.tsx +66 -0
- package/src/tools/Gallery/components/GalleryLightbox.tsx +331 -0
- package/src/tools/Gallery/components/GalleryMedia.tsx +66 -0
- package/src/tools/Gallery/components/GalleryThumbnails.tsx +173 -0
- package/src/tools/Gallery/components/GalleryThumbnailsVirtual.tsx +138 -0
- package/src/tools/Gallery/components/GalleryVideo.tsx +222 -0
- package/src/tools/Gallery/components/index.ts +13 -0
- package/src/tools/Gallery/hooks/index.ts +23 -0
- package/src/tools/Gallery/hooks/useGallery.ts +137 -0
- package/src/tools/Gallery/hooks/useImageDimensions.ts +223 -0
- package/src/tools/Gallery/hooks/usePinchZoom.ts +234 -0
- package/src/tools/Gallery/hooks/usePreloadImages.ts +71 -0
- package/src/tools/Gallery/hooks/useSwipe.ts +86 -0
- package/src/tools/Gallery/hooks/useVirtualList.ts +129 -0
- package/src/tools/Gallery/hooks/useZoom.ts +316 -0
- package/src/tools/Gallery/index.ts +66 -0
- package/src/tools/Gallery/types.ts +183 -0
- package/src/tools/Gallery/utils/imageAnalysis.ts +52 -0
- package/src/tools/Gallery/utils/index.ts +11 -0
- package/src/tools/ImageViewer/components/ImageToolbar.tsx +20 -8
- package/src/tools/ImageViewer/components/ImageViewer.tsx +12 -4
- package/src/tools/ImageViewer/lazy.tsx +37 -0
- package/src/tools/JsonForm/lazy.tsx +43 -0
- package/src/tools/JsonForm/widgets/ColorWidget.tsx +4 -1
- package/src/tools/JsonTree/lazy.tsx +45 -0
- package/src/tools/LottiePlayer/lazy.tsx +57 -0
- package/src/tools/Map/components/CustomOverlay.tsx +54 -0
- package/src/tools/Map/components/DrawControl.tsx +36 -0
- package/src/tools/Map/components/GeocoderControl.tsx +70 -0
- package/src/tools/Map/components/LayerSwitcher.tsx +225 -0
- package/src/tools/Map/components/MapCluster.tsx +273 -0
- package/src/tools/Map/components/MapContainer.tsx +191 -0
- package/src/tools/Map/components/MapControls.tsx +44 -0
- package/src/tools/Map/components/MapLegend.tsx +161 -0
- package/src/tools/Map/components/MapMarker.tsx +102 -0
- package/src/tools/Map/components/MapPopup.tsx +46 -0
- package/src/tools/Map/components/MapSource.tsx +30 -0
- package/src/tools/Map/components/index.ts +20 -0
- package/src/tools/Map/context/MapContext.tsx +89 -0
- package/src/tools/Map/context/index.ts +2 -0
- package/src/tools/Map/hooks/index.ts +9 -0
- package/src/tools/Map/hooks/useMap.ts +11 -0
- package/src/tools/Map/hooks/useMapControl.ts +99 -0
- package/src/tools/Map/hooks/useMapEvents.ts +147 -0
- package/src/tools/Map/hooks/useMapLayers.ts +83 -0
- package/src/tools/Map/hooks/useMapViewport.ts +62 -0
- package/src/tools/Map/hooks/useMarkers.ts +85 -0
- package/src/tools/Map/index.ts +116 -0
- package/src/tools/Map/layers/cluster.ts +94 -0
- package/src/tools/Map/layers/index.ts +15 -0
- package/src/tools/Map/layers/line.ts +93 -0
- package/src/tools/Map/layers/point.ts +61 -0
- package/src/tools/Map/layers/polygon.ts +73 -0
- package/src/tools/Map/lazy.tsx +56 -0
- package/src/tools/Map/styles/index.ts +15 -0
- package/src/tools/Map/types.ts +259 -0
- package/src/tools/Map/utils/geo.ts +88 -0
- package/src/tools/Map/utils/index.ts +16 -0
- package/src/tools/Map/utils/transform.ts +107 -0
- package/src/tools/Mermaid/Mermaid.client.tsx +12 -4
- package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +6 -2
- package/src/tools/Mermaid/lazy.tsx +46 -0
- package/src/tools/OpenapiViewer/lazy.tsx +72 -0
- package/src/tools/PrettyCode/PrettyCode.client.tsx +10 -3
- package/src/tools/PrettyCode/lazy.tsx +64 -0
- package/src/tools/VideoPlayer/lazy.tsx +63 -0
- package/dist/Mermaid.client-4OCKJ6QD.mjs.map +0 -1
- package/dist/Mermaid.client-ZP6OE46Z.cjs.map +0 -1
- package/dist/PlaygroundLayout-LMQTVXSP.mjs.map +0 -1
- package/dist/PlaygroundLayout-XXVBU4WZ.cjs.map +0 -1
- package/dist/PrettyCode.client-2CLSV2VD.cjs.map +0 -1
- package/dist/PrettyCode.client-Y2BVON7R.mjs.map +0 -1
- package/dist/chunk-FB5QBSI3.cjs.map +0 -1
- package/dist/chunk-L6UHASYQ.mjs.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { LayerProps } from 'react-map-gl/maplibre'
|
|
2
|
+
import type { PointLayerOptions } from '../types'
|
|
3
|
+
|
|
4
|
+
export function createPointLayer(
|
|
5
|
+
id: string,
|
|
6
|
+
sourceId: string,
|
|
7
|
+
options: PointLayerOptions = {}
|
|
8
|
+
): LayerProps {
|
|
9
|
+
const {
|
|
10
|
+
color = '#3b82f6',
|
|
11
|
+
radius = 6,
|
|
12
|
+
strokeWidth = 2,
|
|
13
|
+
strokeColor = '#fff',
|
|
14
|
+
} = options
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
id,
|
|
18
|
+
type: 'circle',
|
|
19
|
+
source: sourceId,
|
|
20
|
+
paint: {
|
|
21
|
+
'circle-color': color,
|
|
22
|
+
'circle-radius': radius,
|
|
23
|
+
'circle-stroke-width': strokeWidth,
|
|
24
|
+
'circle-stroke-color': strokeColor,
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createHeatmapLayer(
|
|
30
|
+
id: string,
|
|
31
|
+
sourceId: string,
|
|
32
|
+
options: {
|
|
33
|
+
intensity?: number
|
|
34
|
+
radius?: number
|
|
35
|
+
opacity?: number
|
|
36
|
+
} = {}
|
|
37
|
+
): LayerProps {
|
|
38
|
+
const { intensity = 1, radius = 20, opacity = 0.8 } = options
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
id,
|
|
42
|
+
type: 'heatmap',
|
|
43
|
+
source: sourceId,
|
|
44
|
+
paint: {
|
|
45
|
+
'heatmap-intensity': intensity,
|
|
46
|
+
'heatmap-radius': radius,
|
|
47
|
+
'heatmap-opacity': opacity,
|
|
48
|
+
'heatmap-color': [
|
|
49
|
+
'interpolate',
|
|
50
|
+
['linear'],
|
|
51
|
+
['heatmap-density'],
|
|
52
|
+
0, 'rgba(0, 0, 255, 0)',
|
|
53
|
+
0.2, 'rgb(0, 0, 255)',
|
|
54
|
+
0.4, 'rgb(0, 255, 0)',
|
|
55
|
+
0.6, 'rgb(255, 255, 0)',
|
|
56
|
+
0.8, 'rgb(255, 128, 0)',
|
|
57
|
+
1, 'rgb(255, 0, 0)',
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { LayerProps } from 'react-map-gl/maplibre'
|
|
2
|
+
import type { PolygonLayerOptions } from '../types'
|
|
3
|
+
|
|
4
|
+
export function createPolygonLayer(
|
|
5
|
+
id: string,
|
|
6
|
+
sourceId: string,
|
|
7
|
+
options: PolygonLayerOptions = {}
|
|
8
|
+
): LayerProps {
|
|
9
|
+
const {
|
|
10
|
+
fillColor = '#3b82f6',
|
|
11
|
+
fillOpacity = 0.3,
|
|
12
|
+
strokeColor = '#1d4ed8',
|
|
13
|
+
strokeWidth = 2,
|
|
14
|
+
} = options
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
id,
|
|
18
|
+
type: 'fill',
|
|
19
|
+
source: sourceId,
|
|
20
|
+
paint: {
|
|
21
|
+
'fill-color': fillColor,
|
|
22
|
+
'fill-opacity': fillOpacity,
|
|
23
|
+
'fill-outline-color': strokeColor,
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createPolygonOutlineLayer(
|
|
29
|
+
id: string,
|
|
30
|
+
sourceId: string,
|
|
31
|
+
options: {
|
|
32
|
+
color?: string
|
|
33
|
+
width?: number
|
|
34
|
+
} = {}
|
|
35
|
+
): LayerProps {
|
|
36
|
+
const { color = '#1d4ed8', width = 2 } = options
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
id,
|
|
40
|
+
type: 'line',
|
|
41
|
+
source: sourceId,
|
|
42
|
+
paint: {
|
|
43
|
+
'line-color': color,
|
|
44
|
+
'line-width': width,
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createHighlightLayer(
|
|
50
|
+
id: string,
|
|
51
|
+
sourceId: string,
|
|
52
|
+
options: {
|
|
53
|
+
fillColor?: string
|
|
54
|
+
fillOpacity?: number
|
|
55
|
+
strokeColor?: string
|
|
56
|
+
strokeWidth?: number
|
|
57
|
+
} = {}
|
|
58
|
+
): LayerProps {
|
|
59
|
+
const {
|
|
60
|
+
fillColor = '#fbbf24',
|
|
61
|
+
fillOpacity = 0.5,
|
|
62
|
+
} = options
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
id,
|
|
66
|
+
type: 'fill',
|
|
67
|
+
source: sourceId,
|
|
68
|
+
paint: {
|
|
69
|
+
'fill-color': fillColor,
|
|
70
|
+
'fill-opacity': fillOpacity,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lazy-loaded Map Components
|
|
5
|
+
*
|
|
6
|
+
* Heavy MapLibre GL (~800KB) is loaded only when components are rendered.
|
|
7
|
+
* Use these for automatic code-splitting with Suspense fallback.
|
|
8
|
+
*
|
|
9
|
+
* For direct imports without lazy loading, use:
|
|
10
|
+
* import { MapContainer } from '@djangocfg/ui-tools/map'
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as React from 'react';
|
|
14
|
+
import { createLazyComponent, MapLoadingFallback } from '../../components';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Lazy Components
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* LazyMapContainer - Lazy-loaded map container
|
|
22
|
+
*
|
|
23
|
+
* Automatically shows loading state while MapLibre GL loads (~800KB)
|
|
24
|
+
*/
|
|
25
|
+
export const LazyMapContainer = createLazyComponent(
|
|
26
|
+
() => import('./components/MapContainer').then((mod) => ({ default: mod.MapContainer })),
|
|
27
|
+
{
|
|
28
|
+
displayName: 'LazyMapContainer',
|
|
29
|
+
fallback: <MapLoadingFallback minHeight={400} />,
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* LazyMapView - Lazy-loaded map view (alias for MapContainer)
|
|
35
|
+
*/
|
|
36
|
+
export const LazyMapView = createLazyComponent(
|
|
37
|
+
() => import('./components/MapContainer').then((mod) => ({ default: mod.MapView })),
|
|
38
|
+
{
|
|
39
|
+
displayName: 'LazyMapView',
|
|
40
|
+
fallback: <MapLoadingFallback minHeight={400} />,
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Re-export types for convenience
|
|
46
|
+
// ============================================================================
|
|
47
|
+
|
|
48
|
+
export type {
|
|
49
|
+
MapContainerProps,
|
|
50
|
+
} from './components';
|
|
51
|
+
|
|
52
|
+
export type {
|
|
53
|
+
MapViewport,
|
|
54
|
+
MapStyleKey,
|
|
55
|
+
MarkerData,
|
|
56
|
+
} from './types';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const MAP_STYLES = {
|
|
2
|
+
light: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
|
|
3
|
+
dark: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
|
|
4
|
+
streets: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',
|
|
5
|
+
satellite: 'https://api.maptiler.com/maps/satellite/style.json',
|
|
6
|
+
} as const
|
|
7
|
+
|
|
8
|
+
export type MapStyleKey = keyof typeof MAP_STYLES
|
|
9
|
+
|
|
10
|
+
export function getMapStyle(key: MapStyleKey | string): string {
|
|
11
|
+
if (key in MAP_STYLES) {
|
|
12
|
+
return MAP_STYLES[key as MapStyleKey]
|
|
13
|
+
}
|
|
14
|
+
return key
|
|
15
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import type { RefObject, ReactElement } from 'react'
|
|
2
|
+
import type {
|
|
3
|
+
MapRef,
|
|
4
|
+
ViewState,
|
|
5
|
+
LayerProps,
|
|
6
|
+
FillLayerSpecification,
|
|
7
|
+
CircleLayerSpecification,
|
|
8
|
+
LineLayerSpecification,
|
|
9
|
+
SymbolLayerSpecification,
|
|
10
|
+
ControlPosition,
|
|
11
|
+
MarkerProps,
|
|
12
|
+
} from 'react-map-gl/maplibre'
|
|
13
|
+
import type { LngLatBoundsLike, LngLat, FilterSpecification } from 'maplibre-gl'
|
|
14
|
+
|
|
15
|
+
export type { ControlPosition, MarkerProps }
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Marker Types
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
export interface MarkerData {
|
|
22
|
+
id: string
|
|
23
|
+
longitude: number
|
|
24
|
+
latitude: number
|
|
25
|
+
data?: Record<string, unknown>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Viewport Types
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
export interface MapViewport extends Partial<ViewState> {
|
|
33
|
+
longitude: number
|
|
34
|
+
latitude: number
|
|
35
|
+
zoom: number
|
|
36
|
+
bearing?: number
|
|
37
|
+
pitch?: number
|
|
38
|
+
transitionDuration?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Context Types
|
|
43
|
+
// ============================================================================
|
|
44
|
+
|
|
45
|
+
export interface MapContextValue {
|
|
46
|
+
mapRef: RefObject<MapRef | null>
|
|
47
|
+
viewport: MapViewport
|
|
48
|
+
setViewport: (viewport: Partial<MapViewport>) => void
|
|
49
|
+
initialViewport: MapViewport
|
|
50
|
+
resetToInitial: () => void
|
|
51
|
+
markers: MarkerData[]
|
|
52
|
+
setMarkers: React.Dispatch<React.SetStateAction<MarkerData[]>>
|
|
53
|
+
selectedMarker: MarkerData | null
|
|
54
|
+
setSelectedMarker: (marker: MarkerData | null) => void
|
|
55
|
+
hoveredFeature: GeoJSON.Feature | null
|
|
56
|
+
setHoveredFeature: (feature: GeoJSON.Feature | null) => void
|
|
57
|
+
isLoaded: boolean
|
|
58
|
+
setIsLoaded: (loaded: boolean) => void
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// Hook Return Types
|
|
63
|
+
// ============================================================================
|
|
64
|
+
|
|
65
|
+
export interface MapControlActions {
|
|
66
|
+
flyTo: (center: [number, number], zoom?: number, options?: { duration?: number }) => void
|
|
67
|
+
easeTo: (center: [number, number], zoom?: number, options?: { duration?: number }) => void
|
|
68
|
+
fitBounds: (bounds: LngLatBoundsLike, options?: { padding?: number; duration?: number }) => void
|
|
69
|
+
zoomIn: () => void
|
|
70
|
+
zoomOut: () => void
|
|
71
|
+
resetView: (initialViewport: MapViewport) => void
|
|
72
|
+
getCenter: () => [number, number] | null
|
|
73
|
+
getZoom: () => number | null
|
|
74
|
+
getBounds: () => LngLatBoundsLike | null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface MarkerActions {
|
|
78
|
+
markers: MarkerData[]
|
|
79
|
+
addMarker: (marker: Omit<MarkerData, 'id'>) => string
|
|
80
|
+
removeMarker: (id: string) => void
|
|
81
|
+
updateMarker: (id: string, updates: Partial<Omit<MarkerData, 'id'>>) => void
|
|
82
|
+
clearMarkers: () => void
|
|
83
|
+
fitToMarkers: (padding?: number) => void
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ============================================================================
|
|
87
|
+
// Event Types
|
|
88
|
+
// ============================================================================
|
|
89
|
+
|
|
90
|
+
export interface MapEventHandlers {
|
|
91
|
+
onClick?: (event: MapMouseEvent) => void
|
|
92
|
+
onHover?: (event: MapMouseEvent) => void
|
|
93
|
+
onMoveStart?: () => void
|
|
94
|
+
onMoveEnd?: (viewport: MapViewport) => void
|
|
95
|
+
onZoomStart?: () => void
|
|
96
|
+
onZoomEnd?: (zoom: number) => void
|
|
97
|
+
onLoad?: () => void
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface MapMouseEvent {
|
|
101
|
+
lngLat: LngLat
|
|
102
|
+
point: { x: number; y: number }
|
|
103
|
+
features?: GeoJSON.Feature[]
|
|
104
|
+
originalEvent: MouseEvent
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Style Types
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
export type MapStyleKey = 'light' | 'dark' | 'streets' | 'satellite'
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Layer Types (from react-map-gl & maplibre-gl)
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
export type {
|
|
118
|
+
LayerProps,
|
|
119
|
+
FillLayerSpecification,
|
|
120
|
+
CircleLayerSpecification,
|
|
121
|
+
LineLayerSpecification,
|
|
122
|
+
SymbolLayerSpecification,
|
|
123
|
+
FilterSpecification,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface ClusterLayerOptions {
|
|
127
|
+
sourceId: string
|
|
128
|
+
colors?: [string, string, string]
|
|
129
|
+
radii?: [number, number, number]
|
|
130
|
+
thresholds?: [number, number]
|
|
131
|
+
hoverColor?: string
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface PointLayerOptions {
|
|
135
|
+
color?: string
|
|
136
|
+
radius?: number
|
|
137
|
+
strokeWidth?: number
|
|
138
|
+
strokeColor?: string
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface PolygonLayerOptions {
|
|
142
|
+
fillColor?: string
|
|
143
|
+
fillOpacity?: number
|
|
144
|
+
strokeColor?: string
|
|
145
|
+
strokeWidth?: number
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ============================================================================
|
|
149
|
+
// Line Layer Types
|
|
150
|
+
// ============================================================================
|
|
151
|
+
|
|
152
|
+
export interface LineLayerOptions {
|
|
153
|
+
id: string
|
|
154
|
+
sourceId: string
|
|
155
|
+
color?: string
|
|
156
|
+
width?: number
|
|
157
|
+
opacity?: number
|
|
158
|
+
dashArray?: number[]
|
|
159
|
+
lineCap?: 'butt' | 'round' | 'square'
|
|
160
|
+
lineJoin?: 'bevel' | 'round' | 'miter'
|
|
161
|
+
blur?: number
|
|
162
|
+
minZoom?: number
|
|
163
|
+
maxZoom?: number
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface RouteLayerOptions extends Omit<LineLayerOptions, 'id'> {
|
|
167
|
+
sourceId: string
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// Control Types
|
|
172
|
+
// ============================================================================
|
|
173
|
+
|
|
174
|
+
export interface DrawControlProps {
|
|
175
|
+
position?: ControlPosition
|
|
176
|
+
displayControlsDefault?: boolean
|
|
177
|
+
controls?: {
|
|
178
|
+
point?: boolean
|
|
179
|
+
line_string?: boolean
|
|
180
|
+
polygon?: boolean
|
|
181
|
+
trash?: boolean
|
|
182
|
+
combine_features?: boolean
|
|
183
|
+
uncombine_features?: boolean
|
|
184
|
+
}
|
|
185
|
+
defaultMode?: string
|
|
186
|
+
onCreate?: (evt: { features: object[] }) => void
|
|
187
|
+
onUpdate?: (evt: { features: object[]; action: string }) => void
|
|
188
|
+
onDelete?: (evt: { features: object[] }) => void
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface GeocoderControlProps {
|
|
192
|
+
position: ControlPosition
|
|
193
|
+
marker?: boolean | Omit<MarkerProps, 'longitude' | 'latitude'>
|
|
194
|
+
placeholder?: string
|
|
195
|
+
collapsed?: boolean
|
|
196
|
+
clearOnBlur?: boolean
|
|
197
|
+
showResultsWhileTyping?: boolean
|
|
198
|
+
minLength?: number
|
|
199
|
+
limit?: number
|
|
200
|
+
language?: string
|
|
201
|
+
countries?: string
|
|
202
|
+
zoom?: number
|
|
203
|
+
flyTo?: boolean | object
|
|
204
|
+
onLoading?: (e: object) => void
|
|
205
|
+
onResults?: (e: object) => void
|
|
206
|
+
onResult?: (e: object) => void
|
|
207
|
+
onError?: (e: object) => void
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface CustomOverlayProps {
|
|
211
|
+
children: React.ReactNode
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ============================================================================
|
|
215
|
+
// Legend Types
|
|
216
|
+
// ============================================================================
|
|
217
|
+
|
|
218
|
+
export interface LegendItem {
|
|
219
|
+
id: string
|
|
220
|
+
label: string
|
|
221
|
+
color?: string
|
|
222
|
+
icon?: React.ReactNode
|
|
223
|
+
type?: 'circle' | 'line' | 'fill' | 'symbol'
|
|
224
|
+
visible?: boolean
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface MapLegendProps {
|
|
228
|
+
items: LegendItem[]
|
|
229
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
|
|
230
|
+
title?: string
|
|
231
|
+
collapsible?: boolean
|
|
232
|
+
defaultCollapsed?: boolean
|
|
233
|
+
className?: string
|
|
234
|
+
style?: React.CSSProperties
|
|
235
|
+
onItemClick?: (item: LegendItem) => void
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ============================================================================
|
|
239
|
+
// Layer Switcher Types
|
|
240
|
+
// ============================================================================
|
|
241
|
+
|
|
242
|
+
export interface LayerConfig {
|
|
243
|
+
id: string
|
|
244
|
+
label: string
|
|
245
|
+
defaultVisible?: boolean
|
|
246
|
+
group?: string
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface LayerSwitcherProps {
|
|
250
|
+
layers: LayerConfig[]
|
|
251
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
|
|
252
|
+
title?: string
|
|
253
|
+
collapsible?: boolean
|
|
254
|
+
defaultCollapsed?: boolean
|
|
255
|
+
showToggleAll?: boolean
|
|
256
|
+
className?: string
|
|
257
|
+
style?: React.CSSProperties
|
|
258
|
+
onChange?: (layerId: string, visible: boolean) => void
|
|
259
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { LngLatBoundsLike } from 'maplibre-gl'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calculate bounding box from an array of coordinates
|
|
5
|
+
*/
|
|
6
|
+
export function calculateBounds(points: [number, number][]): LngLatBoundsLike {
|
|
7
|
+
if (points.length === 0) {
|
|
8
|
+
throw new Error('Cannot calculate bounds for empty array')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const lngs = points.map((p) => p[0])
|
|
12
|
+
const lats = points.map((p) => p[1])
|
|
13
|
+
|
|
14
|
+
return [
|
|
15
|
+
[Math.min(...lngs), Math.min(...lats)],
|
|
16
|
+
[Math.max(...lngs), Math.max(...lats)],
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Calculate center point from an array of coordinates
|
|
22
|
+
*/
|
|
23
|
+
export function calculateCenter(points: [number, number][]): [number, number] {
|
|
24
|
+
if (points.length === 0) {
|
|
25
|
+
throw new Error('Cannot calculate center for empty array')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const sumLng = points.reduce((acc, p) => acc + p[0], 0)
|
|
29
|
+
const sumLat = points.reduce((acc, p) => acc + p[1], 0)
|
|
30
|
+
|
|
31
|
+
return [sumLng / points.length, sumLat / points.length]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Calculate distance between two points in kilometers (Haversine formula)
|
|
36
|
+
*/
|
|
37
|
+
export function calculateDistance(
|
|
38
|
+
from: [number, number],
|
|
39
|
+
to: [number, number]
|
|
40
|
+
): number {
|
|
41
|
+
const R = 6371 // Earth's radius in km
|
|
42
|
+
const dLat = toRad(to[1] - from[1])
|
|
43
|
+
const dLng = toRad(to[0] - from[0])
|
|
44
|
+
|
|
45
|
+
const a =
|
|
46
|
+
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
47
|
+
Math.cos(toRad(from[1])) *
|
|
48
|
+
Math.cos(toRad(to[1])) *
|
|
49
|
+
Math.sin(dLng / 2) *
|
|
50
|
+
Math.sin(dLng / 2)
|
|
51
|
+
|
|
52
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
|
|
53
|
+
return R * c
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function toRad(deg: number): number {
|
|
57
|
+
return deg * (Math.PI / 180)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check if a point is within bounds
|
|
62
|
+
*/
|
|
63
|
+
export function isPointInBounds(
|
|
64
|
+
point: [number, number],
|
|
65
|
+
bounds: [[number, number], [number, number]]
|
|
66
|
+
): boolean {
|
|
67
|
+
const [[minLng, minLat], [maxLng, maxLat]] = bounds
|
|
68
|
+
const [lng, lat] = point
|
|
69
|
+
|
|
70
|
+
return lng >= minLng && lng <= maxLng && lat >= minLat && lat <= maxLat
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Expand bounds by a percentage
|
|
75
|
+
*/
|
|
76
|
+
export function expandBounds(
|
|
77
|
+
bounds: [[number, number], [number, number]],
|
|
78
|
+
percentage: number
|
|
79
|
+
): [[number, number], [number, number]] {
|
|
80
|
+
const [[minLng, minLat], [maxLng, maxLat]] = bounds
|
|
81
|
+
const lngDiff = (maxLng - minLng) * (percentage / 100)
|
|
82
|
+
const latDiff = (maxLat - minLat) * (percentage / 100)
|
|
83
|
+
|
|
84
|
+
return [
|
|
85
|
+
[minLng - lngDiff, minLat - latDiff],
|
|
86
|
+
[maxLng + lngDiff, maxLat + latDiff],
|
|
87
|
+
]
|
|
88
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
calculateBounds,
|
|
3
|
+
calculateCenter,
|
|
4
|
+
calculateDistance,
|
|
5
|
+
isPointInBounds,
|
|
6
|
+
expandBounds,
|
|
7
|
+
} from './geo'
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
toGeoJSON,
|
|
11
|
+
fromGeoJSON,
|
|
12
|
+
createPoint,
|
|
13
|
+
createPolygon,
|
|
14
|
+
createLineString,
|
|
15
|
+
createFeatureCollection,
|
|
16
|
+
} from './transform'
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert an array of items to GeoJSON FeatureCollection
|
|
3
|
+
*/
|
|
4
|
+
export function toGeoJSON<T>(
|
|
5
|
+
items: T[],
|
|
6
|
+
getLngLat: (item: T) => [number, number],
|
|
7
|
+
getProperties?: (item: T) => Record<string, unknown>
|
|
8
|
+
): GeoJSON.FeatureCollection {
|
|
9
|
+
return {
|
|
10
|
+
type: 'FeatureCollection',
|
|
11
|
+
features: items.map((item, index) => {
|
|
12
|
+
const [lng, lat] = getLngLat(item)
|
|
13
|
+
return {
|
|
14
|
+
type: 'Feature',
|
|
15
|
+
id: index,
|
|
16
|
+
geometry: {
|
|
17
|
+
type: 'Point',
|
|
18
|
+
coordinates: [lng, lat],
|
|
19
|
+
},
|
|
20
|
+
properties: getProperties ? getProperties(item) : {},
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Convert GeoJSON FeatureCollection to array of items
|
|
28
|
+
*/
|
|
29
|
+
export function fromGeoJSON<T>(
|
|
30
|
+
fc: GeoJSON.FeatureCollection,
|
|
31
|
+
transform: (feature: GeoJSON.Feature) => T
|
|
32
|
+
): T[] {
|
|
33
|
+
return fc.features.map(transform)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create a GeoJSON Point
|
|
38
|
+
*/
|
|
39
|
+
export function createPoint(
|
|
40
|
+
coordinates: [number, number],
|
|
41
|
+
properties: Record<string, unknown> = {}
|
|
42
|
+
): GeoJSON.Feature<GeoJSON.Point> {
|
|
43
|
+
return {
|
|
44
|
+
type: 'Feature',
|
|
45
|
+
geometry: {
|
|
46
|
+
type: 'Point',
|
|
47
|
+
coordinates,
|
|
48
|
+
},
|
|
49
|
+
properties,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a GeoJSON Polygon
|
|
55
|
+
*/
|
|
56
|
+
export function createPolygon(
|
|
57
|
+
coordinates: [number, number][],
|
|
58
|
+
properties: Record<string, unknown> = {}
|
|
59
|
+
): GeoJSON.Feature<GeoJSON.Polygon> {
|
|
60
|
+
// Ensure the polygon is closed
|
|
61
|
+
const closed = [...coordinates]
|
|
62
|
+
if (
|
|
63
|
+
coordinates.length > 0 &&
|
|
64
|
+
(coordinates[0][0] !== coordinates[coordinates.length - 1][0] ||
|
|
65
|
+
coordinates[0][1] !== coordinates[coordinates.length - 1][1])
|
|
66
|
+
) {
|
|
67
|
+
closed.push(coordinates[0])
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
type: 'Feature',
|
|
72
|
+
geometry: {
|
|
73
|
+
type: 'Polygon',
|
|
74
|
+
coordinates: [closed],
|
|
75
|
+
},
|
|
76
|
+
properties,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create a GeoJSON LineString
|
|
82
|
+
*/
|
|
83
|
+
export function createLineString(
|
|
84
|
+
coordinates: [number, number][],
|
|
85
|
+
properties: Record<string, unknown> = {}
|
|
86
|
+
): GeoJSON.Feature<GeoJSON.LineString> {
|
|
87
|
+
return {
|
|
88
|
+
type: 'Feature',
|
|
89
|
+
geometry: {
|
|
90
|
+
type: 'LineString',
|
|
91
|
+
coordinates,
|
|
92
|
+
},
|
|
93
|
+
properties,
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create an empty FeatureCollection
|
|
99
|
+
*/
|
|
100
|
+
export function createFeatureCollection(
|
|
101
|
+
features: GeoJSON.Feature[] = []
|
|
102
|
+
): GeoJSON.FeatureCollection {
|
|
103
|
+
return {
|
|
104
|
+
type: 'FeatureCollection',
|
|
105
|
+
features,
|
|
106
|
+
}
|
|
107
|
+
}
|