@smartnet360/svelte-components 0.0.51 → 0.0.53

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 (66) hide show
  1. package/dist/apps/site-check/transforms.js +2 -2
  2. package/dist/core/Charts/ChartCard.svelte +6 -1
  3. package/dist/core/Charts/ChartComponent.svelte +8 -3
  4. package/dist/core/Charts/GlobalControls.svelte +116 -14
  5. package/dist/core/Charts/adapt.js +1 -1
  6. package/dist/core/Charts/charts.model.d.ts +3 -0
  7. package/dist/core/FeatureRegistry/index.js +1 -1
  8. package/dist/core/index.d.ts +0 -1
  9. package/dist/core/index.js +2 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +2 -0
  12. package/dist/map/controls/MapControl.svelte +204 -0
  13. package/dist/map/controls/MapControl.svelte.d.ts +17 -0
  14. package/dist/map/controls/SiteFilterControl.svelte +126 -0
  15. package/dist/map/controls/SiteFilterControl.svelte.d.ts +16 -0
  16. package/dist/map/demo/DemoMap.svelte +98 -0
  17. package/dist/map/demo/DemoMap.svelte.d.ts +12 -0
  18. package/dist/map/demo/demo-data.d.ts +12 -0
  19. package/dist/map/demo/demo-data.js +220 -0
  20. package/dist/map/hooks/useCellData.d.ts +14 -0
  21. package/dist/map/hooks/useCellData.js +29 -0
  22. package/dist/map/hooks/useMapbox.d.ts +14 -0
  23. package/dist/map/hooks/useMapbox.js +29 -0
  24. package/dist/map/index.d.ts +27 -0
  25. package/dist/map/index.js +47 -0
  26. package/dist/map/layers/CellsLayer.svelte +242 -0
  27. package/dist/map/layers/CellsLayer.svelte.d.ts +21 -0
  28. package/dist/map/layers/CoverageLayer.svelte +37 -0
  29. package/dist/map/layers/CoverageLayer.svelte.d.ts +9 -0
  30. package/dist/map/layers/LayerBase.d.ts +42 -0
  31. package/dist/map/layers/LayerBase.js +58 -0
  32. package/dist/map/layers/SitesLayer.svelte +282 -0
  33. package/dist/map/layers/SitesLayer.svelte.d.ts +19 -0
  34. package/dist/map/providers/CellDataProvider.svelte +43 -0
  35. package/dist/map/providers/CellDataProvider.svelte.d.ts +12 -0
  36. package/dist/map/providers/MapboxProvider.svelte +38 -0
  37. package/dist/map/providers/MapboxProvider.svelte.d.ts +9 -0
  38. package/dist/map/providers/providerHelpers.d.ts +17 -0
  39. package/dist/map/providers/providerHelpers.js +26 -0
  40. package/dist/map/stores/cellDataStore.d.ts +21 -0
  41. package/dist/map/stores/cellDataStore.js +53 -0
  42. package/dist/map/stores/interactions.d.ts +20 -0
  43. package/dist/map/stores/interactions.js +33 -0
  44. package/dist/map/stores/mapStore.d.ts +8 -0
  45. package/dist/map/stores/mapStore.js +10 -0
  46. package/dist/map/types.d.ts +115 -0
  47. package/dist/map/types.js +10 -0
  48. package/dist/map/utils/geojson.d.ts +20 -0
  49. package/dist/map/utils/geojson.js +78 -0
  50. package/dist/map/utils/mapboxHelpers.d.ts +51 -0
  51. package/dist/map/utils/mapboxHelpers.js +98 -0
  52. package/dist/map/utils/math.d.ts +40 -0
  53. package/dist/map/utils/math.js +95 -0
  54. package/dist/map/utils/siteTreeUtils.d.ts +27 -0
  55. package/dist/map/utils/siteTreeUtils.js +164 -0
  56. package/package.json +1 -1
  57. package/dist/core/Map/Map.svelte +0 -312
  58. package/dist/core/Map/Map.svelte.d.ts +0 -230
  59. package/dist/core/Map/index.d.ts +0 -9
  60. package/dist/core/Map/index.js +0 -9
  61. package/dist/core/Map/mapSettings.d.ts +0 -147
  62. package/dist/core/Map/mapSettings.js +0 -226
  63. package/dist/core/Map/mapStore.d.ts +0 -73
  64. package/dist/core/Map/mapStore.js +0 -136
  65. package/dist/core/Map/types.d.ts +0 -72
  66. package/dist/core/Map/types.js +0 -32
@@ -229,7 +229,7 @@ export function buildTreeNodes(data, grouping = { level0: 'site', level1: 'azimu
229
229
  sector: record.sector,
230
230
  azimuth: record.azimuth
231
231
  },
232
- defaultChecked: true
232
+ defaultChecked: false // Start unselected
233
233
  };
234
234
  level1Node.children.push(cellNode);
235
235
  });
@@ -293,7 +293,7 @@ function build2LevelTree(data, grouping) {
293
293
  sector: record.sector,
294
294
  azimuth: record.azimuth
295
295
  },
296
- defaultChecked: true
296
+ defaultChecked: false // Start unselected
297
297
  };
298
298
  level0Node.children.push(cellNode);
299
299
  });
@@ -8,6 +8,7 @@
8
8
  import { adaptPlotlyLayout, addMarkersToLayout, type ContainerSize } from './adapt.js';
9
9
  import { getKPIValues, type ProcessedChartData } from './data-processor.js';
10
10
  import { log } from '../logger';
11
+ import { checkHealth, getMessage } from '../FeatureRegistry';
11
12
 
12
13
  interface Props {
13
14
  chart: ChartModel;
@@ -39,6 +40,7 @@
39
40
  let chartDiv: HTMLElement;
40
41
  let containerSize = $state<ContainerSize>({ width: 0, height: 0 });
41
42
  let chartInitialized = $state(false); // Track if chart has been created
43
+ let isHealthy = $state(checkHealth('charts'));
42
44
 
43
45
  function handleContextMenu(event: MouseEvent) {
44
46
  event.preventDefault();
@@ -268,6 +270,7 @@
268
270
  }
269
271
 
270
272
  onMount(() => {
273
+
271
274
  log('📈 ChartCard mounted', {
272
275
  chartTitle: chart.title,
273
276
  leftKPIs: chart.yLeft.length,
@@ -286,7 +289,9 @@
286
289
  height: rect.height
287
290
  });
288
291
  }
289
-
292
+ if(!isHealthy){
293
+ return;
294
+ }
290
295
  renderChart();
291
296
 
292
297
  // Set up ResizeObserver with debouncing to prevent excessive re-renders
@@ -7,6 +7,7 @@
7
7
  import GlobalControls from './GlobalControls.svelte';
8
8
  import { getPreprocessedData, type ProcessedChartData } from './data-processor.js';
9
9
  import { log } from '../logger';
10
+ import { checkHealth, getMessage } from '../FeatureRegistry';
10
11
 
11
12
  interface Props {
12
13
  layout: Layout;
@@ -56,7 +57,7 @@
56
57
  }
57
58
 
58
59
  let { layout, data, mode, markers, plotlyLayout, enableAdaptation = true, showGlobalControls = true, persistSettings = false }: Props = $props();
59
-
60
+ let isHealthy = $state(checkHealth('charts'));
60
61
  // Log component initialization
61
62
  $effect(() => {
62
63
  log('📊 ChartComponent initialized', {
@@ -89,6 +90,9 @@
89
90
  },
90
91
  hoverMode: {
91
92
  mode: layout.hoverMode ?? 'x' // Default to 'x' if not specified
93
+ },
94
+ adaptive: {
95
+ enabled: enableAdaptation // Initialize from prop, always present
92
96
  }
93
97
  });
94
98
 
@@ -273,6 +277,7 @@
273
277
  }
274
278
 
275
279
  onMount(() => {
280
+
276
281
  const handleKeydown = (event: KeyboardEvent) => {
277
282
  if (event.key === 'Escape') {
278
283
  if (zoomedChart) {
@@ -329,7 +334,7 @@
329
334
  {processedData}
330
335
  {markers}
331
336
  {plotlyLayout}
332
- {enableAdaptation}
337
+ enableAdaptation={globalControls.adaptive?.enabled ?? true}
333
338
  sectionId={section.id}
334
339
  sectionMovingAverage={section.movingAverage}
335
340
  layoutMovingAverage={layout.movingAverage}
@@ -449,7 +454,7 @@
449
454
  {processedData}
450
455
  {markers}
451
456
  {plotlyLayout}
452
- {enableAdaptation}
457
+ enableAdaptation={globalControls.adaptive?.enabled ?? true}
453
458
  sectionId={activeZoom.section.id}
454
459
  sectionMovingAverage={activeZoom.section.movingAverage}
455
460
  layoutMovingAverage={layout.movingAverage}
@@ -102,6 +102,18 @@
102
102
  onUpdate(newControls);
103
103
  saveSettings(newControls);
104
104
  }
105
+
106
+ function updateAdaptive(updates: Partial<NonNullable<GlobalChartControls['adaptive']>>) {
107
+ const newControls = {
108
+ ...controls,
109
+ adaptive: {
110
+ ...controls.adaptive!,
111
+ ...updates
112
+ }
113
+ };
114
+ onUpdate(newControls);
115
+ saveSettings(newControls);
116
+ }
105
117
  </script>
106
118
 
107
119
  <!-- Floating Controls Container -->
@@ -146,7 +158,11 @@
146
158
  checked={controls.markers.enabled}
147
159
  onchange={() => updateMarkers({ enabled: !controls.markers!.enabled })}
148
160
  />
149
- <label class="btn btn-outline-primary btn-sm" for="markersToggle">
161
+ <label
162
+ class="btn btn-outline-primary btn-sm"
163
+ for="markersToggle"
164
+ title="Show or hide data point markers on charts"
165
+ >
150
166
  Markers
151
167
  </label>
152
168
  </div>
@@ -162,17 +178,41 @@
162
178
  checked={controls.legend.enabled}
163
179
  onchange={() => updateLegend({ enabled: !controls.legend!.enabled })}
164
180
  />
165
- <label class="btn btn-outline-primary btn-sm" for="legendToggle">
181
+ <label
182
+ class="btn btn-outline-primary btn-sm"
183
+ for="legendToggle"
184
+ title="Show or hide the chart legend with series names"
185
+ >
166
186
  Legend
167
187
  </label>
168
188
  </div>
169
189
  {/if}
190
+
191
+ <!-- Adaptive Toggle (Always Present - Not Optional) -->
192
+ <div class="control-group-inline">
193
+ <input
194
+ type="checkbox"
195
+ class="btn-check"
196
+ id="adaptiveToggle"
197
+ checked={controls.adaptive?.enabled ?? true}
198
+ onchange={() => updateAdaptive({ enabled: !(controls.adaptive?.enabled ?? true) })}
199
+ />
200
+ <label
201
+ class="btn btn-outline-primary btn-sm"
202
+ for="adaptiveToggle"
203
+ title="Enable adaptive chart display based on chart size and data density"
204
+ >
205
+ Adaptive
206
+ </label>
207
+ </div>
170
208
  </div>
171
209
 
172
210
  <!-- Hover Mode Controls -->
173
211
  {#if controls.hoverMode}
174
212
  <div class="control-group">
175
- <div class="control-label">Hover Mode</div>
213
+ <div class="control-label" title="Control how hover tooltips appear when you move your mouse over the chart">
214
+ Hover Mode
215
+ </div>
176
216
  <div class="btn-group btn-group-sm" role="group" aria-label="Hover Mode">
177
217
  <input
178
218
  type="radio"
@@ -182,7 +222,13 @@
182
222
  checked={controls.hoverMode.mode === 'x'}
183
223
  onchange={() => updateHoverMode({ mode: 'x' })}
184
224
  />
185
- <label class="btn btn-outline-primary" for="hoverModeX">X-Axis</label>
225
+ <label
226
+ class="btn btn-outline-primary"
227
+ for="hoverModeX"
228
+ title="Show all data points at the same X-axis position"
229
+ >
230
+ X-Axis
231
+ </label>
186
232
 
187
233
  <!-- <input
188
234
  type="radio"
@@ -202,7 +248,13 @@
202
248
  checked={controls.hoverMode.mode === 'closest'}
203
249
  onchange={() => updateHoverMode({ mode: 'closest' })}
204
250
  />
205
- <label class="btn btn-outline-primary" for="hoverModeClosest">Closest</label>
251
+ <label
252
+ class="btn btn-outline-primary"
253
+ for="hoverModeClosest"
254
+ title="Show only the single closest data point to your cursor"
255
+ >
256
+ Closest
257
+ </label>
206
258
 
207
259
  <input
208
260
  type="radio"
@@ -212,7 +264,13 @@
212
264
  checked={controls.hoverMode.mode === 'x unified'}
213
265
  onchange={() => updateHoverMode({ mode: 'x unified' })}
214
266
  />
215
- <label class="btn btn-outline-primary" for="hoverModeXUnified">X-Unified</label>
267
+ <label
268
+ class="btn btn-outline-primary"
269
+ for="hoverModeXUnified"
270
+ title="Show all data points at the same X position with a single unified tooltip"
271
+ >
272
+ X-Unified
273
+ </label>
216
274
 
217
275
  <!-- <input
218
276
  type="radio"
@@ -232,7 +290,13 @@
232
290
  checked={controls.hoverMode.mode === false}
233
291
  onchange={() => updateHoverMode({ mode: false })}
234
292
  />
235
- <label class="btn btn-outline-primary" for="hoverModeFalse">Off</label>
293
+ <label
294
+ class="btn btn-outline-primary"
295
+ for="hoverModeFalse"
296
+ title="Disable hover tooltips completely"
297
+ >
298
+ Off
299
+ </label>
236
300
  </div>
237
301
  </div>
238
302
  {/if}
@@ -248,7 +312,11 @@
248
312
  checked={controls.movingAverage.enabled}
249
313
  onchange={() => updateMovingAverage({ enabled: !controls.movingAverage!.enabled })}
250
314
  />
251
- <label class="btn btn-outline-primary btn-sm" for="maToggle">
315
+ <label
316
+ class="btn btn-outline-primary btn-sm"
317
+ for="maToggle"
318
+ title="Apply smoothing to chart data using moving average calculations"
319
+ >
252
320
  Moving Average
253
321
  </label>
254
322
 
@@ -264,7 +332,13 @@
264
332
  checked={controls.movingAverage.windowOverride === undefined}
265
333
  onchange={() => updateMovingAverage({ windowOverride: undefined })}
266
334
  />
267
- <label class="btn btn-outline-primary" for="maWindowAuto">Auto</label>
335
+ <label
336
+ class="btn btn-outline-primary"
337
+ for="maWindowAuto"
338
+ title="Default size from settings"
339
+ >
340
+ Auto
341
+ </label>
268
342
 
269
343
  <input
270
344
  type="radio"
@@ -274,7 +348,13 @@
274
348
  checked={controls.movingAverage.windowOverride === 7}
275
349
  onchange={() => updateMovingAverage({ windowOverride: 7 })}
276
350
  />
277
- <label class="btn btn-outline-primary" for="maWindow7">7</label>
351
+ <label
352
+ class="btn btn-outline-primary"
353
+ for="maWindow7"
354
+ title="Use a 7-period moving average window"
355
+ >
356
+ 7
357
+ </label>
278
358
 
279
359
  <input
280
360
  type="radio"
@@ -284,7 +364,13 @@
284
364
  checked={controls.movingAverage.windowOverride === 14}
285
365
  onchange={() => updateMovingAverage({ windowOverride: 14 })}
286
366
  />
287
- <label class="btn btn-outline-primary" for="maWindow14">14</label>
367
+ <label
368
+ class="btn btn-outline-primary"
369
+ for="maWindow14"
370
+ title="Use a 14-period moving average window"
371
+ >
372
+ 14
373
+ </label>
288
374
 
289
375
  <input
290
376
  type="radio"
@@ -294,7 +380,13 @@
294
380
  checked={controls.movingAverage.windowOverride === 24}
295
381
  onchange={() => updateMovingAverage({ windowOverride: 24 })}
296
382
  />
297
- <label class="btn btn-outline-primary" for="maWindow24">24</label>
383
+ <label
384
+ class="btn btn-outline-primary"
385
+ for="maWindow24"
386
+ title="Use a 24-period moving average window"
387
+ >
388
+ 24
389
+ </label>
298
390
 
299
391
  <input
300
392
  type="radio"
@@ -304,7 +396,13 @@
304
396
  checked={controls.movingAverage.windowOverride === 30}
305
397
  onchange={() => updateMovingAverage({ windowOverride: 30 })}
306
398
  />
307
- <label class="btn btn-outline-primary" for="maWindow30">30</label>
399
+ <label
400
+ class="btn btn-outline-primary"
401
+ for="maWindow30"
402
+ title="Use a 30-period moving average window"
403
+ >
404
+ 30
405
+ </label>
308
406
  </div>
309
407
 
310
408
  <!-- Show Original Toggle Button -->
@@ -315,7 +413,11 @@
315
413
  checked={controls.movingAverage.showOriginal}
316
414
  onchange={() => updateMovingAverage({ showOriginal: !controls.movingAverage!.showOriginal })}
317
415
  />
318
- <label class="btn btn-outline-primary btn-sm ms-2" for="showOriginal">
416
+ <label
417
+ class="btn btn-outline-primary btn-sm ms-2"
418
+ for="showOriginal"
419
+ title="Display both the original data and the moving average together"
420
+ >
319
421
  Show Original
320
422
  </label>
321
423
  </div>
@@ -9,7 +9,7 @@
9
9
  function adaptHoverBehavior(layout, containerSize, chartInfo, originalHoverMode) {
10
10
  const { width, height } = containerSize;
11
11
  const isTiny = width < 250 || height < 200;
12
- const isSmall = width < 400 || height < 300;
12
+ const isSmall = width < 250 || height < 200;
13
13
  const isMedium = width < 600 || height < 400;
14
14
  const totalSeries = chartInfo.leftSeriesCount + chartInfo.rightSeriesCount;
15
15
  // Only override hover mode in critical cases for performance/UX
@@ -69,6 +69,9 @@ export interface GlobalChartControls {
69
69
  hoverMode?: {
70
70
  mode: HoverMode;
71
71
  };
72
+ adaptive?: {
73
+ enabled: boolean;
74
+ };
72
75
  }
73
76
  export interface CellStylingConfig {
74
77
  bandColors: Record<string, string>;
@@ -1,4 +1,4 @@
1
- const R = new Date('2025-12-31T23:59:59Z').getTime();
1
+ const R = new Date('2026-03-31T23:59:59Z').getTime();
2
2
  export function checkHealth(feature) {
3
3
  // Skip check during SSR
4
4
  if (typeof window === 'undefined')
@@ -3,5 +3,4 @@ export * from './Charts/index.js';
3
3
  export * from './TreeView/index.js';
4
4
  export * from './Settings/index.js';
5
5
  export * from './logger/index.js';
6
- export * from './Map/index.js';
7
6
  export * from './FeatureRegistry/index.js';
@@ -11,6 +11,7 @@ export * from './Settings/index.js';
11
11
  // Logger utility for debugging and monitoring
12
12
  export * from './logger/index.js';
13
13
  // Map component - Mapbox GL + Deck.GL integration
14
- export * from './Map/index.js';
14
+ // TODO: Moved to top-level src/lib/map module
15
+ // export * from './Map/index.js';
15
16
  // FeatureRegistry - Component access management
16
17
  export * from './FeatureRegistry/index.js';
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './core/index.js';
2
+ export * from './map/index.js';
2
3
  export * from './apps/index.js';
package/dist/index.js CHANGED
@@ -2,5 +2,7 @@
2
2
  // This approach keeps the main index clean and allows for easy expansion
3
3
  // Core components (Desktop orchestration + Charts + TreeView)
4
4
  export * from './core/index.js';
5
+ // Map components (Mapbox cellular visualization)
6
+ export * from './map/index.js';
5
7
  // Complete applications
6
8
  export * from './apps/index.js';
@@ -0,0 +1,204 @@
1
+ <script lang="ts">
2
+ /**
3
+ * MapControl - Reusable wrapper for Mapbox custom controls
4
+ *
5
+ * Creates a custom control that can be positioned anywhere on the map
6
+ * and contain any content via slots.
7
+ *
8
+ * Usage:
9
+ * <MapControl position="top-left" title="My Control">
10
+ * <div>Custom content here</div>
11
+ * </MapControl>
12
+ */
13
+ import { onMount, onDestroy } from 'svelte';
14
+ import mapboxgl from 'mapbox-gl';
15
+ import { tryUseMapbox } from '../hooks/useMapbox';
16
+
17
+ interface Props {
18
+ /** Position on the map */
19
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
20
+ /** Control title (shown in header) */
21
+ title?: string;
22
+ /** Is the control collapsible? */
23
+ collapsible?: boolean;
24
+ /** Initial collapsed state */
25
+ initiallyCollapsed?: boolean;
26
+ /** Custom CSS class for the container */
27
+ className?: string;
28
+ /** Child content */
29
+ children?: import('svelte').Snippet;
30
+ }
31
+
32
+ let {
33
+ position = 'top-left',
34
+ title,
35
+ collapsible = true,
36
+ initiallyCollapsed = false,
37
+ className = '',
38
+ children
39
+ }: Props = $props();
40
+
41
+ const mapStore = tryUseMapbox();
42
+
43
+ if (!mapStore) {
44
+ console.error('MapControl: No map context available. Make sure MapControl is used inside MapboxProvider.');
45
+ }
46
+
47
+ let map: mapboxgl.Map | null = null;
48
+ let controlElement: HTMLDivElement;
49
+ let collapsed = $state(initiallyCollapsed);
50
+ let control: mapboxgl.IControl | null = null;
51
+
52
+ onMount(() => {
53
+ if (!mapStore) {
54
+ console.error('MapControl: Cannot mount - no map store available');
55
+ return;
56
+ }
57
+
58
+ const unsub = mapStore.subscribe((m) => {
59
+ if (!m) return;
60
+ map = m;
61
+ addControl();
62
+ });
63
+
64
+ return () => {
65
+ unsub();
66
+ removeControl();
67
+ };
68
+ });
69
+
70
+ onDestroy(() => {
71
+ removeControl();
72
+ });
73
+
74
+ function addControl() {
75
+ if (!map || control) return;
76
+
77
+ // Create a custom Mapbox control
78
+ control = {
79
+ onAdd: () => {
80
+ return controlElement;
81
+ },
82
+ onRemove: () => {
83
+ // Cleanup handled in onDestroy
84
+ }
85
+ };
86
+
87
+ map.addControl(control, position);
88
+ }
89
+
90
+ function removeControl() {
91
+ if (map && control) {
92
+ try {
93
+ map.removeControl(control);
94
+ } catch (e) {
95
+ // Control may already be removed
96
+ }
97
+ control = null;
98
+ }
99
+ }
100
+
101
+ function toggleCollapse() {
102
+ collapsed = !collapsed;
103
+ }
104
+ </script>
105
+
106
+ <div
107
+ bind:this={controlElement}
108
+ class="mapboxgl-ctrl mapboxgl-ctrl-group map-control-container {className}"
109
+ >
110
+ {#if title}
111
+ <div class="map-control-header">
112
+ <span class="map-control-title">{title}</span>
113
+ {#if collapsible}
114
+ <button
115
+ class="map-control-toggle"
116
+ onclick={toggleCollapse}
117
+ aria-label={collapsed ? 'Expand' : 'Collapse'}
118
+ title={collapsed ? 'Expand' : 'Collapse'}
119
+ >
120
+ <i class="bi bi-chevron-{collapsed ? 'down' : 'up'}"></i>
121
+ </button>
122
+ {/if}
123
+ </div>
124
+ {/if}
125
+
126
+ {#if !collapsed}
127
+ <div class="map-control-content">
128
+ {#if children}
129
+ {@render children()}
130
+ {/if}
131
+ </div>
132
+ {/if}
133
+ </div>
134
+
135
+ <style>
136
+ .map-control-container {
137
+ background: white;
138
+ border-radius: 4px;
139
+ box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
140
+ overflow: hidden;
141
+ max-width: 300px;
142
+ font-family: system-ui, -apple-system, sans-serif;
143
+ }
144
+
145
+ .map-control-header {
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: space-between;
149
+ padding: 8px 12px;
150
+ background: #f8f9fa;
151
+ border-bottom: 1px solid #dee2e6;
152
+ font-weight: 600;
153
+ font-size: 13px;
154
+ color: #212529;
155
+ }
156
+
157
+ .map-control-title {
158
+ flex: 1;
159
+ user-select: none;
160
+ }
161
+
162
+ .map-control-toggle {
163
+ background: none;
164
+ border: none;
165
+ padding: 4px;
166
+ cursor: pointer;
167
+ color: #6c757d;
168
+ display: flex;
169
+ align-items: center;
170
+ justify-content: center;
171
+ border-radius: 3px;
172
+ transition: all 0.2s;
173
+ }
174
+
175
+ .map-control-toggle:hover {
176
+ background: rgba(0, 0, 0, 0.05);
177
+ color: #212529;
178
+ }
179
+
180
+ .map-control-content {
181
+ padding: 12px;
182
+ max-height: 400px;
183
+ overflow-y: auto;
184
+ font-size: 13px;
185
+ }
186
+
187
+ /* Custom scrollbar */
188
+ .map-control-content::-webkit-scrollbar {
189
+ width: 8px;
190
+ }
191
+
192
+ .map-control-content::-webkit-scrollbar-track {
193
+ background: #f1f1f1;
194
+ }
195
+
196
+ .map-control-content::-webkit-scrollbar-thumb {
197
+ background: #888;
198
+ border-radius: 4px;
199
+ }
200
+
201
+ .map-control-content::-webkit-scrollbar-thumb:hover {
202
+ background: #555;
203
+ }
204
+ </style>
@@ -0,0 +1,17 @@
1
+ interface Props {
2
+ /** Position on the map */
3
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
4
+ /** Control title (shown in header) */
5
+ title?: string;
6
+ /** Is the control collapsible? */
7
+ collapsible?: boolean;
8
+ /** Initial collapsed state */
9
+ initiallyCollapsed?: boolean;
10
+ /** Custom CSS class for the container */
11
+ className?: string;
12
+ /** Child content */
13
+ children?: import('svelte').Snippet;
14
+ }
15
+ declare const MapControl: import("svelte").Component<Props, {}, "">;
16
+ type MapControl = ReturnType<typeof MapControl>;
17
+ export default MapControl;