@djangocfg/ui-tools 2.1.109 → 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
package/README.md CHANGED
@@ -6,8 +6,6 @@ Heavy React tools with lazy loading (React.lazy + Suspense).
6
6
 
7
7
  **Part of [DjangoCFG](https://djangocfg.com)** — modern Django framework for production-ready SaaS applications.
8
8
 
9
- **[Live Demo & Props](https://djangocfg.com/demo/)**
10
-
11
9
  ## Install
12
10
 
13
11
  ```bash
@@ -20,14 +18,16 @@ This package contains heavy components that are loaded lazily to keep your initi
20
18
 
21
19
  | Package | Use Case |
22
20
  |---------|----------|
23
- | `@djangocfg/ui-core` | Lightweight UI components (60 components) |
21
+ | `@djangocfg/ui-core` | Lightweight UI components (60+ components) |
24
22
  | `@djangocfg/ui-tools` | Heavy tools with lazy loading |
25
23
  | `@djangocfg/ui-nextjs` | Next.js apps (extends ui-core) |
26
24
 
27
- ## Tools (9)
25
+ ## Tools (11)
28
26
 
29
27
  | Tool | Bundle Size | Description |
30
28
  |------|-------------|-------------|
29
+ | `Gallery` | ~50KB | Image/video gallery with carousel, grid, lightbox |
30
+ | `Map` | ~800KB | MapLibre GL maps with markers, clusters, layers |
31
31
  | `Mermaid` | ~800KB | Diagram rendering |
32
32
  | `PrettyCode` | ~500KB | Code syntax highlighting |
33
33
  | `OpenapiViewer` | ~400KB | OpenAPI schema viewer & playground |
@@ -38,40 +38,189 @@ This package contains heavy components that are loaded lazily to keep your initi
38
38
  | `JsonTree` | ~100KB | JSON visualization |
39
39
  | `ImageViewer` | ~50KB | Image viewer with zoom/pan/rotate |
40
40
 
41
- ## Components
41
+ ## Tree-Shakeable Imports
42
+
43
+ For better bundle optimization, use subpath imports:
44
+
45
+ ```tsx
46
+ // Only loads Gallery (~50KB instead of full package)
47
+ import { Gallery, GalleryLightbox } from '@djangocfg/ui-tools/gallery';
48
+
49
+ // Only loads Map (~800KB)
50
+ import { MapContainer, MapMarker } from '@djangocfg/ui-tools/map';
51
+ ```
52
+
53
+ ## Gallery
54
+
55
+ Full-featured image/video gallery with carousel, grid view, and fullscreen lightbox.
56
+
57
+ ```tsx
58
+ import { Gallery } from '@djangocfg/ui-tools/gallery';
59
+
60
+ const images = [
61
+ { id: '1', src: '/photo1.jpg', alt: 'Photo 1' },
62
+ { id: '2', src: '/photo2.jpg', alt: 'Photo 2' },
63
+ { id: '3', src: '/video.mp4', alt: 'Video', type: 'video' },
64
+ ];
65
+
66
+ function PhotoGallery() {
67
+ return (
68
+ <Gallery
69
+ images={images}
70
+ previewMode="carousel" // or "grid"
71
+ showThumbnails
72
+ enableLightbox
73
+ aspectRatio={16 / 9}
74
+ />
75
+ );
76
+ }
77
+ ```
78
+
79
+ ### Gallery Components
42
80
 
43
81
  | Component | Description |
44
82
  |-----------|-------------|
45
- | `Markdown` | Markdown renderer with GFM support |
83
+ | `Gallery` | Complete gallery with carousel/grid + lightbox |
84
+ | `GalleryCompact` | Minimal carousel for cards |
85
+ | `GalleryGrid` | Grid layout with "show more" badge |
86
+ | `GalleryLightbox` | Fullscreen lightbox viewer |
87
+ | `GalleryCarousel` | Embla-based carousel |
88
+ | `GalleryThumbnails` | Thumbnail strip navigation |
46
89
 
47
- ## Stores
90
+ ### Gallery Hooks
48
91
 
49
- | Store | Description |
50
- |-------|-------------|
51
- | `useMediaCacheStore` | Media caching for video/audio players |
92
+ | Hook | Description |
93
+ |------|-------------|
94
+ | `useGallery` | Gallery state management |
95
+ | `useSwipe` | Touch swipe gestures |
96
+ | `usePinchZoom` | Pinch-to-zoom for mobile |
97
+ | `usePreloadImages` | Image preloading |
98
+
99
+ ## Map
52
100
 
53
- ## Usage
101
+ MapLibre GL maps with React components for markers, clusters, popups, and custom layers.
54
102
 
55
103
  ```tsx
56
- import { VideoPlayer, AudioPlayer, Mermaid } from '@djangocfg/ui-tools';
57
- import { Markdown } from '@djangocfg/ui-tools';
104
+ import { MapContainer, MapMarker, MapPopup } from '@djangocfg/ui-tools/map';
105
+
106
+ const markers = [
107
+ { id: '1', lat: 37.7749, lng: -122.4194, title: 'San Francisco' },
108
+ { id: '2', lat: 34.0522, lng: -118.2437, title: 'Los Angeles' },
109
+ ];
58
110
 
59
- function Example() {
111
+ function LocationMap() {
60
112
  return (
61
- <>
62
- <VideoPlayer src="https://example.com/video.mp4" />
63
- <AudioPlayer src="https://example.com/audio.mp3" />
64
- <Mermaid chart={`graph TD; A-->B;`} />
65
- <Markdown content="# Hello **World**" />
66
- </>
113
+ <MapContainer
114
+ initialViewport={{ latitude: 36, longitude: -119, zoom: 5 }}
115
+ style="streets"
116
+ >
117
+ {markers.map((m) => (
118
+ <MapMarker key={m.id} latitude={m.lat} longitude={m.lng}>
119
+ <MapPopup>{m.title}</MapPopup>
120
+ </MapMarker>
121
+ ))}
122
+ </MapContainer>
67
123
  );
68
124
  }
69
125
  ```
70
126
 
71
- ## JSON Form Example
127
+ ### Map Components
128
+
129
+ | Component | Description |
130
+ |-----------|-------------|
131
+ | `MapContainer` | Main map container with controls |
132
+ | `MapMarker` | Custom marker with React children |
133
+ | `MapPopup` | Popup attached to marker |
134
+ | `MapCluster` | Clustered markers |
135
+ | `MapSource` / `MapLayer` | Custom GeoJSON layers |
136
+ | `MapControls` | Navigation controls |
137
+ | `MapLegend` | Map legend component |
138
+ | `LayerSwitcher` | Toggle map layers |
139
+ | `DrawControl` | Drawing tools (optional) |
140
+ | `GeocoderControl` | Search/geocoding (optional) |
141
+
142
+ ### Map Hooks
143
+
144
+ | Hook | Description |
145
+ |------|-------------|
146
+ | `useMap` | Access map instance |
147
+ | `useMapControl` | Programmatic map control |
148
+ | `useMarkers` | Marker management |
149
+ | `useMapEvents` | Map event handlers |
150
+ | `useMapViewport` | Viewport state |
151
+ | `useMapLayers` | Layer management |
152
+
153
+ ### Map Styles
154
+
155
+ ```tsx
156
+ import { MAP_STYLES, getMapStyle } from '@djangocfg/ui-tools/map';
157
+
158
+ // Available styles: streets, satellite, dark, light, terrain
159
+ <MapContainer style="dark" />
160
+ ```
161
+
162
+ ### Layer Utilities
72
163
 
73
164
  ```tsx
74
- import { JsonForm } from '@djangocfg/ui-tools';
165
+ import {
166
+ createClusterLayers,
167
+ createPointLayer,
168
+ createPolygonLayer,
169
+ createLineLayer,
170
+ } from '@djangocfg/ui-tools/map';
171
+ ```
172
+
173
+ ## Video Player
174
+
175
+ ```tsx
176
+ import { VideoPlayer } from '@djangocfg/ui-tools';
177
+
178
+ <VideoPlayer
179
+ src="https://example.com/video.mp4"
180
+ poster="/thumbnail.jpg"
181
+ autoplay={false}
182
+ />
183
+ ```
184
+
185
+ ## Audio Player
186
+
187
+ ```tsx
188
+ import { HybridAudioPlayer } from '@djangocfg/ui-tools';
189
+
190
+ <HybridAudioPlayer
191
+ src="https://example.com/audio.mp3"
192
+ showWaveform
193
+ />
194
+ ```
195
+
196
+ ## Mermaid Diagrams
197
+
198
+ ```tsx
199
+ import { Mermaid } from '@djangocfg/ui-tools';
200
+
201
+ <Mermaid chart={`
202
+ graph TD
203
+ A[Start] --> B{Decision}
204
+ B -->|Yes| C[Action]
205
+ B -->|No| D[End]
206
+ `} />
207
+ ```
208
+
209
+ ## Code Highlighting
210
+
211
+ ```tsx
212
+ import { PrettyCode } from '@djangocfg/ui-tools';
213
+
214
+ <PrettyCode
215
+ code={`const hello = "world";`}
216
+ language="typescript"
217
+ />
218
+ ```
219
+
220
+ ## JSON Form
221
+
222
+ ```tsx
223
+ import { JsonSchemaForm } from '@djangocfg/ui-tools';
75
224
 
76
225
  const schema = {
77
226
  type: 'object',
@@ -81,42 +230,76 @@ const schema = {
81
230
  },
82
231
  };
83
232
 
84
- function Form() {
85
- return (
86
- <JsonForm
87
- schema={schema}
88
- onSubmit={(data) => console.log(data)}
89
- />
90
- );
91
- }
233
+ <JsonSchemaForm
234
+ schema={schema}
235
+ onSubmit={(data) => console.log(data)}
236
+ />
92
237
  ```
93
238
 
94
- ## Code Highlighting Example
239
+ ## Components
95
240
 
96
- ```tsx
97
- import { PrettyCode } from '@djangocfg/ui-tools';
241
+ | Component | Description |
242
+ |-----------|-------------|
243
+ | `Markdown` | Markdown renderer with GFM support |
98
244
 
99
- function CodeBlock() {
100
- return (
101
- <PrettyCode
102
- code={`const hello = "world";`}
103
- language="typescript"
104
- />
105
- );
106
- }
107
- ```
245
+ ## Stores
246
+
247
+ | Store | Description |
248
+ |-------|-------------|
249
+ | `useMediaCacheStore` | Media caching for video/audio players |
108
250
 
109
251
  ## Lazy Loading
110
252
 
111
- All tools use React.lazy + Suspense for automatic code splitting:
253
+ All heavy tools have unified lazy-loaded versions with built-in Suspense fallbacks:
254
+
255
+ ```tsx
256
+ import {
257
+ LazyMapContainer, // ~800KB
258
+ LazyMermaid, // ~800KB
259
+ LazyPrettyCode, // ~500KB
260
+ LazyOpenapiViewer, // ~400KB
261
+ LazyJsonSchemaForm, // ~300KB
262
+ LazyLottiePlayer, // ~200KB
263
+ LazyHybridAudioPlayer, // ~200KB
264
+ LazyVideoPlayer, // ~150KB
265
+ LazyJsonTree, // ~100KB
266
+ LazyImageViewer, // ~50KB
267
+ } from '@djangocfg/ui-tools';
268
+
269
+ // Just use them - no Suspense wrapper needed!
270
+ <LazyMermaid chart={diagram} />
271
+ <LazyMapContainer initialViewport={viewport} />
272
+ <LazyVideoPlayer src="/video.mp4" />
273
+ ```
274
+
275
+ ### Custom Lazy Components
276
+
277
+ Create your own lazy components with `createLazyComponent`:
112
278
 
113
279
  ```tsx
114
- // Each tool is loaded only when rendered
115
- <Suspense fallback={<Spinner />}>
116
- <Mermaid chart={diagram} />
117
- </Suspense>
280
+ import { createLazyComponent, CardLoadingFallback } from '@djangocfg/ui-tools';
281
+
282
+ const LazyMyComponent = createLazyComponent(
283
+ () => import('./MyHeavyComponent'),
284
+ {
285
+ displayName: 'LazyMyComponent',
286
+ fallback: <CardLoadingFallback title="Loading..." minHeight={200} />,
287
+ }
288
+ );
118
289
  ```
119
290
 
291
+ ### Loading Fallbacks
292
+
293
+ Built-in fallback components for different use cases:
294
+
295
+ | Component | Use Case |
296
+ |-----------|----------|
297
+ | `LoadingFallback` | Generic spinner with optional text |
298
+ | `CardLoadingFallback` | Card-styled loading with title |
299
+ | `MapLoadingFallback` | Map-specific with location icon |
300
+ | `Spinner` | Simple spinning loader |
301
+ | `LazyWrapper` | Suspense wrapper with configurable fallback |
302
+
120
303
  ## Requirements
121
304
 
122
305
  - React >= 18 or >= 19
@@ -124,6 +307,16 @@ All tools use React.lazy + Suspense for automatic code splitting:
124
307
  - Zustand >= 5
125
308
  - @djangocfg/ui-core (peer dependency)
126
309
 
127
- ---
310
+ ### Optional Dependencies (for Map)
311
+
312
+ ```bash
313
+ # For drawing tools
314
+ pnpm add @mapbox/mapbox-gl-draw
315
+
316
+ # For geocoding/search
317
+ pnpm add @maplibre/maplibre-gl-geocoder
318
+ ```
319
+
320
+ ## License
128
321
 
129
- **[Full documentation & examples](https://djangocfg.com/demo/)**
322
+ MIT
@@ -0,0 +1,4 @@
1
+ export { JsonSchemaForm } from './chunk-7IIRYG4S.mjs';
2
+ import './chunk-CGILA3WO.mjs';
3
+ //# sourceMappingURL=JsonSchemaForm-65NLLK56.mjs.map
4
+ //# sourceMappingURL=JsonSchemaForm-65NLLK56.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"JsonSchemaForm-65NLLK56.mjs"}
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var chunkPNZSJN6T_cjs = require('./chunk-PNZSJN6T.cjs');
4
+ require('./chunk-WGEGR3DF.cjs');
5
+
6
+
7
+
8
+ Object.defineProperty(exports, "JsonSchemaForm", {
9
+ enumerable: true,
10
+ get: function () { return chunkPNZSJN6T_cjs.JsonSchemaForm; }
11
+ });
12
+ //# sourceMappingURL=JsonSchemaForm-PY6DH3HE.cjs.map
13
+ //# sourceMappingURL=JsonSchemaForm-PY6DH3HE.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"JsonSchemaForm-PY6DH3HE.cjs"}
@@ -0,0 +1,4 @@
1
+ export { JsonTree_default as default } from './chunk-W6YHQI4F.mjs';
2
+ import './chunk-CGILA3WO.mjs';
3
+ //# sourceMappingURL=JsonTree-6RYAOPSS.mjs.map
4
+ //# sourceMappingURL=JsonTree-6RYAOPSS.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"JsonTree-6RYAOPSS.mjs"}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var chunk5QT3QYFZ_cjs = require('./chunk-5QT3QYFZ.cjs');
4
+ require('./chunk-WGEGR3DF.cjs');
5
+
6
+
7
+
8
+ module.exports = chunk5QT3QYFZ_cjs.JsonTree_default;
9
+ //# sourceMappingURL=JsonTree-7OH6CIHT.cjs.map
10
+ //# sourceMappingURL=JsonTree-7OH6CIHT.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"JsonTree-7OH6CIHT.cjs"}
@@ -0,0 +1,214 @@
1
+ import { __name } from './chunk-CGILA3WO.mjs';
2
+ import { createContext, useRef, useState, useCallback, useMemo, useContext, useEffect } from 'react';
3
+ import Map from 'react-map-gl/maplibre';
4
+ import { RotateCcw, ExternalLink } from 'lucide-react';
5
+ import { cn } from '@djangocfg/ui-core/lib';
6
+ import 'maplibre-gl/dist/maplibre-gl.css';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
+
9
+ var MapContext = createContext(null);
10
+ var DEFAULT_VIEWPORT = {
11
+ longitude: 115.1889,
12
+ latitude: -8.4095,
13
+ zoom: 10,
14
+ bearing: 0,
15
+ pitch: 0
16
+ };
17
+ function MapProvider({ children, initialViewport }) {
18
+ const mapRef = useRef(null);
19
+ const initialViewportRef = useRef({
20
+ ...DEFAULT_VIEWPORT,
21
+ ...initialViewport
22
+ });
23
+ const [viewport, setViewportState] = useState(initialViewportRef.current);
24
+ const [markers, setMarkers] = useState([]);
25
+ const [selectedMarker, setSelectedMarker] = useState(null);
26
+ const [hoveredFeature, setHoveredFeature] = useState(null);
27
+ const [isLoaded, setIsLoaded] = useState(false);
28
+ const setViewport = useCallback((newViewport) => {
29
+ setViewportState((prev) => ({ ...prev, ...newViewport }));
30
+ }, []);
31
+ const resetToInitial = useCallback(() => {
32
+ const map = mapRef.current;
33
+ if (map) {
34
+ map.flyTo({
35
+ center: [initialViewportRef.current.longitude, initialViewportRef.current.latitude],
36
+ zoom: initialViewportRef.current.zoom,
37
+ bearing: initialViewportRef.current.bearing ?? 0,
38
+ pitch: initialViewportRef.current.pitch ?? 0,
39
+ duration: 1e3
40
+ });
41
+ }
42
+ }, []);
43
+ const value = useMemo(
44
+ () => ({
45
+ mapRef,
46
+ viewport,
47
+ setViewport,
48
+ initialViewport: initialViewportRef.current,
49
+ resetToInitial,
50
+ markers,
51
+ setMarkers,
52
+ selectedMarker,
53
+ setSelectedMarker,
54
+ hoveredFeature,
55
+ setHoveredFeature,
56
+ isLoaded,
57
+ setIsLoaded
58
+ }),
59
+ [viewport, setViewport, resetToInitial, markers, selectedMarker, hoveredFeature, isLoaded]
60
+ );
61
+ return /* @__PURE__ */ jsx(MapContext.Provider, { value, children });
62
+ }
63
+ __name(MapProvider, "MapProvider");
64
+ function useMapContext() {
65
+ const context = useContext(MapContext);
66
+ if (!context) {
67
+ throw new Error("useMapContext must be used within a MapProvider");
68
+ }
69
+ return context;
70
+ }
71
+ __name(useMapContext, "useMapContext");
72
+
73
+ // src/tools/Map/styles/index.ts
74
+ var MAP_STYLES = {
75
+ light: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
76
+ dark: "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
77
+ streets: "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json",
78
+ satellite: "https://api.maptiler.com/maps/satellite/style.json"
79
+ };
80
+ function MapInner({
81
+ children,
82
+ mapStyle = "light",
83
+ interactiveLayerIds,
84
+ style,
85
+ cursor,
86
+ attributionControl = true,
87
+ reuseMaps = true,
88
+ openInMapsUrl,
89
+ openInMapsLabel = "Open in Maps",
90
+ autoResetDelay = 0,
91
+ showResetButton = false
92
+ }) {
93
+ const { mapRef, viewport, setViewport, setIsLoaded, resetToInitial, initialViewport } = useMapContext();
94
+ const resetTimerRef = useRef(null);
95
+ const isInteractingRef = useRef(false);
96
+ const hasViewportChanged = Math.abs(viewport.longitude - initialViewport.longitude) > 1e-4 || Math.abs(viewport.latitude - initialViewport.latitude) > 1e-4 || Math.abs(viewport.zoom - initialViewport.zoom) > 0.1;
97
+ const handleMove = useCallback(
98
+ (evt) => {
99
+ setViewport({
100
+ longitude: evt.viewState.longitude,
101
+ latitude: evt.viewState.latitude,
102
+ zoom: evt.viewState.zoom,
103
+ bearing: evt.viewState.bearing,
104
+ pitch: evt.viewState.pitch
105
+ });
106
+ },
107
+ [setViewport]
108
+ );
109
+ const handleMoveStart = useCallback(() => {
110
+ isInteractingRef.current = true;
111
+ if (resetTimerRef.current) {
112
+ clearTimeout(resetTimerRef.current);
113
+ resetTimerRef.current = null;
114
+ }
115
+ }, []);
116
+ const handleMoveEnd = useCallback(() => {
117
+ isInteractingRef.current = false;
118
+ if (autoResetDelay > 0) {
119
+ resetTimerRef.current = setTimeout(() => {
120
+ if (!isInteractingRef.current) {
121
+ resetToInitial();
122
+ }
123
+ }, autoResetDelay);
124
+ }
125
+ }, [autoResetDelay, resetToInitial]);
126
+ const handleLoad = useCallback(() => {
127
+ setIsLoaded(true);
128
+ }, [setIsLoaded]);
129
+ useEffect(() => {
130
+ return () => {
131
+ if (resetTimerRef.current) {
132
+ clearTimeout(resetTimerRef.current);
133
+ }
134
+ };
135
+ }, []);
136
+ const resolvedStyle = mapStyle in MAP_STYLES ? MAP_STYLES[mapStyle] : mapStyle;
137
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
138
+ /* @__PURE__ */ jsx(
139
+ Map,
140
+ {
141
+ ref: mapRef,
142
+ ...viewport,
143
+ onMove: handleMove,
144
+ onMoveStart: handleMoveStart,
145
+ onMoveEnd: handleMoveEnd,
146
+ onLoad: handleLoad,
147
+ mapStyle: resolvedStyle,
148
+ interactiveLayerIds,
149
+ attributionControl: attributionControl ? {} : false,
150
+ reuseMaps,
151
+ style: {
152
+ width: "100%",
153
+ height: "100%",
154
+ ...style
155
+ },
156
+ cursor,
157
+ children
158
+ }
159
+ ),
160
+ /* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 right-3 flex items-center gap-2", children: [
161
+ showResetButton && hasViewportChanged && /* @__PURE__ */ jsxs(
162
+ "button",
163
+ {
164
+ type: "button",
165
+ onClick: resetToInitial,
166
+ className: cn(
167
+ "inline-flex items-center gap-2 px-3 py-2 rounded-lg",
168
+ "bg-background/95 backdrop-blur-sm text-foreground text-sm font-medium",
169
+ "hover:bg-background transition-colors shadow-sm border border-border"
170
+ ),
171
+ children: [
172
+ /* @__PURE__ */ jsx(RotateCcw, { className: "w-4 h-4" }),
173
+ "Reset"
174
+ ]
175
+ }
176
+ ),
177
+ openInMapsUrl && /* @__PURE__ */ jsxs(
178
+ "a",
179
+ {
180
+ href: openInMapsUrl,
181
+ target: "_blank",
182
+ rel: "noopener noreferrer",
183
+ className: cn(
184
+ "inline-flex items-center gap-2 px-4 py-2 rounded-lg",
185
+ "bg-background/95 backdrop-blur-sm text-foreground text-sm font-medium",
186
+ "hover:bg-background transition-colors shadow-sm border border-border"
187
+ ),
188
+ children: [
189
+ /* @__PURE__ */ jsx(ExternalLink, { className: "w-4 h-4" }),
190
+ openInMapsLabel
191
+ ]
192
+ }
193
+ )
194
+ ] })
195
+ ] });
196
+ }
197
+ __name(MapInner, "MapInner");
198
+ function MapContainer({
199
+ children,
200
+ initialViewport,
201
+ className,
202
+ ...props
203
+ }) {
204
+ return /* @__PURE__ */ jsx("div", { className, style: { width: "100%", height: "100%", position: "relative" }, children: /* @__PURE__ */ jsx(MapProvider, { initialViewport, children: /* @__PURE__ */ jsx(MapInner, { ...props, children }) }) });
205
+ }
206
+ __name(MapContainer, "MapContainer");
207
+ function MapView(props) {
208
+ return /* @__PURE__ */ jsx(MapInner, { ...props });
209
+ }
210
+ __name(MapView, "MapView");
211
+
212
+ export { MapContainer, MapView };
213
+ //# sourceMappingURL=MapContainer-GXQLP5WY.mjs.map
214
+ //# sourceMappingURL=MapContainer-GXQLP5WY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools/Map/context/MapContext.tsx","../src/tools/Map/styles/index.ts","../src/tools/Map/components/MapContainer.tsx"],"names":["useRef","useCallback","jsx"],"mappings":";;;;;;;;AAcA,IAAM,UAAA,GAAa,cAAsC,IAAI,CAAA;AAO7D,IAAM,gBAAA,GAAgC;AAAA,EACpC,SAAA,EAAW,QAAA;AAAA,EACX,QAAA,EAAU,OAAA;AAAA,EACV,IAAA,EAAM,EAAA;AAAA,EACN,OAAA,EAAS,CAAA;AAAA,EACT,KAAA,EAAO;AACT,CAAA;AAEO,SAAS,WAAA,CAAY,EAAE,QAAA,EAAU,eAAA,EAAgB,EAAqB;AAC3E,EAAA,MAAM,MAAA,GAAS,OAAsB,IAAI,CAAA;AACzC,EAAA,MAAM,qBAAqB,MAAA,CAAoB;AAAA,IAC7C,GAAG,gBAAA;AAAA,IACH,GAAG;AAAA,GACJ,CAAA;AACD,EAAA,MAAM,CAAC,QAAA,EAAU,gBAAgB,CAAA,GAAI,QAAA,CAAsB,mBAAmB,OAAO,CAAA;AACrF,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAuB,EAAE,CAAA;AACvD,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAA4B,IAAI,CAAA;AAC5E,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAiC,IAAI,CAAA;AACjF,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,KAAK,CAAA;AAE9C,EAAA,MAAM,WAAA,GAAc,WAAA,CAAY,CAAC,WAAA,KAAsC;AACrE,IAAA,gBAAA,CAAiB,CAAC,IAAA,MAAU,EAAE,GAAG,IAAA,EAAM,GAAG,aAAY,CAAE,CAAA;AAAA,EAC1D,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,cAAA,GAAiB,YAAY,MAAM;AACvC,IAAA,MAAM,MAAM,MAAA,CAAO,OAAA;AACnB,IAAA,IAAI,GAAA,EAAK;AACP,MAAA,GAAA,CAAI,KAAA,CAAM;AAAA,QACR,QAAQ,CAAC,kBAAA,CAAmB,QAAQ,SAAA,EAAW,kBAAA,CAAmB,QAAQ,QAAQ,CAAA;AAAA,QAClF,IAAA,EAAM,mBAAmB,OAAA,CAAQ,IAAA;AAAA,QACjC,OAAA,EAAS,kBAAA,CAAmB,OAAA,CAAQ,OAAA,IAAW,CAAA;AAAA,QAC/C,KAAA,EAAO,kBAAA,CAAmB,OAAA,CAAQ,KAAA,IAAS,CAAA;AAAA,QAC3C,QAAA,EAAU;AAAA,OACX,CAAA;AAAA,IACH;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACZ,OAAO;AAAA,MACL,MAAA;AAAA,MACA,QAAA;AAAA,MACA,WAAA;AAAA,MACA,iBAAiB,kBAAA,CAAmB,OAAA;AAAA,MACpC,cAAA;AAAA,MACA,OAAA;AAAA,MACA,UAAA;AAAA,MACA,cAAA;AAAA,MACA,iBAAA;AAAA,MACA,cAAA;AAAA,MACA,iBAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,QAAA,EAAU,WAAA,EAAa,gBAAgB,OAAA,EAAS,cAAA,EAAgB,gBAAgB,QAAQ;AAAA,GAC3F;AAEA,EAAA,uBAAO,GAAA,CAAC,UAAA,CAAW,QAAA,EAAX,EAAoB,OAAe,QAAA,EAAS,CAAA;AACtD;AAjDgB,MAAA,CAAA,WAAA,EAAA,aAAA,CAAA;AAmDT,SAAS,aAAA,GAAiC;AAC/C,EAAA,MAAM,OAAA,GAAU,WAAW,UAAU,CAAA;AACrC,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,iDAAiD,CAAA;AAAA,EACnE;AACA,EAAA,OAAO,OAAA;AACT;AANgB,MAAA,CAAA,aAAA,EAAA,eAAA,CAAA;;;AChFT,IAAM,UAAA,GAAa;AAAA,EACxB,KAAA,EAAO,+DAAA;AAAA,EACP,IAAA,EAAM,kEAAA;AAAA,EACN,OAAA,EAAS,8DAAA;AAAA,EACT,SAAA,EAAW;AACb,CAAA;AC6BA,SAAS,QAAA,CAAS;AAAA,EAChB,QAAA;AAAA,EACA,QAAA,GAAW,OAAA;AAAA,EACX,mBAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,kBAAA,GAAqB,IAAA;AAAA,EACrB,SAAA,GAAY,IAAA;AAAA,EACZ,aAAA;AAAA,EACA,eAAA,GAAkB,cAAA;AAAA,EAClB,cAAA,GAAiB,CAAA;AAAA,EACjB,eAAA,GAAkB;AACpB,CAAA,EAAkB;AAChB,EAAA,MAAM,EAAE,QAAQ,QAAA,EAAU,WAAA,EAAa,aAAa,cAAA,EAAgB,eAAA,KAAoB,aAAA,EAAc;AACtG,EAAA,MAAM,aAAA,GAAgBA,OAA8B,IAAI,CAAA;AACxD,EAAA,MAAM,gBAAA,GAAmBA,OAAO,KAAK,CAAA;AAGrC,EAAA,MAAM,kBAAA,GACJ,KAAK,GAAA,CAAI,QAAA,CAAS,YAAY,eAAA,CAAgB,SAAS,CAAA,GAAI,IAAA,IAC3D,IAAA,CAAK,GAAA,CAAI,SAAS,QAAA,GAAW,eAAA,CAAgB,QAAQ,CAAA,GAAI,IAAA,IACzD,IAAA,CAAK,IAAI,QAAA,CAAS,IAAA,GAAO,eAAA,CAAgB,IAAI,CAAA,GAAI,GAAA;AAEnD,EAAA,MAAM,UAAA,GAAaC,WAAAA;AAAA,IACjB,CAAC,GAAA,KAA8B;AAC7B,MAAA,WAAA,CAAY;AAAA,QACV,SAAA,EAAW,IAAI,SAAA,CAAU,SAAA;AAAA,QACzB,QAAA,EAAU,IAAI,SAAA,CAAU,QAAA;AAAA,QACxB,IAAA,EAAM,IAAI,SAAA,CAAU,IAAA;AAAA,QACpB,OAAA,EAAS,IAAI,SAAA,CAAU,OAAA;AAAA,QACvB,KAAA,EAAO,IAAI,SAAA,CAAU;AAAA,OACtB,CAAA;AAAA,IACH,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,MAAM,eAAA,GAAkBA,YAAY,MAAM;AACxC,IAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAE3B,IAAA,IAAI,cAAc,OAAA,EAAS;AACzB,MAAA,YAAA,CAAa,cAAc,OAAO,CAAA;AAClC,MAAA,aAAA,CAAc,OAAA,GAAU,IAAA;AAAA,IAC1B;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,aAAA,GAAgBA,YAAY,MAAM;AACtC,IAAA,gBAAA,CAAiB,OAAA,GAAU,KAAA;AAE3B,IAAA,IAAI,iBAAiB,CAAA,EAAG;AACtB,MAAA,aAAA,CAAc,OAAA,GAAU,WAAW,MAAM;AACvC,QAAA,IAAI,CAAC,iBAAiB,OAAA,EAAS;AAC7B,UAAA,cAAA,EAAe;AAAA,QACjB;AAAA,MACF,GAAG,cAAc,CAAA;AAAA,IACnB;AAAA,EACF,CAAA,EAAG,CAAC,cAAA,EAAgB,cAAc,CAAC,CAAA;AAEnC,EAAA,MAAM,UAAA,GAAaA,YAAY,MAAM;AACnC,IAAA,WAAA,CAAY,IAAI,CAAA;AAAA,EAClB,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAGhB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,cAAc,OAAA,EAAS;AACzB,QAAA,YAAA,CAAa,cAAc,OAAO,CAAA;AAAA,MACpC;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,aAAA,GAAgB,QAAA,IAAY,UAAA,GAC9B,UAAA,CAAW,QAAuB,CAAA,GAClC,QAAA;AAEJ,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAAC,GAAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,MAAA;AAAA,QACJ,GAAG,QAAA;AAAA,QACJ,MAAA,EAAQ,UAAA;AAAA,QACR,WAAA,EAAa,eAAA;AAAA,QACb,SAAA,EAAW,aAAA;AAAA,QACX,MAAA,EAAQ,UAAA;AAAA,QACR,QAAA,EAAU,aAAA;AAAA,QACV,mBAAA;AAAA,QACA,kBAAA,EAAoB,kBAAA,GAAqB,EAAC,GAAI,KAAA;AAAA,QAC9C,SAAA;AAAA,QACA,KAAA,EAAO;AAAA,UACL,KAAA,EAAO,MAAA;AAAA,UACP,MAAA,EAAQ,MAAA;AAAA,UACR,GAAG;AAAA,SACL;AAAA,QACA,MAAA;AAAA,QAEC;AAAA;AAAA,KACH;AAAA,oBAGA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mDAAA,EAEZ,QAAA,EAAA;AAAA,MAAA,eAAA,IAAmB,kBAAA,oBAClB,IAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAK,QAAA;AAAA,UACL,OAAA,EAAS,cAAA;AAAA,UACT,SAAA,EAAW,EAAA;AAAA,YACT,qDAAA;AAAA,YACA,uEAAA;AAAA,YACA;AAAA,WACF;AAAA,UAEA,QAAA,EAAA;AAAA,4BAAAA,GAAAA,CAAC,SAAA,EAAA,EAAU,SAAA,EAAU,SAAA,EAAU,CAAA;AAAA,YAAE;AAAA;AAAA;AAAA,OAEnC;AAAA,MAID,aAAA,oBACC,IAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAM,aAAA;AAAA,UACN,MAAA,EAAO,QAAA;AAAA,UACP,GAAA,EAAI,qBAAA;AAAA,UACJ,SAAA,EAAW,EAAA;AAAA,YACT,qDAAA;AAAA,YACA,uEAAA;AAAA,YACA;AAAA,WACF;AAAA,UAEA,QAAA,EAAA;AAAA,4BAAAA,GAAAA,CAAC,YAAA,EAAA,EAAa,SAAA,EAAU,SAAA,EAAU,CAAA;AAAA,YACjC;AAAA;AAAA;AAAA;AACH,KAAA,EAEJ;AAAA,GAAA,EACF,CAAA;AAEJ;AAtIS,MAAA,CAAA,QAAA,EAAA,UAAA,CAAA;AAwIF,SAAS,YAAA,CAAa;AAAA,EAC3B,QAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAsB;AACpB,EAAA,uBACEA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAsB,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAQ,QAAA,EAAU,UAAA,IAC3E,QAAA,kBAAAA,GAAAA,CAAC,WAAA,EAAA,EAAY,eAAA,EACX,QAAA,kBAAAA,GAAAA,CAAC,YAAU,GAAG,KAAA,EAAQ,QAAA,EAAS,CAAA,EACjC,CAAA,EACF,CAAA;AAEJ;AAbgB,MAAA,CAAA,YAAA,EAAA,cAAA,CAAA;AAkBT,SAAS,QAAQ,KAAA,EAAmD;AACzE,EAAA,uBAAOA,GAAAA,CAAC,QAAA,EAAA,EAAU,GAAG,KAAA,EAAO,CAAA;AAC9B;AAFgB,MAAA,CAAA,OAAA,EAAA,SAAA,CAAA","file":"MapContainer-GXQLP5WY.mjs","sourcesContent":["'use client'\n\nimport {\n createContext,\n useContext,\n useState,\n useRef,\n useMemo,\n useCallback,\n type ReactNode,\n} from 'react'\nimport type { MapRef } from 'react-map-gl/maplibre'\nimport type { MapContextValue, MapViewport, MarkerData } from '../types'\n\nconst MapContext = createContext<MapContextValue | null>(null)\n\nexport interface MapProviderProps {\n children: ReactNode\n initialViewport?: Partial<MapViewport>\n}\n\nconst DEFAULT_VIEWPORT: MapViewport = {\n longitude: 115.1889,\n latitude: -8.4095,\n zoom: 10,\n bearing: 0,\n pitch: 0,\n}\n\nexport function MapProvider({ children, initialViewport }: MapProviderProps) {\n const mapRef = useRef<MapRef | null>(null)\n const initialViewportRef = useRef<MapViewport>({\n ...DEFAULT_VIEWPORT,\n ...initialViewport,\n })\n const [viewport, setViewportState] = useState<MapViewport>(initialViewportRef.current)\n const [markers, setMarkers] = useState<MarkerData[]>([])\n const [selectedMarker, setSelectedMarker] = useState<MarkerData | null>(null)\n const [hoveredFeature, setHoveredFeature] = useState<GeoJSON.Feature | null>(null)\n const [isLoaded, setIsLoaded] = useState(false)\n\n const setViewport = useCallback((newViewport: Partial<MapViewport>) => {\n setViewportState((prev) => ({ ...prev, ...newViewport }))\n }, [])\n\n const resetToInitial = useCallback(() => {\n const map = mapRef.current\n if (map) {\n map.flyTo({\n center: [initialViewportRef.current.longitude, initialViewportRef.current.latitude],\n zoom: initialViewportRef.current.zoom,\n bearing: initialViewportRef.current.bearing ?? 0,\n pitch: initialViewportRef.current.pitch ?? 0,\n duration: 1000,\n })\n }\n }, [])\n\n const value = useMemo<MapContextValue>(\n () => ({\n mapRef,\n viewport,\n setViewport,\n initialViewport: initialViewportRef.current,\n resetToInitial,\n markers,\n setMarkers,\n selectedMarker,\n setSelectedMarker,\n hoveredFeature,\n setHoveredFeature,\n isLoaded,\n setIsLoaded,\n }),\n [viewport, setViewport, resetToInitial, markers, selectedMarker, hoveredFeature, isLoaded]\n )\n\n return <MapContext.Provider value={value}>{children}</MapContext.Provider>\n}\n\nexport function useMapContext(): MapContextValue {\n const context = useContext(MapContext)\n if (!context) {\n throw new Error('useMapContext must be used within a MapProvider')\n }\n return context\n}\n\nexport { MapContext }\n","export const MAP_STYLES = {\n light: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',\n dark: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',\n streets: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',\n satellite: 'https://api.maptiler.com/maps/satellite/style.json',\n} as const\n\nexport type MapStyleKey = keyof typeof MAP_STYLES\n\nexport function getMapStyle(key: MapStyleKey | string): string {\n if (key in MAP_STYLES) {\n return MAP_STYLES[key as MapStyleKey]\n }\n return key\n}\n","'use client'\n\nimport { useCallback, useEffect, useRef, type ReactNode } from 'react'\nimport Map, { type ViewStateChangeEvent } from 'react-map-gl/maplibre'\nimport { ExternalLink, RotateCcw } from 'lucide-react'\nimport { cn } from '@djangocfg/ui-core/lib'\nimport 'maplibre-gl/dist/maplibre-gl.css'\n\nimport { MapProvider, useMapContext } from '../context'\nimport { MAP_STYLES } from '../styles'\nimport type { MapViewport, MapStyleKey } from '../types'\n\nexport interface MapContainerProps {\n children?: ReactNode\n initialViewport?: Partial<MapViewport>\n mapStyle?: MapStyleKey | string\n interactiveLayerIds?: string[]\n className?: string\n style?: React.CSSProperties\n cursor?: string\n attributionControl?: boolean\n reuseMaps?: boolean\n /** URL to open in external maps app (shows \"Open in Maps\" button if provided) */\n openInMapsUrl?: string\n /** Label for the open in maps button */\n openInMapsLabel?: string\n /** Auto-reset to initial viewport after N ms of inactivity (0 = disabled) */\n autoResetDelay?: number\n /** Show reset button */\n showResetButton?: boolean\n}\n\ninterface MapInnerProps extends Omit<MapContainerProps, 'initialViewport'> {}\n\nfunction MapInner({\n children,\n mapStyle = 'light',\n interactiveLayerIds,\n style,\n cursor,\n attributionControl = true,\n reuseMaps = true,\n openInMapsUrl,\n openInMapsLabel = 'Open in Maps',\n autoResetDelay = 0,\n showResetButton = false,\n}: MapInnerProps) {\n const { mapRef, viewport, setViewport, setIsLoaded, resetToInitial, initialViewport } = useMapContext()\n const resetTimerRef = useRef<NodeJS.Timeout | null>(null)\n const isInteractingRef = useRef(false)\n\n // Check if viewport has changed from initial\n const hasViewportChanged =\n Math.abs(viewport.longitude - initialViewport.longitude) > 0.0001 ||\n Math.abs(viewport.latitude - initialViewport.latitude) > 0.0001 ||\n Math.abs(viewport.zoom - initialViewport.zoom) > 0.1\n\n const handleMove = useCallback(\n (evt: ViewStateChangeEvent) => {\n setViewport({\n longitude: evt.viewState.longitude,\n latitude: evt.viewState.latitude,\n zoom: evt.viewState.zoom,\n bearing: evt.viewState.bearing,\n pitch: evt.viewState.pitch,\n })\n },\n [setViewport]\n )\n\n const handleMoveStart = useCallback(() => {\n isInteractingRef.current = true\n // Clear any pending reset timer\n if (resetTimerRef.current) {\n clearTimeout(resetTimerRef.current)\n resetTimerRef.current = null\n }\n }, [])\n\n const handleMoveEnd = useCallback(() => {\n isInteractingRef.current = false\n // Start auto-reset timer if enabled\n if (autoResetDelay > 0) {\n resetTimerRef.current = setTimeout(() => {\n if (!isInteractingRef.current) {\n resetToInitial()\n }\n }, autoResetDelay)\n }\n }, [autoResetDelay, resetToInitial])\n\n const handleLoad = useCallback(() => {\n setIsLoaded(true)\n }, [setIsLoaded])\n\n // Cleanup timer on unmount\n useEffect(() => {\n return () => {\n if (resetTimerRef.current) {\n clearTimeout(resetTimerRef.current)\n }\n }\n }, [])\n\n const resolvedStyle = mapStyle in MAP_STYLES\n ? MAP_STYLES[mapStyle as MapStyleKey]\n : mapStyle\n\n return (\n <>\n <Map\n ref={mapRef}\n {...viewport}\n onMove={handleMove}\n onMoveStart={handleMoveStart}\n onMoveEnd={handleMoveEnd}\n onLoad={handleLoad}\n mapStyle={resolvedStyle}\n interactiveLayerIds={interactiveLayerIds}\n attributionControl={attributionControl ? {} : false}\n reuseMaps={reuseMaps}\n style={{\n width: '100%',\n height: '100%',\n ...style,\n }}\n cursor={cursor}\n >\n {children}\n </Map>\n\n {/* Map overlay buttons */}\n <div className=\"absolute bottom-3 right-3 flex items-center gap-2\">\n {/* Reset button */}\n {showResetButton && hasViewportChanged && (\n <button\n type=\"button\"\n onClick={resetToInitial}\n className={cn(\n 'inline-flex items-center gap-2 px-3 py-2 rounded-lg',\n 'bg-background/95 backdrop-blur-sm text-foreground text-sm font-medium',\n 'hover:bg-background transition-colors shadow-sm border border-border'\n )}\n >\n <RotateCcw className=\"w-4 h-4\" />\n Reset\n </button>\n )}\n\n {/* Open in Maps button */}\n {openInMapsUrl && (\n <a\n href={openInMapsUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cn(\n 'inline-flex items-center gap-2 px-4 py-2 rounded-lg',\n 'bg-background/95 backdrop-blur-sm text-foreground text-sm font-medium',\n 'hover:bg-background transition-colors shadow-sm border border-border'\n )}\n >\n <ExternalLink className=\"w-4 h-4\" />\n {openInMapsLabel}\n </a>\n )}\n </div>\n </>\n )\n}\n\nexport function MapContainer({\n children,\n initialViewport,\n className,\n ...props\n}: MapContainerProps) {\n return (\n <div className={className} style={{ width: '100%', height: '100%', position: 'relative' }}>\n <MapProvider initialViewport={initialViewport}>\n <MapInner {...props}>{children}</MapInner>\n </MapProvider>\n </div>\n )\n}\n\n/**\n * Use this when you need the map inside an existing MapProvider\n */\nexport function MapView(props: Omit<MapContainerProps, 'initialViewport'>) {\n return <MapInner {...props} />\n}\n"]}