@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.
Files changed (159) hide show
  1. package/README.md +242 -49
  2. package/dist/JsonSchemaForm-65NLLK56.mjs +4 -0
  3. package/dist/JsonSchemaForm-65NLLK56.mjs.map +1 -0
  4. package/dist/JsonSchemaForm-PY6DH3HE.cjs +13 -0
  5. package/dist/JsonSchemaForm-PY6DH3HE.cjs.map +1 -0
  6. package/dist/JsonTree-6RYAOPSS.mjs +4 -0
  7. package/dist/JsonTree-6RYAOPSS.mjs.map +1 -0
  8. package/dist/JsonTree-7OH6CIHT.cjs +10 -0
  9. package/dist/JsonTree-7OH6CIHT.cjs.map +1 -0
  10. package/dist/MapContainer-GXQLP5WY.mjs +214 -0
  11. package/dist/MapContainer-GXQLP5WY.mjs.map +1 -0
  12. package/dist/MapContainer-RYG4HPH4.cjs +221 -0
  13. package/dist/MapContainer-RYG4HPH4.cjs.map +1 -0
  14. package/dist/{Mermaid.client-4OCKJ6QD.mjs → Mermaid.client-OKACITCW.mjs} +16 -7
  15. package/dist/Mermaid.client-OKACITCW.mjs.map +1 -0
  16. package/dist/{Mermaid.client-ZP6OE46Z.cjs → Mermaid.client-PNXEC6YL.cjs} +16 -7
  17. package/dist/Mermaid.client-PNXEC6YL.cjs.map +1 -0
  18. package/dist/{PlaygroundLayout-XXVBU4WZ.cjs → PlaygroundLayout-SYMEAG3J.cjs} +25 -24
  19. package/dist/PlaygroundLayout-SYMEAG3J.cjs.map +1 -0
  20. package/dist/{PlaygroundLayout-LMQTVXSP.mjs → PlaygroundLayout-UQRBU5RH.mjs} +4 -3
  21. package/dist/PlaygroundLayout-UQRBU5RH.mjs.map +1 -0
  22. package/dist/{PrettyCode.client-2CLSV2VD.cjs → PrettyCode.client-DANYYQYO.cjs} +11 -4
  23. package/dist/PrettyCode.client-DANYYQYO.cjs.map +1 -0
  24. package/dist/{PrettyCode.client-Y2BVON7R.mjs → PrettyCode.client-RS5ZTNBT.mjs} +11 -4
  25. package/dist/PrettyCode.client-RS5ZTNBT.mjs.map +1 -0
  26. package/dist/chunk-2DSR7V2L.mjs +561 -0
  27. package/dist/chunk-2DSR7V2L.mjs.map +1 -0
  28. package/dist/chunk-47T5ECYV.cjs +1357 -0
  29. package/dist/chunk-47T5ECYV.cjs.map +1 -0
  30. package/dist/chunk-5QT3QYFZ.cjs +189 -0
  31. package/dist/chunk-5QT3QYFZ.cjs.map +1 -0
  32. package/dist/chunk-7IIRYG4S.mjs +1057 -0
  33. package/dist/chunk-7IIRYG4S.mjs.map +1 -0
  34. package/dist/{chunk-FB5QBSI3.cjs → chunk-DI3HUXHK.cjs} +15 -195
  35. package/dist/chunk-DI3HUXHK.cjs.map +1 -0
  36. package/dist/chunk-EVGWYASL.cjs +1528 -0
  37. package/dist/chunk-EVGWYASL.cjs.map +1 -0
  38. package/dist/chunk-F2N7P5XU.cjs +30 -0
  39. package/dist/chunk-F2N7P5XU.cjs.map +1 -0
  40. package/dist/{chunk-L6UHASYQ.mjs → chunk-G6PRZP5I.mjs} +7 -186
  41. package/dist/chunk-G6PRZP5I.mjs.map +1 -0
  42. package/dist/chunk-JWB2EWQO.mjs +5 -0
  43. package/dist/chunk-JWB2EWQO.mjs.map +1 -0
  44. package/dist/chunk-LTJX2JXE.mjs +338 -0
  45. package/dist/chunk-LTJX2JXE.mjs.map +1 -0
  46. package/dist/chunk-OVNC4KW6.mjs +1494 -0
  47. package/dist/chunk-OVNC4KW6.mjs.map +1 -0
  48. package/dist/chunk-PNZSJN6T.cjs +1086 -0
  49. package/dist/chunk-PNZSJN6T.cjs.map +1 -0
  50. package/dist/chunk-TEFRA7GW.cjs +565 -0
  51. package/dist/chunk-TEFRA7GW.cjs.map +1 -0
  52. package/dist/chunk-UOMPPIED.mjs +1343 -0
  53. package/dist/chunk-UOMPPIED.mjs.map +1 -0
  54. package/dist/chunk-W6YHQI4F.mjs +187 -0
  55. package/dist/chunk-W6YHQI4F.mjs.map +1 -0
  56. package/dist/chunk-XTBRWVIV.cjs +346 -0
  57. package/dist/chunk-XTBRWVIV.cjs.map +1 -0
  58. package/dist/components-C7ZL7OMY.mjs +5 -0
  59. package/dist/components-C7ZL7OMY.mjs.map +1 -0
  60. package/dist/components-CJ2IB65O.cjs +27 -0
  61. package/dist/components-CJ2IB65O.cjs.map +1 -0
  62. package/dist/components-EASJYK45.mjs +6 -0
  63. package/dist/components-EASJYK45.mjs.map +1 -0
  64. package/dist/components-LDRFDV4A.cjs +22 -0
  65. package/dist/components-LDRFDV4A.cjs.map +1 -0
  66. package/dist/components-VZKUTDJK.mjs +5 -0
  67. package/dist/components-VZKUTDJK.mjs.map +1 -0
  68. package/dist/components-Y64GTIMQ.cjs +42 -0
  69. package/dist/components-Y64GTIMQ.cjs.map +1 -0
  70. package/dist/index.cjs +701 -4813
  71. package/dist/index.cjs.map +1 -1
  72. package/dist/index.d.cts +1274 -1026
  73. package/dist/index.d.ts +1274 -1026
  74. package/dist/index.mjs +358 -4730
  75. package/dist/index.mjs.map +1 -1
  76. package/package.json +27 -4
  77. package/src/components/index.ts +17 -0
  78. package/src/components/lazy-wrapper.tsx +281 -0
  79. package/src/index.ts +92 -7
  80. package/src/tools/AudioPlayer/components/HybridAudioPlayer.tsx +14 -5
  81. package/src/tools/AudioPlayer/lazy.tsx +85 -0
  82. package/src/tools/Gallery/components/Gallery.tsx +182 -0
  83. package/src/tools/Gallery/components/GalleryCarousel.tsx +251 -0
  84. package/src/tools/Gallery/components/GalleryCompact.tsx +173 -0
  85. package/src/tools/Gallery/components/GalleryGrid.tsx +493 -0
  86. package/src/tools/Gallery/components/GalleryImage.tsx +66 -0
  87. package/src/tools/Gallery/components/GalleryLightbox.tsx +331 -0
  88. package/src/tools/Gallery/components/GalleryMedia.tsx +66 -0
  89. package/src/tools/Gallery/components/GalleryThumbnails.tsx +173 -0
  90. package/src/tools/Gallery/components/GalleryThumbnailsVirtual.tsx +138 -0
  91. package/src/tools/Gallery/components/GalleryVideo.tsx +222 -0
  92. package/src/tools/Gallery/components/index.ts +13 -0
  93. package/src/tools/Gallery/hooks/index.ts +23 -0
  94. package/src/tools/Gallery/hooks/useGallery.ts +137 -0
  95. package/src/tools/Gallery/hooks/useImageDimensions.ts +223 -0
  96. package/src/tools/Gallery/hooks/usePinchZoom.ts +234 -0
  97. package/src/tools/Gallery/hooks/usePreloadImages.ts +71 -0
  98. package/src/tools/Gallery/hooks/useSwipe.ts +86 -0
  99. package/src/tools/Gallery/hooks/useVirtualList.ts +129 -0
  100. package/src/tools/Gallery/hooks/useZoom.ts +316 -0
  101. package/src/tools/Gallery/index.ts +66 -0
  102. package/src/tools/Gallery/types.ts +183 -0
  103. package/src/tools/Gallery/utils/imageAnalysis.ts +52 -0
  104. package/src/tools/Gallery/utils/index.ts +11 -0
  105. package/src/tools/ImageViewer/components/ImageToolbar.tsx +20 -8
  106. package/src/tools/ImageViewer/components/ImageViewer.tsx +12 -4
  107. package/src/tools/ImageViewer/lazy.tsx +37 -0
  108. package/src/tools/JsonForm/lazy.tsx +43 -0
  109. package/src/tools/JsonForm/widgets/ColorWidget.tsx +4 -1
  110. package/src/tools/JsonTree/lazy.tsx +45 -0
  111. package/src/tools/LottiePlayer/lazy.tsx +57 -0
  112. package/src/tools/Map/components/CustomOverlay.tsx +54 -0
  113. package/src/tools/Map/components/DrawControl.tsx +36 -0
  114. package/src/tools/Map/components/GeocoderControl.tsx +70 -0
  115. package/src/tools/Map/components/LayerSwitcher.tsx +225 -0
  116. package/src/tools/Map/components/MapCluster.tsx +273 -0
  117. package/src/tools/Map/components/MapContainer.tsx +191 -0
  118. package/src/tools/Map/components/MapControls.tsx +44 -0
  119. package/src/tools/Map/components/MapLegend.tsx +161 -0
  120. package/src/tools/Map/components/MapMarker.tsx +102 -0
  121. package/src/tools/Map/components/MapPopup.tsx +46 -0
  122. package/src/tools/Map/components/MapSource.tsx +30 -0
  123. package/src/tools/Map/components/index.ts +20 -0
  124. package/src/tools/Map/context/MapContext.tsx +89 -0
  125. package/src/tools/Map/context/index.ts +2 -0
  126. package/src/tools/Map/hooks/index.ts +9 -0
  127. package/src/tools/Map/hooks/useMap.ts +11 -0
  128. package/src/tools/Map/hooks/useMapControl.ts +99 -0
  129. package/src/tools/Map/hooks/useMapEvents.ts +147 -0
  130. package/src/tools/Map/hooks/useMapLayers.ts +83 -0
  131. package/src/tools/Map/hooks/useMapViewport.ts +62 -0
  132. package/src/tools/Map/hooks/useMarkers.ts +85 -0
  133. package/src/tools/Map/index.ts +116 -0
  134. package/src/tools/Map/layers/cluster.ts +94 -0
  135. package/src/tools/Map/layers/index.ts +15 -0
  136. package/src/tools/Map/layers/line.ts +93 -0
  137. package/src/tools/Map/layers/point.ts +61 -0
  138. package/src/tools/Map/layers/polygon.ts +73 -0
  139. package/src/tools/Map/lazy.tsx +56 -0
  140. package/src/tools/Map/styles/index.ts +15 -0
  141. package/src/tools/Map/types.ts +259 -0
  142. package/src/tools/Map/utils/geo.ts +88 -0
  143. package/src/tools/Map/utils/index.ts +16 -0
  144. package/src/tools/Map/utils/transform.ts +107 -0
  145. package/src/tools/Mermaid/Mermaid.client.tsx +12 -4
  146. package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +6 -2
  147. package/src/tools/Mermaid/lazy.tsx +46 -0
  148. package/src/tools/OpenapiViewer/lazy.tsx +72 -0
  149. package/src/tools/PrettyCode/PrettyCode.client.tsx +10 -3
  150. package/src/tools/PrettyCode/lazy.tsx +64 -0
  151. package/src/tools/VideoPlayer/lazy.tsx +63 -0
  152. package/dist/Mermaid.client-4OCKJ6QD.mjs.map +0 -1
  153. package/dist/Mermaid.client-ZP6OE46Z.cjs.map +0 -1
  154. package/dist/PlaygroundLayout-LMQTVXSP.mjs.map +0 -1
  155. package/dist/PlaygroundLayout-XXVBU4WZ.cjs.map +0 -1
  156. package/dist/PrettyCode.client-2CLSV2VD.cjs.map +0 -1
  157. package/dist/PrettyCode.client-Y2BVON7R.mjs.map +0 -1
  158. package/dist/chunk-FB5QBSI3.cjs.map +0 -1
  159. 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
+ }