@smartnet360/svelte-components 0.0.96 → 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,59 +84,73 @@
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
- min="0.3"
131
- max="2.0"
146
+ min="0.5"
147
+ max="3.0"
132
148
  step="0.1"
133
149
  bind:value={displayStore.autoSizeBase}
134
150
  />
135
151
  </div>
136
152
  </div>
137
- {/if}
138
-
139
- <div class="border-top my-3"></div>
153
+ {/if} <div class="border-top my-3"></div>
140
154
 
141
155
  <!-- Show Labels -->
142
156
  <div class="row align-items-center g-2 mb-3">
@@ -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,9 +127,10 @@
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;
132
+ const _useMinCap = displayStore.useMinCap;
133
+ const _useMaxCap = displayStore.useMaxCap;
137
134
 
138
135
  updateLayer();
139
136
  });
@@ -149,82 +146,79 @@
149
146
  }
150
147
 
151
148
  function renderCells(map: mapboxgl.Map) {
149
+ const bounds = map.getBounds();
150
+ if (!bounds) return;
151
+
152
152
  const zoom = map.getZoom();
153
153
  const centerLat = map.getCenter().lat;
154
154
 
155
155
  console.log(`[CellsLayer] Rendering.. Zoom: ${zoom.toFixed(2)}, Cells: ${dataStore.filteredCells.length}`);
156
156
 
157
- // 1. Calculate base radius (only used in manual mode)
157
+ // Calculate base radius from pixel size
158
158
  const baseRadiusMeters = calculateRadiusInMeters(centerLat, zoom, displayStore.targetPixelSize);
159
-
160
- if (!displayStore.useAutoSize) {
161
- console.log(`[CellsLayer] Base radius: ${baseRadiusMeters.toFixed(2)}m for target ${displayStore.targetPixelSize}px`);
162
- }
159
+ console.log(`[CellsLayer] Base radius: ${baseRadiusMeters.toFixed(2)}m for target ${displayStore.targetPixelSize}px`);
163
160
 
164
- // 2. Group cells (Level 1=Tech, Level 2=Band for now hardcoded)
165
- // In real app, this comes from a store
161
+ // Group cells
166
162
  const groups = groupCells(dataStore.filteredCells, displayStore.level1, displayStore.level2);
167
163
  console.log(`[CellsLayer] Groups: ${groups.size}`);
168
164
 
169
165
  const features: GeoJSON.Feature[] = [];
170
166
  let groupIndex = 0;
171
167
 
172
- // 3. Iterate groups and generate features
168
+ // Iterate groups and generate features
173
169
  for (const [groupId, cells] of groups) {
174
- // Get style from registry
175
170
  const defaultColor = getColorForGroup(groupIndex++);
176
171
  const style = registry.getStyle(groupId, defaultColor);
177
172
 
178
173
  if (!style.visible) continue;
179
174
 
180
175
  for (const cell of cells) {
181
- // 4. BBox Filter - SKIP if auto-size is enabled
182
- if (!displayStore.useAutoSize) {
183
- const bounds = map.getBounds();
184
- if (!bounds || !bounds.contains([cell.longitude, cell.latitude])) {
185
- continue;
186
- }
176
+ // Viewport filter
177
+ if (!bounds.contains([cell.longitude, cell.latitude])) {
178
+ continue;
187
179
  }
188
180
 
189
- // 5. Z-Index Lookup
181
+ // Z-Index Lookup
190
182
  const zIndexKey = `${cell.tech}_${cell.frq}` as TechnologyBandKey;
191
183
  const zIndex = displayStore.currentZIndex[zIndexKey] ?? 10;
192
184
 
193
- // 6. Calculate radius with z-index scaling
185
+ // Calculate radius with z-index scaling
194
186
  const MAX_Z = 35;
195
- let radiusMeters: number;
187
+ const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08;
188
+ let radiusMeters = baseRadiusMeters * scaleFactor;
196
189
 
197
- if (displayStore.useAutoSize) {
198
- // Auto-size mode: get target radius for this site
190
+ // Apply density-based caps if enabled
191
+ if (displayStore.useMinCap || displayStore.useMaxCap) {
199
192
  const siteDistance = dataStore.siteDistanceStore.getDistance(cell.siteId, 500);
200
193
  const autoRadius = calculateAutoRadius(siteDistance, displayStore.autoSizeMode);
201
-
202
- // Apply base size multiplier
203
194
  const baseAdjusted = autoRadius * displayStore.autoSizeBase;
195
+ const scaledAuto = baseAdjusted * scaleFactor;
196
+
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;
204
200
 
205
- // Scale based on z-index for stacking visibility
206
- // Lower z-index (background) = larger, higher z-index (foreground) = smaller
207
- const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08; // 8% per layer
208
- radiusMeters = baseAdjusted * scaleFactor;
209
- } else {
210
- // Manual mode: base from pixel size, then scale by z-index
211
- const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08;
212
- radiusMeters = baseRadiusMeters * scaleFactor;
201
+ if (displayStore.useMinCap && radiusMeters < minCap) {
202
+ radiusMeters = minCap;
203
+ }
204
+ if (displayStore.useMaxCap && radiusMeters > maxCap) {
205
+ radiusMeters = maxCap;
206
+ }
213
207
  }
214
208
 
215
- // 7. Apply beamwidth boost from displayStore preset
209
+ // Apply beamwidth boost
216
210
  const beamwidthBoost = displayStore.currentBeamwidthBoost[zIndexKey] || 0;
217
211
  const adjustedBeamwidth = cell.beamwidth + beamwidthBoost;
218
212
 
219
- // 8. Generate Arc
213
+ // Generate Arc
220
214
  const feature = generateCellArc(cell, radiusMeters, zIndex, style.color, adjustedBeamwidth);
221
215
  features.push(feature);
222
216
  }
223
217
  }
224
218
 
225
- console.log(`[CellsLayer] Generated ${features.length} features ${displayStore.useAutoSize ? '(all cells)' : 'in view'}`);
219
+ console.log(`[CellsLayer] Generated ${features.length} features in view`);
226
220
 
227
- // 8. Update Source
221
+ // Update Source
228
222
  const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
229
223
  if (source) {
230
224
  source.setData({
@@ -7,9 +7,10 @@ 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;
12
+ useMinCap: boolean;
13
+ useMaxCap: boolean;
13
14
  level1: CellGroupingField;
14
15
  level2: CellGroupingField;
15
16
  currentZIndex: Record<string, number>;
@@ -8,10 +8,12 @@ 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);
14
+ // Density-based caps
15
+ useMinCap = $state(false);
16
+ useMaxCap = $state(false);
15
17
  // Grouping
16
18
  level1 = $state('tech');
17
19
  level2 = $state('fband');
@@ -39,9 +41,10 @@ export class CellDisplayStore {
39
41
  this.lineWidth = parsed.lineWidth ?? 1;
40
42
  this.showLabels = parsed.showLabels ?? false;
41
43
  this.layerGrouping = parsed.layerGrouping ?? 'frequency';
42
- this.useAutoSize = parsed.useAutoSize ?? false;
43
44
  this.autoSizeMode = parsed.autoSizeMode ?? 'logarithmic';
44
45
  this.autoSizeBase = parsed.autoSizeBase ?? 1.0;
46
+ this.useMinCap = parsed.useMinCap ?? false;
47
+ this.useMaxCap = parsed.useMaxCap ?? false;
45
48
  this.level1 = parsed.level1 ?? 'tech';
46
49
  this.level2 = parsed.level2 ?? 'fband';
47
50
  this.labelPixelDistance = parsed.labelPixelDistance ?? 60;
@@ -64,9 +67,10 @@ export class CellDisplayStore {
64
67
  lineWidth: this.lineWidth,
65
68
  showLabels: this.showLabels,
66
69
  layerGrouping: this.layerGrouping,
67
- useAutoSize: this.useAutoSize,
68
70
  autoSizeMode: this.autoSizeMode,
69
71
  autoSizeBase: this.autoSizeBase,
72
+ useMinCap: this.useMinCap,
73
+ useMaxCap: this.useMaxCap,
70
74
  level1: this.level1,
71
75
  level2: this.level2,
72
76
  labelPixelDistance: this.labelPixelDistance,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.96",
3
+ "version": "0.0.98",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",