@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,147 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useCallback } from 'react'
4
+ import { useMapContext } from '../context'
5
+ import type { MapEventHandlers, MapMouseEvent, MapViewport } from '../types'
6
+
7
+ /**
8
+ * Hook for subscribing to map events
9
+ * Automatically cleans up event listeners on unmount
10
+ */
11
+ export function useMapEvents(handlers: MapEventHandlers) {
12
+ const { mapRef, setViewport, setHoveredFeature, isLoaded } = useMapContext()
13
+
14
+ const handleClick = useCallback(
15
+ (event: maplibregl.MapMouseEvent & { features?: GeoJSON.Feature[] }) => {
16
+ if (!handlers.onClick) return
17
+
18
+ const mapEvent: MapMouseEvent = {
19
+ lngLat: event.lngLat,
20
+ point: event.point,
21
+ features: event.features,
22
+ originalEvent: event.originalEvent,
23
+ }
24
+ handlers.onClick(mapEvent)
25
+ },
26
+ [handlers]
27
+ )
28
+
29
+ const handleMouseMove = useCallback(
30
+ (event: maplibregl.MapMouseEvent & { features?: GeoJSON.Feature[] }) => {
31
+ if (!handlers.onHover) return
32
+
33
+ const feature = event.features?.[0] ?? null
34
+ setHoveredFeature(feature)
35
+
36
+ const mapEvent: MapMouseEvent = {
37
+ lngLat: event.lngLat,
38
+ point: event.point,
39
+ features: event.features,
40
+ originalEvent: event.originalEvent,
41
+ }
42
+ handlers.onHover(mapEvent)
43
+ },
44
+ [handlers, setHoveredFeature]
45
+ )
46
+
47
+ const handleMoveStart = useCallback(() => {
48
+ handlers.onMoveStart?.()
49
+ }, [handlers])
50
+
51
+ const handleMoveEnd = useCallback(() => {
52
+ const map = mapRef.current
53
+ if (!map) return
54
+
55
+ const center = map.getCenter()
56
+ const zoom = map.getZoom()
57
+ const bearing = map.getBearing()
58
+ const pitch = map.getPitch()
59
+
60
+ const newViewport: MapViewport = {
61
+ longitude: center.lng,
62
+ latitude: center.lat,
63
+ zoom,
64
+ bearing,
65
+ pitch,
66
+ }
67
+
68
+ setViewport(newViewport)
69
+ handlers.onMoveEnd?.(newViewport)
70
+ }, [mapRef, setViewport, handlers])
71
+
72
+ const handleZoomStart = useCallback(() => {
73
+ handlers.onZoomStart?.()
74
+ }, [handlers])
75
+
76
+ const handleZoomEnd = useCallback(() => {
77
+ const zoom = mapRef.current?.getZoom()
78
+ if (zoom !== undefined) {
79
+ handlers.onZoomEnd?.(zoom)
80
+ }
81
+ }, [mapRef, handlers])
82
+
83
+ const handleLoad = useCallback(() => {
84
+ handlers.onLoad?.()
85
+ }, [handlers])
86
+
87
+ useEffect(() => {
88
+ const map = mapRef.current?.getMap()
89
+ if (!map || !isLoaded) return
90
+
91
+ if (handlers.onClick) {
92
+ map.on('click', handleClick)
93
+ }
94
+ if (handlers.onHover) {
95
+ map.on('mousemove', handleMouseMove)
96
+ }
97
+ if (handlers.onMoveStart) {
98
+ map.on('movestart', handleMoveStart)
99
+ }
100
+ if (handlers.onMoveEnd) {
101
+ map.on('moveend', handleMoveEnd)
102
+ }
103
+ if (handlers.onZoomStart) {
104
+ map.on('zoomstart', handleZoomStart)
105
+ }
106
+ if (handlers.onZoomEnd) {
107
+ map.on('zoomend', handleZoomEnd)
108
+ }
109
+
110
+ return () => {
111
+ if (handlers.onClick) {
112
+ map.off('click', handleClick)
113
+ }
114
+ if (handlers.onHover) {
115
+ map.off('mousemove', handleMouseMove)
116
+ }
117
+ if (handlers.onMoveStart) {
118
+ map.off('movestart', handleMoveStart)
119
+ }
120
+ if (handlers.onMoveEnd) {
121
+ map.off('moveend', handleMoveEnd)
122
+ }
123
+ if (handlers.onZoomStart) {
124
+ map.off('zoomstart', handleZoomStart)
125
+ }
126
+ if (handlers.onZoomEnd) {
127
+ map.off('zoomend', handleZoomEnd)
128
+ }
129
+ }
130
+ }, [
131
+ mapRef,
132
+ isLoaded,
133
+ handlers,
134
+ handleClick,
135
+ handleMouseMove,
136
+ handleMoveStart,
137
+ handleMoveEnd,
138
+ handleZoomStart,
139
+ handleZoomEnd,
140
+ ])
141
+
142
+ useEffect(() => {
143
+ if (isLoaded && handlers.onLoad) {
144
+ handleLoad()
145
+ }
146
+ }, [isLoaded, handlers.onLoad, handleLoad])
147
+ }
@@ -0,0 +1,83 @@
1
+ 'use client'
2
+
3
+ import { useCallback } from 'react'
4
+ import type { LayerProps } from 'react-map-gl/maplibre'
5
+ import type { FilterSpecification } from 'maplibre-gl'
6
+ import { useMapContext } from '../context'
7
+
8
+ /**
9
+ * Hook for dynamic layer management
10
+ */
11
+ export function useMapLayers() {
12
+ const { mapRef, isLoaded } = useMapContext()
13
+
14
+ const addLayer = useCallback(
15
+ (layer: LayerProps, beforeId?: string) => {
16
+ const map = mapRef.current?.getMap()
17
+ if (!map || !isLoaded) return
18
+
19
+ if (layer.id && map.getLayer(layer.id)) {
20
+ map.removeLayer(layer.id)
21
+ }
22
+
23
+ map.addLayer(layer as Parameters<typeof map.addLayer>[0], beforeId)
24
+ },
25
+ [mapRef, isLoaded]
26
+ )
27
+
28
+ const removeLayer = useCallback(
29
+ (id: string) => {
30
+ const map = mapRef.current?.getMap()
31
+ if (!map || !isLoaded) return
32
+
33
+ if (map.getLayer(id)) {
34
+ map.removeLayer(id)
35
+ }
36
+ },
37
+ [mapRef, isLoaded]
38
+ )
39
+
40
+ const setLayerVisibility = useCallback(
41
+ (id: string, visible: boolean) => {
42
+ const map = mapRef.current?.getMap()
43
+ if (!map || !isLoaded) return
44
+
45
+ if (map.getLayer(id)) {
46
+ map.setLayoutProperty(id, 'visibility', visible ? 'visible' : 'none')
47
+ }
48
+ },
49
+ [mapRef, isLoaded]
50
+ )
51
+
52
+ const setLayerFilter = useCallback(
53
+ (id: string, filter: FilterSpecification | null) => {
54
+ const map = mapRef.current?.getMap()
55
+ if (!map || !isLoaded) return
56
+
57
+ if (map.getLayer(id)) {
58
+ map.setFilter(id, filter)
59
+ }
60
+ },
61
+ [mapRef, isLoaded]
62
+ )
63
+
64
+ const setLayerPaint = useCallback(
65
+ (id: string, property: string, value: unknown) => {
66
+ const map = mapRef.current?.getMap()
67
+ if (!map || !isLoaded) return
68
+
69
+ if (map.getLayer(id)) {
70
+ map.setPaintProperty(id, property, value)
71
+ }
72
+ },
73
+ [mapRef, isLoaded]
74
+ )
75
+
76
+ return {
77
+ addLayer,
78
+ removeLayer,
79
+ setLayerVisibility,
80
+ setLayerFilter,
81
+ setLayerPaint,
82
+ }
83
+ }
@@ -0,0 +1,62 @@
1
+ 'use client'
2
+
3
+ import { useCallback, useMemo } from 'react'
4
+ import { useMapContext } from '../context'
5
+ import type { MapViewport } from '../types'
6
+
7
+ /**
8
+ * Hook for viewport state and animations
9
+ */
10
+ export function useMapViewport() {
11
+ const { mapRef, viewport, setViewport } = useMapContext()
12
+
13
+ const animateTo = useCallback(
14
+ (newViewport: Partial<MapViewport>, duration = 1000) => {
15
+ const map = mapRef.current
16
+ if (!map) {
17
+ setViewport(newViewport)
18
+ return
19
+ }
20
+
21
+ map.easeTo({
22
+ center: newViewport.longitude !== undefined && newViewport.latitude !== undefined
23
+ ? [newViewport.longitude, newViewport.latitude]
24
+ : undefined,
25
+ zoom: newViewport.zoom,
26
+ bearing: newViewport.bearing,
27
+ pitch: newViewport.pitch,
28
+ duration,
29
+ })
30
+ },
31
+ [mapRef, setViewport]
32
+ )
33
+
34
+ const center = useMemo<[number, number]>(
35
+ () => [viewport.longitude, viewport.latitude],
36
+ [viewport.longitude, viewport.latitude]
37
+ )
38
+
39
+ const bounds = useMemo(() => {
40
+ const map = mapRef.current
41
+ if (!map) return null
42
+
43
+ const b = map.getBounds()
44
+ if (!b) return null
45
+
46
+ return {
47
+ north: b.getNorth(),
48
+ south: b.getSouth(),
49
+ east: b.getEast(),
50
+ west: b.getWest(),
51
+ }
52
+ }, [mapRef, viewport])
53
+
54
+ return {
55
+ viewport,
56
+ setViewport,
57
+ animateTo,
58
+ zoom: viewport.zoom,
59
+ center,
60
+ bounds,
61
+ }
62
+ }
@@ -0,0 +1,85 @@
1
+ 'use client'
2
+
3
+ import { useCallback } from 'react'
4
+ import { useMapContext } from '../context'
5
+ import { useMapControl } from './useMapControl'
6
+ import type { MarkerActions, MarkerData } from '../types'
7
+
8
+ let markerId = 0
9
+ const generateMarkerId = () => `marker-${++markerId}`
10
+
11
+ /**
12
+ * Hook for marker management
13
+ * Provides methods to add, remove, update markers
14
+ */
15
+ export function useMarkers(): MarkerActions {
16
+ const { markers, setMarkers } = useMapContext()
17
+ const { fitBounds } = useMapControl()
18
+
19
+ const addMarker = useCallback(
20
+ (marker: Omit<MarkerData, 'id'>): string => {
21
+ const id = generateMarkerId()
22
+ setMarkers((prev) => [...prev, { ...marker, id }])
23
+ return id
24
+ },
25
+ [setMarkers]
26
+ )
27
+
28
+ const removeMarker = useCallback(
29
+ (id: string) => {
30
+ setMarkers((prev) => prev.filter((m) => m.id !== id))
31
+ },
32
+ [setMarkers]
33
+ )
34
+
35
+ const updateMarker = useCallback(
36
+ (id: string, updates: Partial<Omit<MarkerData, 'id'>>) => {
37
+ setMarkers((prev) =>
38
+ prev.map((m) => (m.id === id ? { ...m, ...updates } : m))
39
+ )
40
+ },
41
+ [setMarkers]
42
+ )
43
+
44
+ const clearMarkers = useCallback(() => {
45
+ setMarkers([])
46
+ }, [setMarkers])
47
+
48
+ const fitToMarkers = useCallback(
49
+ (padding = 50) => {
50
+ if (markers.length === 0) return
51
+
52
+ if (markers.length === 1) {
53
+ const marker = markers[0]
54
+ fitBounds(
55
+ [
56
+ [marker.longitude - 0.01, marker.latitude - 0.01],
57
+ [marker.longitude + 0.01, marker.latitude + 0.01],
58
+ ],
59
+ { padding }
60
+ )
61
+ return
62
+ }
63
+
64
+ const lngs = markers.map((m) => m.longitude)
65
+ const lats = markers.map((m) => m.latitude)
66
+
67
+ const bounds: [[number, number], [number, number]] = [
68
+ [Math.min(...lngs), Math.min(...lats)],
69
+ [Math.max(...lngs), Math.max(...lats)],
70
+ ]
71
+
72
+ fitBounds(bounds, { padding })
73
+ },
74
+ [markers, fitBounds]
75
+ )
76
+
77
+ return {
78
+ markers,
79
+ addMarker,
80
+ removeMarker,
81
+ updateMarker,
82
+ clearMarkers,
83
+ fitToMarkers,
84
+ }
85
+ }
@@ -0,0 +1,116 @@
1
+ // Types (single source of truth)
2
+ export type {
3
+ // Marker
4
+ MarkerData,
5
+ // Viewport
6
+ MapViewport,
7
+ // Context
8
+ MapContextValue,
9
+ // Hook return types
10
+ MapControlActions,
11
+ MarkerActions,
12
+ MapEventHandlers,
13
+ MapMouseEvent,
14
+ // Style
15
+ MapStyleKey,
16
+ // Layer specs (re-exported from react-map-gl)
17
+ LayerProps,
18
+ FillLayerSpecification,
19
+ CircleLayerSpecification,
20
+ LineLayerSpecification,
21
+ SymbolLayerSpecification,
22
+ FilterSpecification,
23
+ ControlPosition,
24
+ MarkerProps,
25
+ // Layer options
26
+ ClusterLayerOptions,
27
+ PointLayerOptions,
28
+ PolygonLayerOptions,
29
+ LineLayerOptions,
30
+ RouteLayerOptions,
31
+ // Control props
32
+ DrawControlProps,
33
+ GeocoderControlProps,
34
+ CustomOverlayProps,
35
+ // Legend
36
+ LegendItem,
37
+ MapLegendProps,
38
+ // Layer switcher
39
+ LayerConfig,
40
+ LayerSwitcherProps,
41
+ } from './types'
42
+
43
+ // Context
44
+ export { MapProvider, useMapContext, MapContext } from './context'
45
+ export type { MapProviderProps } from './context'
46
+
47
+ // Hooks
48
+ export {
49
+ useMap,
50
+ useMapControl,
51
+ useMarkers,
52
+ useMapEvents,
53
+ useMapViewport,
54
+ useMapLayers,
55
+ useControl,
56
+ } from './hooks'
57
+
58
+ // Components
59
+ export {
60
+ MapContainer,
61
+ MapView,
62
+ MapControls,
63
+ MapMarker,
64
+ MapPopup,
65
+ MapCluster,
66
+ MapSource,
67
+ MapLayer,
68
+ CustomOverlay,
69
+ MapLegend,
70
+ LayerSwitcher,
71
+ } from './components'
72
+ export type {
73
+ MapContainerProps,
74
+ MapControlsProps,
75
+ MapMarkerProps,
76
+ MapPopupProps,
77
+ MapClusterProps,
78
+ MapSourceProps,
79
+ MapLayerProps,
80
+ } from './components'
81
+
82
+ // Layers
83
+ export {
84
+ createClusterLayers,
85
+ createPointLayer,
86
+ createHeatmapLayer,
87
+ createPolygonLayer,
88
+ createPolygonOutlineLayer,
89
+ createHighlightLayer,
90
+ createLineLayer,
91
+ createRouteLayers,
92
+ createDashedLineLayer,
93
+ createAnimatedLineLayer,
94
+ } from './layers'
95
+
96
+ // Styles
97
+ export { MAP_STYLES, getMapStyle } from './styles'
98
+
99
+ // Utils
100
+ export {
101
+ calculateBounds,
102
+ calculateCenter,
103
+ calculateDistance,
104
+ isPointInBounds,
105
+ expandBounds,
106
+ toGeoJSON,
107
+ fromGeoJSON,
108
+ createPoint,
109
+ createPolygon,
110
+ createLineString,
111
+ createFeatureCollection,
112
+ } from './utils'
113
+
114
+ // Re-export useful types from react-map-gl
115
+ export type { ViewState, MapRef } from 'react-map-gl/maplibre'
116
+ export { Source, Layer, Marker, Popup } from 'react-map-gl/maplibre'
@@ -0,0 +1,94 @@
1
+ import type { LayerProps } from 'react-map-gl/maplibre'
2
+ import type { ClusterLayerOptions } from '../types'
3
+
4
+ const DEFAULT_COLORS: [string, string, string] = ['#51bbd6', '#f1f075', '#f28cb1']
5
+ const DEFAULT_RADII: [number, number, number] = [20, 30, 40]
6
+ const DEFAULT_THRESHOLDS: [number, number] = [100, 750]
7
+ const DEFAULT_HOVER_COLOR = '#3b82f6' // blue-500
8
+
9
+ export function createClusterLayers(options: ClusterLayerOptions): {
10
+ cluster: LayerProps
11
+ clusterCount: LayerProps
12
+ unclusteredPoint: LayerProps
13
+ } {
14
+ const {
15
+ sourceId,
16
+ colors = DEFAULT_COLORS,
17
+ radii = DEFAULT_RADII,
18
+ thresholds = DEFAULT_THRESHOLDS,
19
+ hoverColor = DEFAULT_HOVER_COLOR,
20
+ } = options
21
+
22
+ const cluster: LayerProps = {
23
+ id: `${sourceId}-clusters`,
24
+ type: 'circle',
25
+ source: sourceId,
26
+ filter: ['has', 'point_count'],
27
+ paint: {
28
+ 'circle-color': [
29
+ 'case',
30
+ ['boolean', ['feature-state', 'hover'], false],
31
+ hoverColor,
32
+ [
33
+ 'step',
34
+ ['get', 'point_count'],
35
+ colors[0],
36
+ thresholds[0],
37
+ colors[1],
38
+ thresholds[1],
39
+ colors[2],
40
+ ],
41
+ ],
42
+ 'circle-radius': [
43
+ 'step',
44
+ ['get', 'point_count'],
45
+ radii[0],
46
+ thresholds[0],
47
+ radii[1],
48
+ thresholds[1],
49
+ radii[2],
50
+ ],
51
+ 'circle-stroke-width': 2,
52
+ 'circle-stroke-color': '#fff',
53
+ },
54
+ }
55
+
56
+ const clusterCount: LayerProps = {
57
+ id: `${sourceId}-cluster-count`,
58
+ type: 'symbol',
59
+ source: sourceId,
60
+ filter: ['has', 'point_count'],
61
+ layout: {
62
+ 'text-field': ['get', 'point_count_abbreviated'],
63
+ 'text-size': 12,
64
+ },
65
+ paint: {
66
+ 'text-color': '#000',
67
+ },
68
+ }
69
+
70
+ const unclusteredPoint: LayerProps = {
71
+ id: `${sourceId}-unclustered-point`,
72
+ type: 'circle',
73
+ source: sourceId,
74
+ filter: ['!', ['has', 'point_count']],
75
+ paint: {
76
+ 'circle-color': [
77
+ 'case',
78
+ ['boolean', ['feature-state', 'hover'], false],
79
+ hoverColor,
80
+ colors[0],
81
+ ],
82
+ 'circle-radius': [
83
+ 'case',
84
+ ['boolean', ['feature-state', 'hover'], false],
85
+ 10,
86
+ 8,
87
+ ],
88
+ 'circle-stroke-width': 2,
89
+ 'circle-stroke-color': '#fff',
90
+ },
91
+ }
92
+
93
+ return { cluster, clusterCount, unclusteredPoint }
94
+ }
@@ -0,0 +1,15 @@
1
+ export { createClusterLayers } from './cluster'
2
+ export { createPointLayer, createHeatmapLayer } from './point'
3
+ export {
4
+ createPolygonLayer,
5
+ createPolygonOutlineLayer,
6
+ createHighlightLayer,
7
+ } from './polygon'
8
+ export {
9
+ createLineLayer,
10
+ createRouteLayers,
11
+ createDashedLineLayer,
12
+ createAnimatedLineLayer,
13
+ } from './line'
14
+
15
+ // Types are exported from ../types.ts
@@ -0,0 +1,93 @@
1
+ import type { LayerProps, LineLayerSpecification } from 'react-map-gl/maplibre'
2
+ import type { LineLayerOptions, RouteLayerOptions } from '../types'
3
+
4
+ export function createLineLayer({
5
+ id,
6
+ sourceId,
7
+ color = '#3b82f6',
8
+ width = 3,
9
+ opacity = 1,
10
+ dashArray,
11
+ lineCap = 'round',
12
+ lineJoin = 'round',
13
+ blur = 0,
14
+ minZoom,
15
+ maxZoom,
16
+ }: LineLayerOptions): LayerProps {
17
+ const paint: LineLayerSpecification['paint'] = {
18
+ 'line-color': color,
19
+ 'line-width': width,
20
+ 'line-opacity': opacity,
21
+ }
22
+
23
+ if (dashArray) {
24
+ paint['line-dasharray'] = dashArray
25
+ }
26
+
27
+ if (blur > 0) {
28
+ paint['line-blur'] = blur
29
+ }
30
+
31
+ const layer: LayerProps = {
32
+ id,
33
+ type: 'line',
34
+ source: sourceId,
35
+ layout: {
36
+ 'line-cap': lineCap,
37
+ 'line-join': lineJoin,
38
+ },
39
+ paint,
40
+ }
41
+
42
+ if (minZoom !== undefined) layer.minzoom = minZoom
43
+ if (maxZoom !== undefined) layer.maxzoom = maxZoom
44
+
45
+ return layer
46
+ }
47
+
48
+ export function createRouteLayers({
49
+ sourceId,
50
+ color = '#3b82f6',
51
+ width = 4,
52
+ ...rest
53
+ }: RouteLayerOptions): {
54
+ outline: LayerProps
55
+ route: LayerProps
56
+ } {
57
+ return {
58
+ outline: createLineLayer({
59
+ id: `${sourceId}-outline`,
60
+ sourceId,
61
+ color: '#ffffff',
62
+ width: width + 2,
63
+ lineCap: 'round',
64
+ lineJoin: 'round',
65
+ ...rest,
66
+ }),
67
+ route: createLineLayer({
68
+ id: `${sourceId}-route`,
69
+ sourceId,
70
+ color,
71
+ width,
72
+ lineCap: 'round',
73
+ lineJoin: 'round',
74
+ ...rest,
75
+ }),
76
+ }
77
+ }
78
+
79
+ export function createDashedLineLayer(
80
+ options: Omit<LineLayerOptions, 'dashArray'>
81
+ ): LayerProps {
82
+ return createLineLayer({
83
+ ...options,
84
+ dashArray: [2, 2],
85
+ })
86
+ }
87
+
88
+ export function createAnimatedLineLayer(options: LineLayerOptions): LayerProps {
89
+ return createLineLayer({
90
+ ...options,
91
+ dashArray: [0, 4, 3],
92
+ })
93
+ }