@smartnet360/svelte-components 0.0.97 → 0.0.98

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.
@@ -17,18 +17,19 @@
17
17
  baseMetrics: string[];
18
18
  mode: Mode;
19
19
  markers?: ChartMarker[];
20
- cellStyling?: CellStylingConfig; // Optional cell styling config (defaults to defaultCellStyling)
21
- initialGrouping?: TreeGroupingConfig; // Optional initial tree grouping (defaults to Site → Azimuth → Cell)
22
- showGroupingSelector?: boolean; // Show/hide the grouping dropdown (default: true)
23
- onSearch?: (searchTerm: string) => void; // Optional: Search callback (if provided, shows search box)
24
- searchPlaceholder?: string; // Optional: Search box placeholder text (default: "Search...")
25
- plotlyLayout?: Record<string, any>; // Optional Plotly layout configuration
20
+ cellStyling?: CellStylingConfig; // Optional cell styling config (defaults to defaultCellStyling)
21
+ initialGrouping?: TreeGroupingConfig; // Optional initial tree grouping (defaults to Site → Azimuth → Cell)
22
+ showGroupingSelector?: boolean; // Show/hide the grouping dropdown (default: true)
23
+ useSectorLineStyles?: boolean; // Enable sector-based line style differentiation (default: false)
24
+ onSearch?: (searchTerm: string) => void; // Optional: Search callback (if provided, shows search box)
25
+ searchPlaceholder?: string; // Optional: Search box placeholder text (default: "Search...")
26
+ plotlyLayout?: Record<string, any>; // Optional Plotly layout configuration
26
27
  }
27
28
 
28
29
  let { rawData, multiCellLayout, singleLteLayout,
29
30
  singleNrLayout, baseMetrics, mode = "scrollspy", markers = [],
30
31
  cellStyling = defaultCellStyling, initialGrouping = defaultTreeGrouping,
31
- showGroupingSelector = true, onSearch, searchPlaceholder = "Search...", plotlyLayout }: Props = $props();
32
+ showGroupingSelector = true, useSectorLineStyles = false, onSearch, searchPlaceholder = "Search...", plotlyLayout }: Props = $props();
32
33
 
33
34
  // Search state
34
35
  let searchTerm = $state('');
@@ -61,6 +62,9 @@
61
62
  // Color dimension state (defaults to 'band' for semantic RF characteristics)
62
63
  let colorDimension = $state<ColorDimension>('band');
63
64
 
65
+ // Single root select mode - only one Level 0 node at a time (radio behavior)
66
+ let singleRootSelect = $state(false);
67
+
64
68
  // Available field options for grouping levels
65
69
  const fieldOptions: { value: TreeGroupField; label: string }[] = [
66
70
  { value: 'site', label: 'Site' },
@@ -92,10 +96,10 @@
92
96
  return fieldOptions.filter(opt => opt.value !== treeGrouping.level0);
93
97
  }); let treeStore = $state<ReturnType<typeof createTreeStore> | null>(null);
94
98
 
95
- // Rebuild tree whenever treeGrouping changes
99
+ // Rebuild tree whenever treeGrouping or singleRootSelect changes
96
100
  $effect(() => {
97
101
 
98
- log(' Rebuilding tree with grouping', { treeGrouping });
102
+ log('🔄 Rebuilding tree with grouping', { treeGrouping, singleRootSelect });
99
103
 
100
104
  // Clear any existing localStorage data to prevent stale state
101
105
  const storageKey = 'site-check:treeState';
@@ -119,11 +123,13 @@
119
123
  nodes: treeNodes,
120
124
  namespace: 'site-check',
121
125
  persistState: false, // Don't persist when grouping changes dynamically
122
- defaultExpandAll: false
126
+ defaultExpandAll: false,
127
+ singleRootSelect // Pass single root select mode
123
128
  });
124
129
  log('✅ Tree Store Created', {
125
130
  namespace: 'site-check',
126
- grouping: treeGrouping
131
+ grouping: treeGrouping,
132
+ singleRootSelect
127
133
  });
128
134
  });
129
135
 
@@ -157,9 +163,9 @@
157
163
 
158
164
  // Expand layout based on selected cells and chosen base layout
159
165
  let chartLayout = $derived.by(() => {
160
- // Pass cellStyling, treeGrouping, and colorDimension - helper will decide per-section whether to use styling,
161
- // and generate appropriate labels based on grouping and colors based on colorDimension
162
- const expanded = expandLayoutForCells(selectedBaseLayout, filteredData, treeGrouping, colorDimension, cellStyling);
166
+ // Pass cellStyling, treeGrouping, colorDimension, and useSectorLineStyles - helper will decide per-section whether to use styling,
167
+ // and generate appropriate labels based on grouping, colors based on colorDimension, and line styles based on useSectorLineStyles
168
+ const expanded = expandLayoutForCells(selectedBaseLayout, filteredData, treeGrouping, colorDimension, useSectorLineStyles, cellStyling);
163
169
  log('📐 Chart Layout:', {
164
170
  layoutName: selectedBaseLayout.layoutName,
165
171
  layoutDefaultColors: selectedBaseLayout.useDefaultChartColors ?? false,
@@ -359,6 +365,39 @@
359
365
  </div>
360
366
  </div>
361
367
 
368
+ <!-- Single Root Select Toggle -->
369
+ <div class="form-check mt-2">
370
+ <input
371
+ class="form-check-input"
372
+ type="checkbox"
373
+ id="singleRootSelectCheck"
374
+ checked={singleRootSelect}
375
+ onchange={(e) => {
376
+ singleRootSelect = e.currentTarget.checked;
377
+ log('🔘 Single root select mode:', singleRootSelect);
378
+
379
+ // When enabling single root mode, uncheck all roots except the first one
380
+ if (singleRootSelect && treeStore) {
381
+ const store = $treeStore;
382
+ if (store) {
383
+ const checkedRoots = store.state.rootPaths.filter(path =>
384
+ store.state.checkedPaths.has(path)
385
+ );
386
+ if (checkedRoots.length > 1) {
387
+ log('🔘 Multiple roots selected, keeping only first one:', checkedRoots[0]);
388
+ // Uncheck all except the first
389
+ for (let i = 1; i < checkedRoots.length; i++) {
390
+ store.toggle(checkedRoots[i]);
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }}
396
+ />
397
+ <label class="form-check-label small" for="singleRootSelectCheck">
398
+ Single selection on level 0
399
+ </label>
400
+ </div>
362
401
  </div>
363
402
  {/if}
364
403
  {/if} <!-- Tree View -->
@@ -12,6 +12,7 @@ interface Props {
12
12
  cellStyling?: CellStylingConfig;
13
13
  initialGrouping?: TreeGroupingConfig;
14
14
  showGroupingSelector?: boolean;
15
+ useSectorLineStyles?: boolean;
15
16
  onSearch?: (searchTerm: string) => void;
16
17
  searchPlaceholder?: string;
17
18
  plotlyLayout?: Record<string, any>;
@@ -13,7 +13,7 @@ import { type StackGroupMode } from './transforms.js';
13
13
  * @param stackGroupMode - Optional stackgroup strategy for stacked charts (default: 'none' = single stack)
14
14
  * @returns Expanded layout with cell-specific KPIs
15
15
  */
16
- export declare function expandLayoutForCells(baseLayout: Layout, data: CellTrafficRecord[], grouping: TreeGroupingConfig, colorDimension: ColorDimension, stylingConfig?: CellStylingConfig, stackGroupMode?: StackGroupMode): Layout;
16
+ export declare function expandLayoutForCells(baseLayout: Layout, data: CellTrafficRecord[], grouping: TreeGroupingConfig, colorDimension: ColorDimension, useSectorLineStyles: boolean, stylingConfig?: CellStylingConfig, stackGroupMode?: StackGroupMode): Layout;
17
17
  /**
18
18
  * Extract base metric names from a layout configuration
19
19
  * Returns unique metric rawNames that need to be pivoted
@@ -11,7 +11,7 @@ import { createStyledKPI, sortCellsByBandFrequency, assignStackGroups } from './
11
11
  * @param stackGroupMode - Optional stackgroup strategy for stacked charts (default: 'none' = single stack)
12
12
  * @returns Expanded layout with cell-specific KPIs
13
13
  */
14
- export function expandLayoutForCells(baseLayout, data, grouping, colorDimension, stylingConfig, stackGroupMode = 'none') {
14
+ export function expandLayoutForCells(baseLayout, data, grouping, colorDimension, useSectorLineStyles, stylingConfig, stackGroupMode = 'none') {
15
15
  // Get unique cells and their metadata, sorted by band frequency
16
16
  const cellMap = new Map();
17
17
  data.forEach((record) => {
@@ -39,8 +39,8 @@ export function expandLayoutForCells(baseLayout, data, grouping, colorDimension,
39
39
  ...section,
40
40
  charts: section.charts.map((chart) => ({
41
41
  ...chart,
42
- yLeft: expandKPIs(chart.yLeft, cells, grouping, colorDimension, effectiveStyling, stackGroupMode),
43
- yRight: expandKPIs(chart.yRight, cells, grouping, colorDimension, effectiveStyling, stackGroupMode)
42
+ yLeft: expandKPIs(chart.yLeft, cells, grouping, colorDimension, useSectorLineStyles, effectiveStyling, stackGroupMode),
43
+ yRight: expandKPIs(chart.yRight, cells, grouping, colorDimension, useSectorLineStyles, effectiveStyling, stackGroupMode)
44
44
  }))
45
45
  };
46
46
  })
@@ -60,13 +60,13 @@ export function expandLayoutForCells(baseLayout, data, grouping, colorDimension,
60
60
  * @param stackGroupMode - Stackgroup strategy for this set of KPIs
61
61
  * @returns Expanded array of KPIs (styled or default, with stackgroups assigned)
62
62
  */
63
- function expandKPIs(baseKPIs, cells, grouping, colorDimension, stylingConfig, stackGroupMode = 'none') {
63
+ function expandKPIs(baseKPIs, cells, grouping, colorDimension, useSectorLineStyles, stylingConfig, stackGroupMode = 'none') {
64
64
  let expandedKPIs = [];
65
65
  baseKPIs.forEach((baseKPI) => {
66
66
  cells.forEach(([cellName, record]) => {
67
67
  if (stylingConfig) {
68
68
  // Apply custom styling (band colors, sector line styles)
69
- const styledKPI = createStyledKPI(baseKPI.rawName, record, baseKPI.unit, grouping, colorDimension, stylingConfig);
69
+ const styledKPI = createStyledKPI(baseKPI.rawName, record, baseKPI.unit, grouping, colorDimension, useSectorLineStyles, stylingConfig);
70
70
  expandedKPIs.push({
71
71
  ...styledKPI,
72
72
  stackGroup: undefined // Initialize for treeshake-safe property assignment
@@ -84,4 +84,4 @@ export declare function transformChartData(data: CellTrafficRecord[], baseMetric
84
84
  * @param stylingConfig - Optional styling configuration
85
85
  * @returns KPI with cell-specific styling applied
86
86
  */
87
- export declare function createStyledKPI(metricName: string, cellRecord: CellTrafficRecord, unit: string, grouping: TreeGroupingConfig, colorDimension: ColorDimension, stylingConfig?: CellStylingConfig): KPI;
87
+ export declare function createStyledKPI(metricName: string, cellRecord: CellTrafficRecord, unit: string, grouping: TreeGroupingConfig, colorDimension: ColorDimension, useSectorLineStyles: boolean, stylingConfig?: CellStylingConfig): KPI;
@@ -483,7 +483,7 @@ function generateSiteColor(siteName) {
483
483
  * @param stylingConfig - Optional styling configuration
484
484
  * @returns KPI with cell-specific styling applied
485
485
  */
486
- export function createStyledKPI(metricName, cellRecord, unit, grouping, colorDimension, stylingConfig) {
486
+ export function createStyledKPI(metricName, cellRecord, unit, grouping, colorDimension, useSectorLineStyles, stylingConfig) {
487
487
  const { band, sector, azimuth, cellName, siteName } = cellRecord;
488
488
  // Determine color based on colorDimension
489
489
  let color;
@@ -495,8 +495,8 @@ export function createStyledKPI(metricName, cellRecord, unit, grouping, colorDim
495
495
  // Generate consistent color for site
496
496
  color = generateSiteColor(siteName);
497
497
  }
498
- // Get line style from sector (if config provided)
499
- const lineStyle = stylingConfig?.sectorLineStyles?.[sector.toString()];
498
+ // Get line style from sector only if explicitly enabled
499
+ const lineStyle = useSectorLineStyles ? stylingConfig?.sectorLineStyles?.[sector.toString()] : undefined;
500
500
  // Generate label based on tree grouping configuration
501
501
  const displayName = generateAdaptiveLabel(cellRecord, grouping);
502
502
  // Build KPI with cell-specific styling
@@ -74,6 +74,8 @@ export interface TreeConfig<T = any> {
74
74
  persistState?: boolean;
75
75
  /** Show indeterminate checkbox states */
76
76
  showIndeterminate?: boolean;
77
+ /** Single root selection mode - only one root node can be checked at a time (radio behavior) */
78
+ singleRootSelect?: boolean;
77
79
  }
78
80
  /**
79
81
  * Store value exposed to consumers
@@ -87,6 +87,21 @@ export function createTreeStore(config) {
87
87
  const newChecked = !state.checkedPaths.has(path);
88
88
  const newCheckedPaths = new Set(state.checkedPaths);
89
89
  log('📌 Toggle action', { path, newChecked });
90
+ // STEP 0: If singleRootSelect mode and this is a root node being checked, uncheck all other roots
91
+ if (config.singleRootSelect && newChecked && nodeState.level === 0) {
92
+ log('🔘 Single root select mode: unchecking other roots', { path });
93
+ // Uncheck all root nodes and their descendants
94
+ state.rootPaths.forEach(rootPath => {
95
+ if (rootPath !== path) {
96
+ newCheckedPaths.delete(rootPath);
97
+ // Also uncheck all descendants of this root
98
+ const rootDescendants = getDescendantPaths(rootPath, state.nodes, separator);
99
+ rootDescendants.forEach(descendantPath => {
100
+ newCheckedPaths.delete(descendantPath);
101
+ });
102
+ }
103
+ });
104
+ }
90
105
  // STEP 1: Update this node
91
106
  if (newChecked) {
92
107
  newCheckedPaths.add(path);
@@ -84,47 +84,63 @@
84
84
 
85
85
  <!-- Auto Size -->
86
86
  <div class="row align-items-center g-2 mb-3">
87
- <div class="col-4 text-secondary fw-semibold small text-uppercase">Auto Size</div>
88
- <div class="col-3"></div>
89
- <div class="col-5">
90
- <div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
91
- <input
92
- id="cell-autosize-toggle"
93
- type="checkbox"
94
- class="form-check-input"
95
- role="switch"
96
- bind:checked={displayStore.useAutoSize}
97
- />
87
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Density Caps</div>
88
+ <div class="col-8">
89
+ <div class="d-flex gap-3">
90
+ <div class="form-check form-switch m-0">
91
+ <input
92
+ id="cell-mincap-toggle"
93
+ type="checkbox"
94
+ class="form-check-input"
95
+ role="switch"
96
+ bind:checked={displayStore.useMinCap}
97
+ />
98
+ <label class="form-check-label small text-secondary" for="cell-mincap-toggle">
99
+ Min
100
+ </label>
101
+ </div>
102
+ <div class="form-check form-switch m-0">
103
+ <input
104
+ id="cell-maxcap-toggle"
105
+ type="checkbox"
106
+ class="form-check-input"
107
+ role="switch"
108
+ bind:checked={displayStore.useMaxCap}
109
+ />
110
+ <label class="form-check-label small text-secondary" for="cell-maxcap-toggle">
111
+ Max
112
+ </label>
113
+ </div>
98
114
  </div>
99
115
  </div>
100
116
  </div>
101
117
 
102
- {#if displayStore.useAutoSize}
103
- <!-- Auto Size Mode -->
118
+ {#if displayStore.useMinCap || displayStore.useMaxCap}
119
+ <!-- Cap Mode -->
104
120
  <div class="row align-items-center g-2 mb-3 ps-3">
105
- <div class="col-4 text-secondary small">Mode</div>
121
+ <div class="col-4 text-secondary small">Cap Mode</div>
106
122
  <div class="col-8">
107
123
  <select
108
124
  class="form-select form-select-sm"
109
125
  bind:value={displayStore.autoSizeMode}
110
126
  >
111
- <option value="logarithmic">Logarithmic (smooth)</option>
112
- <option value="percentage">Proportional (40%)</option>
113
- <option value="tiered">Tiered (4 levels)</option>
114
- <option value="hybrid">Hybrid (stepped proportional)</option>
127
+ <option value="logarithmic">Logarithmic</option>
128
+ <option value="percentage">Proportional</option>
129
+ <option value="tiered">Tiered</option>
130
+ <option value="hybrid">Hybrid</option>
115
131
  </select>
116
132
  </div>
117
133
  </div>
118
134
 
119
- <!-- Auto Size Base Multiplier -->
135
+ <!-- Cap Base -->
120
136
  <div class="row align-items-center g-2 mb-3 ps-3">
121
- <div class="col-4 text-secondary small">Base Size</div>
137
+ <div class="col-4 text-secondary small">Cap Base</div>
122
138
  <div class="col-3 text-end">
123
139
  <span class="badge bg-white text-muted border">{displayStore.autoSizeBase.toFixed(1)}x</span>
124
140
  </div>
125
141
  <div class="col-5">
126
142
  <input
127
- id="cell-autosize-base-slider"
143
+ id="cell-cap-base-slider"
128
144
  type="range"
129
145
  class="form-range w-100"
130
146
  min="0.5"
@@ -134,78 +150,6 @@
134
150
  />
135
151
  </div>
136
152
  </div>
137
- {:else}
138
- <!-- Density-Based Caps (Manual Mode Only) -->
139
- <div class="ps-3 border-start border-2 mb-3">
140
- <!-- Min Cap -->
141
- <div class="row align-items-center g-2 mb-2">
142
- <div class="col-7 text-secondary small">Min Size Cap (Density)</div>
143
- <div class="col-5">
144
- <div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
145
- <input
146
- id="cell-mincap-toggle"
147
- type="checkbox"
148
- class="form-check-input"
149
- role="switch"
150
- bind:checked={displayStore.useMinCap}
151
- />
152
- </div>
153
- </div>
154
- </div>
155
-
156
- <!-- Max Cap -->
157
- <div class="row align-items-center g-2 mb-2">
158
- <div class="col-7 text-secondary small">Max Size Cap (Density)</div>
159
- <div class="col-5">
160
- <div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
161
- <input
162
- id="cell-maxcap-toggle"
163
- type="checkbox"
164
- class="form-check-input"
165
- role="switch"
166
- bind:checked={displayStore.useMaxCap}
167
- />
168
- </div>
169
- </div>
170
- </div>
171
-
172
- {#if displayStore.useMinCap || displayStore.useMaxCap}
173
- <!-- Cap Mode (shares auto-size settings) -->
174
- <div class="row align-items-center g-2 mb-2 mt-2">
175
- <div class="col-4 text-secondary small">Cap Mode</div>
176
- <div class="col-8">
177
- <select
178
- class="form-select form-select-sm"
179
- bind:value={displayStore.autoSizeMode}
180
- >
181
- <option value="logarithmic">Logarithmic</option>
182
- <option value="percentage">Proportional</option>
183
- <option value="tiered">Tiered</option>
184
- <option value="hybrid">Hybrid</option>
185
- </select>
186
- </div>
187
- </div>
188
-
189
- <!-- Cap Base -->
190
- <div class="row align-items-center g-2 mb-2">
191
- <div class="col-4 text-secondary small">Cap Base</div>
192
- <div class="col-3 text-end">
193
- <span class="badge bg-white text-muted border">{displayStore.autoSizeBase.toFixed(1)}x</span>
194
- </div>
195
- <div class="col-5">
196
- <input
197
- id="cell-cap-base-slider"
198
- type="range"
199
- class="form-range w-100"
200
- min="0.5"
201
- max="3.0"
202
- step="0.1"
203
- bind:value={displayStore.autoSizeBase}
204
- />
205
- </div>
206
- </div>
207
- {/if}
208
- </div>
209
153
  {/if} <div class="border-top my-3"></div>
210
154
 
211
155
  <!-- Show Labels -->
@@ -100,14 +100,10 @@
100
100
  // Initial setup
101
101
  addLayers();
102
102
 
103
- // Events for updating - conditional based on auto-size
103
+ // Events for updating - always listen to zoom/move
104
104
  map.on('style.load', addLayers);
105
-
106
- // Only listen to zoom/move events if NOT using auto-size
107
- if (!displayStore.useAutoSize) {
108
- map.on('moveend', updateLayer);
109
- map.on('zoomend', updateLayer);
110
- }
105
+ map.on('moveend', updateLayer);
106
+ map.on('zoomend', updateLayer);
111
107
 
112
108
  // Cleanup
113
109
  return () => {
@@ -131,7 +127,6 @@
131
127
  const _l1 = displayStore.level1;
132
128
  const _l2 = displayStore.level2;
133
129
  const _layerGrouping = displayStore.layerGrouping;
134
- const _useAutoSize = displayStore.useAutoSize;
135
130
  const _autoSizeMode = displayStore.autoSizeMode;
136
131
  const _autoSizeBase = displayStore.autoSizeBase;
137
132
  const _useMinCap = displayStore.useMinCap;
@@ -151,101 +146,79 @@
151
146
  }
152
147
 
153
148
  function renderCells(map: mapboxgl.Map) {
149
+ const bounds = map.getBounds();
150
+ if (!bounds) return;
151
+
154
152
  const zoom = map.getZoom();
155
153
  const centerLat = map.getCenter().lat;
156
154
 
157
155
  console.log(`[CellsLayer] Rendering.. Zoom: ${zoom.toFixed(2)}, Cells: ${dataStore.filteredCells.length}`);
158
156
 
159
- // 1. Calculate base radius (only used in manual mode)
157
+ // Calculate base radius from pixel size
160
158
  const baseRadiusMeters = calculateRadiusInMeters(centerLat, zoom, displayStore.targetPixelSize);
161
-
162
- if (!displayStore.useAutoSize) {
163
- console.log(`[CellsLayer] Base radius: ${baseRadiusMeters.toFixed(2)}m for target ${displayStore.targetPixelSize}px`);
164
- }
159
+ console.log(`[CellsLayer] Base radius: ${baseRadiusMeters.toFixed(2)}m for target ${displayStore.targetPixelSize}px`);
165
160
 
166
- // 2. Group cells (Level 1=Tech, Level 2=Band for now hardcoded)
167
- // In real app, this comes from a store
161
+ // Group cells
168
162
  const groups = groupCells(dataStore.filteredCells, displayStore.level1, displayStore.level2);
169
163
  console.log(`[CellsLayer] Groups: ${groups.size}`);
170
164
 
171
165
  const features: GeoJSON.Feature[] = [];
172
166
  let groupIndex = 0;
173
167
 
174
- // 3. Iterate groups and generate features
168
+ // Iterate groups and generate features
175
169
  for (const [groupId, cells] of groups) {
176
- // Get style from registry
177
170
  const defaultColor = getColorForGroup(groupIndex++);
178
171
  const style = registry.getStyle(groupId, defaultColor);
179
172
 
180
173
  if (!style.visible) continue;
181
174
 
182
175
  for (const cell of cells) {
183
- // 4. BBox Filter - SKIP if auto-size is enabled
184
- if (!displayStore.useAutoSize) {
185
- const bounds = map.getBounds();
186
- if (!bounds || !bounds.contains([cell.longitude, cell.latitude])) {
187
- continue;
188
- }
176
+ // Viewport filter
177
+ if (!bounds.contains([cell.longitude, cell.latitude])) {
178
+ continue;
189
179
  }
190
180
 
191
- // 5. Z-Index Lookup
181
+ // Z-Index Lookup
192
182
  const zIndexKey = `${cell.tech}_${cell.frq}` as TechnologyBandKey;
193
183
  const zIndex = displayStore.currentZIndex[zIndexKey] ?? 10;
194
184
 
195
- // 6. Calculate radius with z-index scaling
185
+ // Calculate radius with z-index scaling
196
186
  const MAX_Z = 35;
197
- let radiusMeters: number;
187
+ const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08;
188
+ let radiusMeters = baseRadiusMeters * scaleFactor;
198
189
 
199
- if (displayStore.useAutoSize) {
200
- // Auto-size mode: get target radius for this site
190
+ // Apply density-based caps if enabled
191
+ if (displayStore.useMinCap || displayStore.useMaxCap) {
201
192
  const siteDistance = dataStore.siteDistanceStore.getDistance(cell.siteId, 500);
202
193
  const autoRadius = calculateAutoRadius(siteDistance, displayStore.autoSizeMode);
203
-
204
- // Apply base size multiplier
205
194
  const baseAdjusted = autoRadius * displayStore.autoSizeBase;
195
+ const scaledAuto = baseAdjusted * scaleFactor;
206
196
 
207
- // Scale based on z-index for stacking visibility
208
- // Lower z-index (background) = larger, higher z-index (foreground) = smaller
209
- const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08; // 8% per layer
210
- radiusMeters = baseAdjusted * scaleFactor;
211
- } else {
212
- // Manual mode: base from pixel size, then scale by z-index
213
- const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08;
214
- radiusMeters = baseRadiusMeters * scaleFactor;
197
+ // Apply caps: min = 60% of auto-size, max = 140% of auto-size
198
+ const minCap = scaledAuto * 0.6;
199
+ const maxCap = scaledAuto * 1.4;
215
200
 
216
- // Apply density-based caps if enabled
217
- if (displayStore.useMinCap || displayStore.useMaxCap) {
218
- const siteDistance = dataStore.siteDistanceStore.getDistance(cell.siteId, 500);
219
- const autoRadius = calculateAutoRadius(siteDistance, displayStore.autoSizeMode);
220
- const baseAdjusted = autoRadius * displayStore.autoSizeBase;
221
- const scaledAuto = baseAdjusted * scaleFactor;
222
-
223
- // Apply caps: min = 60% of auto-size, max = 140% of auto-size
224
- const minCap = scaledAuto * 0.6;
225
- const maxCap = scaledAuto * 1.4;
226
-
227
- if (displayStore.useMinCap && radiusMeters < minCap) {
228
- radiusMeters = minCap;
229
- }
230
- if (displayStore.useMaxCap && radiusMeters > maxCap) {
231
- radiusMeters = maxCap;
232
- }
201
+ if (displayStore.useMinCap && radiusMeters < minCap) {
202
+ radiusMeters = minCap;
203
+ }
204
+ if (displayStore.useMaxCap && radiusMeters > maxCap) {
205
+ radiusMeters = maxCap;
233
206
  }
234
207
  }
235
208
 
236
- // 7. Apply beamwidth boost from displayStore preset
209
+ // Apply beamwidth boost
237
210
  const beamwidthBoost = displayStore.currentBeamwidthBoost[zIndexKey] || 0;
238
211
  const adjustedBeamwidth = cell.beamwidth + beamwidthBoost;
239
212
 
240
- // 8. Generate Arc
213
+ // Generate Arc
241
214
  const feature = generateCellArc(cell, radiusMeters, zIndex, style.color, adjustedBeamwidth);
242
215
  features.push(feature);
243
216
  }
244
217
  }
245
218
 
246
- console.log(`[CellsLayer] Generated ${features.length} features ${displayStore.useAutoSize ? '(all cells)' : 'in view'}`);
219
+ console.log(`[CellsLayer] Generated ${features.length} features in view`);
247
220
 
248
- // 8. Update Source
221
+ // Update Source
249
222
  const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
250
223
  if (source) {
251
224
  source.setData({
@@ -7,7 +7,6 @@ export declare class CellDisplayStore {
7
7
  lineWidth: number;
8
8
  showLabels: boolean;
9
9
  layerGrouping: LayerGroupingPreset;
10
- useAutoSize: boolean;
11
10
  autoSizeMode: AutoSizeMode;
12
11
  autoSizeBase: number;
13
12
  useMinCap: boolean;
@@ -8,11 +8,10 @@ export class CellDisplayStore {
8
8
  lineWidth = $state(1);
9
9
  showLabels = $state(false);
10
10
  layerGrouping = $state('frequency');
11
- // Auto-size settings
12
- useAutoSize = $state(false);
11
+ // Auto-size settings (used by density caps)
13
12
  autoSizeMode = $state('logarithmic');
14
13
  autoSizeBase = $state(1.0);
15
- // Density-based caps (for manual mode)
14
+ // Density-based caps
16
15
  useMinCap = $state(false);
17
16
  useMaxCap = $state(false);
18
17
  // Grouping
@@ -42,7 +41,6 @@ export class CellDisplayStore {
42
41
  this.lineWidth = parsed.lineWidth ?? 1;
43
42
  this.showLabels = parsed.showLabels ?? false;
44
43
  this.layerGrouping = parsed.layerGrouping ?? 'frequency';
45
- this.useAutoSize = parsed.useAutoSize ?? false;
46
44
  this.autoSizeMode = parsed.autoSizeMode ?? 'logarithmic';
47
45
  this.autoSizeBase = parsed.autoSizeBase ?? 1.0;
48
46
  this.useMinCap = parsed.useMinCap ?? false;
@@ -69,7 +67,6 @@ export class CellDisplayStore {
69
67
  lineWidth: this.lineWidth,
70
68
  showLabels: this.showLabels,
71
69
  layerGrouping: this.layerGrouping,
72
- useAutoSize: this.useAutoSize,
73
70
  autoSizeMode: this.autoSizeMode,
74
71
  autoSizeBase: this.autoSizeBase,
75
72
  useMinCap: this.useMinCap,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.97",
3
+ "version": "0.0.98",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",