@smartnet360/svelte-components 0.0.85 → 0.0.87

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 (115) hide show
  1. package/dist/apps/antenna-pattern/components/AntennaControls.svelte +1 -106
  2. package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte +0 -36
  3. package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte +0 -2
  4. package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte +0 -22
  5. package/dist/apps/antenna-pattern/components/chart-engines/PolarAreaChart.svelte +0 -2
  6. package/dist/apps/antenna-pattern/components/chart-engines/PolarBarChart.svelte +0 -2
  7. package/dist/apps/antenna-pattern/components/chart-engines/PolarLineChart.svelte +0 -2
  8. package/dist/apps/site-check/data-loader.js +0 -8
  9. package/dist/core/Charts/GlobalControls.svelte +0 -4
  10. package/dist/core/Desktop/Grid/ResizeHandle.svelte +0 -7
  11. package/dist/core/Desktop/Grid/resizeStore.js +0 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +2 -0
  14. package/dist/map-v2/demo/DemoMap.svelte +0 -2
  15. package/dist/map-v2/demo/demo-cells.js +0 -1
  16. package/dist/map-v2/features/cells/layers/CellsLayer.svelte +7 -26
  17. package/dist/map-v2/features/cells/utils/cellTree.js +0 -29
  18. package/dist/map-v2/features/repeaters/layers/RepeaterLabelsLayer.svelte +3 -27
  19. package/dist/map-v2/features/repeaters/layers/RepeatersLayer.svelte +8 -25
  20. package/dist/map-v2/features/repeaters/utils/repeaterTree.js +0 -6
  21. package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +0 -8
  22. package/dist/map-v2/features/sites/utils/siteTreeUtils.js +0 -6
  23. package/dist/map-v3/core/components/Map.svelte +89 -0
  24. package/dist/map-v3/core/components/Map.svelte.d.ts +13 -0
  25. package/dist/map-v3/core/controls/FeatureSettingsControl.svelte +103 -0
  26. package/dist/map-v3/core/controls/FeatureSettingsControl.svelte.d.ts +15 -0
  27. package/dist/map-v3/core/controls/MapStyleControl.svelte +271 -0
  28. package/dist/map-v3/core/controls/MapStyleControl.svelte.d.ts +28 -0
  29. package/dist/map-v3/core/index.d.ts +6 -0
  30. package/dist/map-v3/core/index.js +5 -0
  31. package/dist/map-v3/core/stores/map.store.svelte.d.ts +8 -0
  32. package/dist/map-v3/core/stores/map.store.svelte.js +29 -0
  33. package/dist/map-v3/core/stores/viewport.store.svelte.d.ts +38 -0
  34. package/dist/map-v3/core/stores/viewport.store.svelte.js +107 -0
  35. package/dist/map-v3/demo/DemoMap.svelte +104 -0
  36. package/dist/map-v3/demo/DemoMap.svelte.d.ts +6 -0
  37. package/dist/map-v3/demo/demo-cells.d.ts +13 -0
  38. package/dist/map-v3/demo/demo-cells.js +130 -0
  39. package/dist/map-v3/demo/demo-data.d.ts +8 -0
  40. package/dist/map-v3/demo/demo-data.js +104 -0
  41. package/dist/map-v3/demo/demo-repeaters.d.ts +13 -0
  42. package/dist/map-v3/demo/demo-repeaters.js +73 -0
  43. package/dist/map-v3/features/cells/components/CellFilterControl.svelte +208 -0
  44. package/dist/map-v3/features/cells/components/CellFilterControl.svelte.d.ts +12 -0
  45. package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte +229 -0
  46. package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte.d.ts +7 -0
  47. package/dist/map-v3/features/cells/constants.d.ts +18 -0
  48. package/dist/map-v3/features/cells/constants.js +37 -0
  49. package/dist/map-v3/features/cells/layers/CellLabelsLayer.svelte +230 -0
  50. package/dist/map-v3/features/cells/layers/CellLabelsLayer.svelte.d.ts +11 -0
  51. package/dist/map-v3/features/cells/layers/CellsLayer.svelte +194 -0
  52. package/dist/map-v3/features/cells/layers/CellsLayer.svelte.d.ts +11 -0
  53. package/dist/map-v3/features/cells/layers/index.d.ts +2 -0
  54. package/dist/map-v3/features/cells/layers/index.js +2 -0
  55. package/dist/map-v3/features/cells/logic/geometry.d.ts +12 -0
  56. package/dist/map-v3/features/cells/logic/geometry.js +35 -0
  57. package/dist/map-v3/features/cells/logic/grouping.d.ts +18 -0
  58. package/dist/map-v3/features/cells/logic/grouping.js +30 -0
  59. package/dist/map-v3/features/cells/logic/tree-adapter.d.ts +11 -0
  60. package/dist/map-v3/features/cells/logic/tree-adapter.js +53 -0
  61. package/dist/map-v3/features/cells/stores/cell.data.svelte.d.ts +9 -0
  62. package/dist/map-v3/features/cells/stores/cell.data.svelte.js +16 -0
  63. package/dist/map-v3/features/cells/stores/cell.display.svelte.d.ts +25 -0
  64. package/dist/map-v3/features/cells/stores/cell.display.svelte.js +67 -0
  65. package/dist/map-v3/features/cells/stores/cell.registry.svelte.d.ts +23 -0
  66. package/dist/map-v3/features/cells/stores/cell.registry.svelte.js +68 -0
  67. package/dist/map-v3/features/cells/types.d.ts +62 -0
  68. package/dist/map-v3/features/cells/types.js +6 -0
  69. package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +148 -0
  70. package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte.d.ts +12 -0
  71. package/dist/map-v3/features/repeaters/components/RepeaterSettingsPanel.svelte +209 -0
  72. package/dist/map-v3/features/repeaters/components/RepeaterSettingsPanel.svelte.d.ts +7 -0
  73. package/dist/map-v3/features/repeaters/layers/RepeaterLabelsLayer.svelte +177 -0
  74. package/dist/map-v3/features/repeaters/layers/RepeaterLabelsLayer.svelte.d.ts +11 -0
  75. package/dist/map-v3/features/repeaters/layers/RepeatersLayer.svelte +163 -0
  76. package/dist/map-v3/features/repeaters/layers/RepeatersLayer.svelte.d.ts +11 -0
  77. package/dist/map-v3/features/repeaters/logic/geometry.d.ts +3 -0
  78. package/dist/map-v3/features/repeaters/logic/geometry.js +23 -0
  79. package/dist/map-v3/features/repeaters/logic/grouping.d.ts +8 -0
  80. package/dist/map-v3/features/repeaters/logic/grouping.js +20 -0
  81. package/dist/map-v3/features/repeaters/logic/tree-adapter.d.ts +8 -0
  82. package/dist/map-v3/features/repeaters/logic/tree-adapter.js +43 -0
  83. package/dist/map-v3/features/repeaters/stores/repeater.data.svelte.d.ts +8 -0
  84. package/dist/map-v3/features/repeaters/stores/repeater.data.svelte.js +13 -0
  85. package/dist/map-v3/features/repeaters/stores/repeater.display.svelte.d.ts +21 -0
  86. package/dist/map-v3/features/repeaters/stores/repeater.display.svelte.js +64 -0
  87. package/dist/map-v3/features/repeaters/stores/repeater.registry.svelte.d.ts +23 -0
  88. package/dist/map-v3/features/repeaters/stores/repeater.registry.svelte.js +68 -0
  89. package/dist/map-v3/features/repeaters/types.d.ts +18 -0
  90. package/dist/map-v3/features/repeaters/types.js +1 -0
  91. package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +119 -0
  92. package/dist/map-v3/features/sites/components/SiteFilterControl.svelte.d.ts +12 -0
  93. package/dist/map-v3/features/sites/components/SiteSettingsPanel.svelte +241 -0
  94. package/dist/map-v3/features/sites/components/SiteSettingsPanel.svelte.d.ts +7 -0
  95. package/dist/map-v3/features/sites/layers/SiteLabelsLayer.svelte +152 -0
  96. package/dist/map-v3/features/sites/layers/SiteLabelsLayer.svelte.d.ts +11 -0
  97. package/dist/map-v3/features/sites/layers/SitesLayer.svelte +132 -0
  98. package/dist/map-v3/features/sites/layers/SitesLayer.svelte.d.ts +11 -0
  99. package/dist/map-v3/features/sites/logic/tree-adapter.d.ts +9 -0
  100. package/dist/map-v3/features/sites/logic/tree-adapter.js +75 -0
  101. package/dist/map-v3/features/sites/stores/site.data.svelte.d.ts +8 -0
  102. package/dist/map-v3/features/sites/stores/site.data.svelte.js +40 -0
  103. package/dist/map-v3/features/sites/stores/site.display.svelte.d.ts +20 -0
  104. package/dist/map-v3/features/sites/stores/site.display.svelte.js +63 -0
  105. package/dist/map-v3/features/sites/stores/site.registry.svelte.d.ts +13 -0
  106. package/dist/map-v3/features/sites/stores/site.registry.svelte.js +83 -0
  107. package/dist/map-v3/features/sites/types.d.ts +12 -0
  108. package/dist/map-v3/features/sites/types.js +1 -0
  109. package/dist/map-v3/index.d.ts +30 -0
  110. package/dist/map-v3/index.js +36 -0
  111. package/dist/map-v3/shared/controls/MapControl.svelte +242 -0
  112. package/dist/map-v3/shared/controls/MapControl.svelte.d.ts +27 -0
  113. package/dist/map-v3/shared/index.d.ts +1 -0
  114. package/dist/map-v3/shared/index.js +1 -0
  115. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ export declare class SiteDisplayStore {
2
+ key: string;
3
+ radius: number;
4
+ color: string;
5
+ opacity: number;
6
+ strokeColor: string;
7
+ strokeWidth: number;
8
+ showLabels: boolean;
9
+ labelPixelDistance: number;
10
+ labelFontSize: number;
11
+ labelColor: string;
12
+ labelHaloColor: string;
13
+ labelHaloWidth: number;
14
+ labels: {
15
+ primary: string;
16
+ secondary: string;
17
+ };
18
+ constructor();
19
+ }
20
+ export declare function createSiteDisplayStore(): SiteDisplayStore;
@@ -0,0 +1,63 @@
1
+ import { browser } from '$app/environment';
2
+ export class SiteDisplayStore {
3
+ key = 'map-v3-site-display';
4
+ // Circle Appearance
5
+ radius = $state(6);
6
+ color = $state('#808080'); // Default gray for sites
7
+ opacity = $state(0.8);
8
+ strokeColor = $state('#ffffff');
9
+ strokeWidth = $state(1);
10
+ // Label Settings
11
+ showLabels = $state(false);
12
+ labelPixelDistance = $state(15);
13
+ labelFontSize = $state(12);
14
+ labelColor = $state('#333333');
15
+ labelHaloColor = $state('#ffffff');
16
+ labelHaloWidth = $state(1);
17
+ // Label Fields
18
+ labels = $state({ primary: 'siteId', secondary: 'none' });
19
+ constructor() {
20
+ if (browser) {
21
+ const saved = localStorage.getItem(this.key);
22
+ if (saved) {
23
+ try {
24
+ const parsed = JSON.parse(saved);
25
+ this.radius = parsed.radius ?? 6;
26
+ this.color = parsed.color ?? '#808080';
27
+ this.opacity = parsed.opacity ?? 0.8;
28
+ this.strokeColor = parsed.strokeColor ?? '#ffffff';
29
+ this.strokeWidth = parsed.strokeWidth ?? 1;
30
+ this.showLabels = parsed.showLabels ?? false;
31
+ this.labelPixelDistance = parsed.labelPixelDistance ?? 15;
32
+ this.labelFontSize = parsed.labelFontSize ?? 12;
33
+ this.labelColor = parsed.labelColor ?? '#333333';
34
+ this.labelHaloColor = parsed.labelHaloColor ?? '#ffffff';
35
+ this.labelHaloWidth = parsed.labelHaloWidth ?? 1;
36
+ this.labels = parsed.labels ?? { primary: 'siteId', secondary: 'none' };
37
+ }
38
+ catch (e) {
39
+ console.error('Failed to load site display settings', e);
40
+ }
41
+ }
42
+ $effect(() => {
43
+ localStorage.setItem(this.key, JSON.stringify({
44
+ radius: this.radius,
45
+ color: this.color,
46
+ opacity: this.opacity,
47
+ strokeColor: this.strokeColor,
48
+ strokeWidth: this.strokeWidth,
49
+ showLabels: this.showLabels,
50
+ labelPixelDistance: this.labelPixelDistance,
51
+ labelFontSize: this.labelFontSize,
52
+ labelColor: this.labelColor,
53
+ labelHaloColor: this.labelHaloColor,
54
+ labelHaloWidth: this.labelHaloWidth,
55
+ labels: this.labels
56
+ }));
57
+ });
58
+ }
59
+ }
60
+ }
61
+ export function createSiteDisplayStore() {
62
+ return new SiteDisplayStore();
63
+ }
@@ -0,0 +1,13 @@
1
+ export declare class SiteRegistry {
2
+ namespace: string;
3
+ hiddenSites: Set<string>;
4
+ groupColors: Map<string, string>;
5
+ version: number;
6
+ constructor(namespace: string);
7
+ isVisible(siteId: string): boolean;
8
+ getColor(level1: string, level2: string, defaultColor?: string): string;
9
+ setGroupColor(level1: string, level2: string, color: string): void;
10
+ toggleVisibility(siteId: string, visible?: boolean): void;
11
+ setVisible(siteIds: string[], visible: boolean): void;
12
+ }
13
+ export declare function createSiteRegistry(namespace: string): SiteRegistry;
@@ -0,0 +1,83 @@
1
+ import { browser } from '$app/environment';
2
+ export class SiteRegistry {
3
+ namespace;
4
+ // Map of siteId -> visible (boolean)
5
+ // If a siteId is NOT in the map, it is assumed visible (default true)
6
+ hiddenSites = $state(new Set());
7
+ // Map of Group ID (e.g. "L1:L2") -> Color
8
+ groupColors = $state(new Map());
9
+ // Version counter to trigger reactivity in layers
10
+ version = $state(0);
11
+ constructor(namespace) {
12
+ this.namespace = namespace;
13
+ if (browser) {
14
+ // Load state
15
+ const savedColors = localStorage.getItem(`${this.namespace}:site-colors`);
16
+ if (savedColors) {
17
+ try {
18
+ const parsed = JSON.parse(savedColors);
19
+ this.groupColors = new Map(parsed);
20
+ }
21
+ catch (e) {
22
+ console.warn('Failed to load site colors', e);
23
+ }
24
+ }
25
+ }
26
+ $effect(() => {
27
+ if (browser) {
28
+ localStorage.setItem(`${this.namespace}:site-colors`, JSON.stringify(Array.from(this.groupColors.entries())));
29
+ }
30
+ });
31
+ }
32
+ isVisible(siteId) {
33
+ return !this.hiddenSites.has(siteId);
34
+ }
35
+ getColor(level1, level2, defaultColor = '#3388ff') {
36
+ const key = `${level1}:${level2}`;
37
+ return this.groupColors.get(key) || defaultColor;
38
+ }
39
+ setGroupColor(level1, level2, color) {
40
+ const key = `${level1}:${level2}`;
41
+ this.groupColors.set(key, color);
42
+ // Force reactivity
43
+ this.groupColors = new Map(this.groupColors);
44
+ this.version++;
45
+ }
46
+ toggleVisibility(siteId, visible) {
47
+ const isHidden = this.hiddenSites.has(siteId);
48
+ const shouldBeVisible = visible !== undefined ? visible : isHidden;
49
+ if (shouldBeVisible) {
50
+ this.hiddenSites.delete(siteId);
51
+ }
52
+ else {
53
+ this.hiddenSites.add(siteId);
54
+ }
55
+ // Force reactivity
56
+ this.hiddenSites = new Set(this.hiddenSites);
57
+ this.version++;
58
+ }
59
+ setVisible(siteIds, visible) {
60
+ let changed = false;
61
+ for (const id of siteIds) {
62
+ if (visible) {
63
+ if (this.hiddenSites.has(id)) {
64
+ this.hiddenSites.delete(id);
65
+ changed = true;
66
+ }
67
+ }
68
+ else {
69
+ if (!this.hiddenSites.has(id)) {
70
+ this.hiddenSites.add(id);
71
+ changed = true;
72
+ }
73
+ }
74
+ }
75
+ if (changed) {
76
+ this.hiddenSites = new Set(this.hiddenSites);
77
+ this.version++;
78
+ }
79
+ }
80
+ }
81
+ export function createSiteRegistry(namespace) {
82
+ return new SiteRegistry(namespace);
83
+ }
@@ -0,0 +1,12 @@
1
+ export interface Site {
2
+ siteId: string;
3
+ siteName: string;
4
+ latitude: number;
5
+ longitude: number;
6
+ techs: string[];
7
+ fbands: string[];
8
+ provider: string;
9
+ level1: string;
10
+ level2: string;
11
+ cellCount: number;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ export * from './core';
2
+ export * from './shared';
3
+ export * from './features/cells/types';
4
+ export { default as CellsLayer } from './features/cells/layers/CellsLayer.svelte';
5
+ export { default as CellLabelsLayer } from './features/cells/layers/CellLabelsLayer.svelte';
6
+ export { default as CellFilterControl } from './features/cells/components/CellFilterControl.svelte';
7
+ export { default as CellSettingsPanel } from './features/cells/components/CellSettingsPanel.svelte';
8
+ export * from './features/cells/stores/cell.data.svelte';
9
+ export * from './features/cells/stores/cell.display.svelte';
10
+ export * from './features/cells/stores/cell.registry.svelte';
11
+ export * from './features/repeaters/types';
12
+ export { default as RepeatersLayer } from './features/repeaters/layers/RepeatersLayer.svelte';
13
+ export { default as RepeaterLabelsLayer } from './features/repeaters/layers/RepeaterLabelsLayer.svelte';
14
+ export { default as RepeaterFilterControl } from './features/repeaters/components/RepeaterFilterControl.svelte';
15
+ export { default as RepeaterSettingsPanel } from './features/repeaters/components/RepeaterSettingsPanel.svelte';
16
+ export * from './features/repeaters/stores/repeater.data.svelte';
17
+ export * from './features/repeaters/stores/repeater.display.svelte';
18
+ export * from './features/repeaters/stores/repeater.registry.svelte';
19
+ export * from './features/sites/types';
20
+ export { default as SitesLayer } from './features/sites/layers/SitesLayer.svelte';
21
+ export { default as SiteLabelsLayer } from './features/sites/layers/SiteLabelsLayer.svelte';
22
+ export { default as SiteFilterControl } from './features/sites/components/SiteFilterControl.svelte';
23
+ export { default as SiteSettingsPanel } from './features/sites/components/SiteSettingsPanel.svelte';
24
+ export * from './features/sites/stores/site.data.svelte';
25
+ export * from './features/sites/stores/site.display.svelte';
26
+ export * from './features/sites/stores/site.registry.svelte';
27
+ export { default as DemoMap } from './demo/DemoMap.svelte';
28
+ export { demoCells } from './demo/demo-cells';
29
+ export { demoRepeaters } from './demo/demo-repeaters';
30
+ export { demoSites } from './demo/demo-data';
@@ -0,0 +1,36 @@
1
+ // Core
2
+ export * from './core';
3
+ // Shared
4
+ export * from './shared';
5
+ // Features - Cells
6
+ export * from './features/cells/types';
7
+ export { default as CellsLayer } from './features/cells/layers/CellsLayer.svelte';
8
+ export { default as CellLabelsLayer } from './features/cells/layers/CellLabelsLayer.svelte';
9
+ export { default as CellFilterControl } from './features/cells/components/CellFilterControl.svelte';
10
+ export { default as CellSettingsPanel } from './features/cells/components/CellSettingsPanel.svelte';
11
+ export * from './features/cells/stores/cell.data.svelte';
12
+ export * from './features/cells/stores/cell.display.svelte';
13
+ export * from './features/cells/stores/cell.registry.svelte';
14
+ // Features - Repeaters
15
+ export * from './features/repeaters/types';
16
+ export { default as RepeatersLayer } from './features/repeaters/layers/RepeatersLayer.svelte';
17
+ export { default as RepeaterLabelsLayer } from './features/repeaters/layers/RepeaterLabelsLayer.svelte';
18
+ export { default as RepeaterFilterControl } from './features/repeaters/components/RepeaterFilterControl.svelte';
19
+ export { default as RepeaterSettingsPanel } from './features/repeaters/components/RepeaterSettingsPanel.svelte';
20
+ export * from './features/repeaters/stores/repeater.data.svelte';
21
+ export * from './features/repeaters/stores/repeater.display.svelte';
22
+ export * from './features/repeaters/stores/repeater.registry.svelte';
23
+ // Features - Sites
24
+ export * from './features/sites/types';
25
+ export { default as SitesLayer } from './features/sites/layers/SitesLayer.svelte';
26
+ export { default as SiteLabelsLayer } from './features/sites/layers/SiteLabelsLayer.svelte';
27
+ export { default as SiteFilterControl } from './features/sites/components/SiteFilterControl.svelte';
28
+ export { default as SiteSettingsPanel } from './features/sites/components/SiteSettingsPanel.svelte';
29
+ export * from './features/sites/stores/site.data.svelte';
30
+ export * from './features/sites/stores/site.display.svelte';
31
+ export * from './features/sites/stores/site.registry.svelte';
32
+ // Demo
33
+ export { default as DemoMap } from './demo/DemoMap.svelte';
34
+ export { demoCells } from './demo/demo-cells';
35
+ export { demoRepeaters } from './demo/demo-repeaters';
36
+ export { demoSites } from './demo/demo-data';
@@ -0,0 +1,242 @@
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
+ import { onDestroy, getContext } from 'svelte';
9
+ import type { IControl, Map as MapboxMap } from 'mapbox-gl';
10
+ import type { MapStore } from '../../core/stores/map.store.svelte';
11
+
12
+ interface Props {
13
+ /** Position on the map */
14
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
15
+ /** Control title (shown in header) */
16
+ title?: string;
17
+ /** Optional Bootstrap icon name to render when collapsed */
18
+ icon?: string;
19
+ /** When collapsed, should we show only the icon (if provided)? */
20
+ iconOnlyWhenCollapsed?: boolean;
21
+ /** Is the control collapsible? */
22
+ collapsible?: boolean;
23
+ /** Initial collapsed state */
24
+ initiallyCollapsed?: boolean;
25
+ /** Optional callback when collapse state changes */
26
+ onCollapseToggle?: (collapsed: boolean) => void;
27
+ /** Custom CSS class for the container */
28
+ class?: string;
29
+ /** Child content */
30
+ children?: import('svelte').Snippet;
31
+ /** Optional offset from map edge (e.g., '12px') */
32
+ edgeOffset?: string;
33
+ /** Width of the map control (e.g., '360px') */
34
+ controlWidth?: string;
35
+ }
36
+
37
+ let {
38
+ position = 'top-left',
39
+ title,
40
+ icon,
41
+ iconOnlyWhenCollapsed = true,
42
+ collapsible = true,
43
+ initiallyCollapsed = true,
44
+ onCollapseToggle,
45
+ class: className = '',
46
+ children,
47
+ edgeOffset = '12px',
48
+ controlWidth = '420px'
49
+ }: Props = $props();
50
+
51
+ const mapStore = getContext<MapStore>('MAP_CONTEXT');
52
+
53
+ if (!mapStore) {
54
+ console.error('MapControl: No map context available. Make sure MapControl is used inside Map.');
55
+ }
56
+
57
+ let controlElement: HTMLDivElement;
58
+ let collapsed = $state(initiallyCollapsed);
59
+ let control: IControl | null = null;
60
+
61
+ // Reactively add/remove control when map becomes available/unavailable
62
+ $effect(() => {
63
+ const map = mapStore.map;
64
+ if (map && controlElement) {
65
+ addControl(map);
66
+ return () => {
67
+ removeControl(map);
68
+ };
69
+ }
70
+ });
71
+
72
+ function addControl(map: MapboxMap) {
73
+ if (control) return;
74
+
75
+ // Create a custom Mapbox control
76
+ control = {
77
+ onAdd: () => {
78
+ return controlElement;
79
+ },
80
+ onRemove: () => {
81
+ // Cleanup handled in onDestroy/effect cleanup
82
+ }
83
+ };
84
+
85
+ map.addControl(control, position);
86
+ }
87
+
88
+ function removeControl(map: MapboxMap) {
89
+ if (control) {
90
+ try {
91
+ map.removeControl(control);
92
+ } catch (e) {
93
+ // Control may already be removed or map destroyed
94
+ }
95
+ control = null;
96
+ }
97
+ }
98
+
99
+ function toggleCollapse() {
100
+ collapsed = !collapsed;
101
+ // Call the callback if provided
102
+ if (onCollapseToggle) {
103
+ onCollapseToggle(collapsed);
104
+ }
105
+ }
106
+ </script>
107
+
108
+ <div
109
+ bind:this={controlElement}
110
+ class="mapboxgl-ctrl mapboxgl-ctrl-group map-control-container {className}"
111
+ style="--edge-offset: {edgeOffset}; --map-control-width: {controlWidth};"
112
+ data-edge-offset={edgeOffset}
113
+ >
114
+ {#if title || icon}
115
+ <div class="map-control-header" title={title}>
116
+ <span class="map-control-title">
117
+ {#if collapsed && iconOnlyWhenCollapsed && icon}
118
+ <i class="bi bi-{icon}" aria-hidden="true"></i>
119
+ {#if title}
120
+ <span class="visually-hidden">{title}</span>
121
+ {/if}
122
+ {:else}
123
+ {#if title}
124
+ {title}
125
+ {:else if icon}
126
+ <i class="bi bi-{icon}" aria-hidden="true"></i>
127
+ {/if}
128
+ {/if}
129
+ </span>
130
+ {#if collapsible}
131
+ <button
132
+ class="map-control-toggle"
133
+ onclick={toggleCollapse}
134
+ aria-label={collapsed ? 'Expand' : 'Collapse'}
135
+ title={collapsed ? 'Expand' : 'Collapse'}
136
+ >
137
+ <i class="bi bi-chevron-{collapsed ? 'down' : 'up'}"></i>
138
+ </button>
139
+ {/if}
140
+ </div>
141
+ {/if}
142
+
143
+ {#if !collapsed}
144
+ <div class="map-control-content">
145
+ {#if children}
146
+ {@render children()}
147
+ {/if}
148
+ </div>
149
+ {/if}
150
+ </div>
151
+
152
+ <style>
153
+ .map-control-container {
154
+ background: white;
155
+ border-radius: 0.6rem; /* modern, slightly larger radius */
156
+ box-shadow: 0 6px 18px rgba(14, 30, 37, 0.08);
157
+ overflow: hidden;
158
+ max-width: var(--map-control-width, 420px);
159
+ margin: var(--edge-offset, 12px); /* this ensures a default offset from the map edge */
160
+ font-family: system-ui, -apple-system, sans-serif;
161
+ /* Ensure it sits above map */
162
+ pointer-events: auto;
163
+ }
164
+
165
+ .map-control-header {
166
+ display: flex;
167
+ align-items: center;
168
+ justify-content: space-between;
169
+ padding: 10px 14px;
170
+ background: #f8f9fa;
171
+ border-bottom: 1px solid #dee2e6;
172
+ font-weight: 600;
173
+ font-size: 13px;
174
+ color: #212529;
175
+ }
176
+
177
+ .map-control-title {
178
+ flex: 1;
179
+ user-select: none;
180
+ }
181
+
182
+ .map-control-title i {
183
+ display: inline-flex;
184
+ align-items: center;
185
+ justify-content: center;
186
+ font-size: 1.1rem;
187
+ }
188
+
189
+ .map-control-toggle {
190
+ background: none;
191
+ border: none;
192
+ padding: 4px;
193
+ cursor: pointer;
194
+ color: #6c757d;
195
+ display: flex;
196
+ align-items: center;
197
+ justify-content: center;
198
+ border-radius: 3px;
199
+ transition: all 0.2s;
200
+ }
201
+
202
+ .map-control-toggle:hover {
203
+ background: rgba(0, 0, 0, 0.05);
204
+ color: #212529;
205
+ }
206
+
207
+ .map-control-content {
208
+ padding: 12px;
209
+ max-height: var(--map-control-max-height, 420px);
210
+ overflow-y: auto;
211
+ font-size: 13px;
212
+ }
213
+
214
+ /* Custom scrollbar */
215
+ .map-control-content::-webkit-scrollbar {
216
+ width: 8px;
217
+ }
218
+
219
+ .map-control-content::-webkit-scrollbar-track {
220
+ background: #f1f1f1;
221
+ }
222
+
223
+ .map-control-content::-webkit-scrollbar-thumb {
224
+ background: #888;
225
+ border-radius: 4px;
226
+ }
227
+
228
+ .map-control-content::-webkit-scrollbar-thumb:hover {
229
+ background: #555;
230
+ }
231
+
232
+ .visually-hidden {
233
+ position: absolute;
234
+ width: 1px;
235
+ height: 1px;
236
+ padding: 0;
237
+ overflow: hidden;
238
+ clip: rect(0, 0, 0, 0);
239
+ white-space: nowrap;
240
+ border: 0;
241
+ }
242
+ </style>
@@ -0,0 +1,27 @@
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
+ /** Optional Bootstrap icon name to render when collapsed */
7
+ icon?: string;
8
+ /** When collapsed, should we show only the icon (if provided)? */
9
+ iconOnlyWhenCollapsed?: boolean;
10
+ /** Is the control collapsible? */
11
+ collapsible?: boolean;
12
+ /** Initial collapsed state */
13
+ initiallyCollapsed?: boolean;
14
+ /** Optional callback when collapse state changes */
15
+ onCollapseToggle?: (collapsed: boolean) => void;
16
+ /** Custom CSS class for the container */
17
+ class?: string;
18
+ /** Child content */
19
+ children?: import('svelte').Snippet;
20
+ /** Optional offset from map edge (e.g., '12px') */
21
+ edgeOffset?: string;
22
+ /** Width of the map control (e.g., '360px') */
23
+ controlWidth?: string;
24
+ }
25
+ declare const MapControl: import("svelte").Component<Props, {}, "">;
26
+ type MapControl = ReturnType<typeof MapControl>;
27
+ export default MapControl;
@@ -0,0 +1 @@
1
+ export { default as MapControl } from './controls/MapControl.svelte';
@@ -0,0 +1 @@
1
+ export { default as MapControl } from './controls/MapControl.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.85",
3
+ "version": "0.0.87",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",