@smartnet360/svelte-components 0.0.51 → 0.0.54

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 (73) 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/TreeView/TreeNode.svelte +40 -45
  9. package/dist/core/TreeView/TreeNode.svelte.d.ts +10 -0
  10. package/dist/core/TreeView/TreeView.svelte +14 -2
  11. package/dist/core/TreeView/TreeView.svelte.d.ts +10 -0
  12. package/dist/core/TreeView/tree-utils.d.ts +3 -0
  13. package/dist/core/TreeView/tree-utils.js +33 -9
  14. package/dist/core/TreeView/tree.store.js +49 -24
  15. package/dist/core/index.d.ts +0 -1
  16. package/dist/core/index.js +2 -1
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +2 -0
  19. package/dist/map/controls/MapControl.svelte +204 -0
  20. package/dist/map/controls/MapControl.svelte.d.ts +17 -0
  21. package/dist/map/controls/SiteFilterControl.svelte +126 -0
  22. package/dist/map/controls/SiteFilterControl.svelte.d.ts +16 -0
  23. package/dist/map/demo/DemoMap.svelte +98 -0
  24. package/dist/map/demo/DemoMap.svelte.d.ts +12 -0
  25. package/dist/map/demo/demo-data.d.ts +12 -0
  26. package/dist/map/demo/demo-data.js +220 -0
  27. package/dist/map/hooks/useCellData.d.ts +14 -0
  28. package/dist/map/hooks/useCellData.js +29 -0
  29. package/dist/map/hooks/useMapbox.d.ts +14 -0
  30. package/dist/map/hooks/useMapbox.js +29 -0
  31. package/dist/map/index.d.ts +27 -0
  32. package/dist/map/index.js +47 -0
  33. package/dist/map/layers/CellsLayer.svelte +242 -0
  34. package/dist/map/layers/CellsLayer.svelte.d.ts +21 -0
  35. package/dist/map/layers/CoverageLayer.svelte +37 -0
  36. package/dist/map/layers/CoverageLayer.svelte.d.ts +9 -0
  37. package/dist/map/layers/LayerBase.d.ts +42 -0
  38. package/dist/map/layers/LayerBase.js +58 -0
  39. package/dist/map/layers/SitesLayer.svelte +282 -0
  40. package/dist/map/layers/SitesLayer.svelte.d.ts +19 -0
  41. package/dist/map/providers/CellDataProvider.svelte +43 -0
  42. package/dist/map/providers/CellDataProvider.svelte.d.ts +12 -0
  43. package/dist/map/providers/MapboxProvider.svelte +38 -0
  44. package/dist/map/providers/MapboxProvider.svelte.d.ts +9 -0
  45. package/dist/map/providers/providerHelpers.d.ts +17 -0
  46. package/dist/map/providers/providerHelpers.js +26 -0
  47. package/dist/map/stores/cellDataStore.d.ts +21 -0
  48. package/dist/map/stores/cellDataStore.js +53 -0
  49. package/dist/map/stores/interactions.d.ts +20 -0
  50. package/dist/map/stores/interactions.js +33 -0
  51. package/dist/map/stores/mapStore.d.ts +8 -0
  52. package/dist/map/stores/mapStore.js +10 -0
  53. package/dist/map/types.d.ts +115 -0
  54. package/dist/map/types.js +10 -0
  55. package/dist/map/utils/geojson.d.ts +20 -0
  56. package/dist/map/utils/geojson.js +78 -0
  57. package/dist/map/utils/mapboxHelpers.d.ts +51 -0
  58. package/dist/map/utils/mapboxHelpers.js +98 -0
  59. package/dist/map/utils/math.d.ts +40 -0
  60. package/dist/map/utils/math.js +95 -0
  61. package/dist/map/utils/siteTreeUtils.d.ts +27 -0
  62. package/dist/map/utils/siteTreeUtils.js +164 -0
  63. package/package.json +1 -1
  64. package/dist/core/Map/Map.svelte +0 -312
  65. package/dist/core/Map/Map.svelte.d.ts +0 -230
  66. package/dist/core/Map/index.d.ts +0 -9
  67. package/dist/core/Map/index.js +0 -9
  68. package/dist/core/Map/mapSettings.d.ts +0 -147
  69. package/dist/core/Map/mapSettings.js +0 -226
  70. package/dist/core/Map/mapStore.d.ts +0 -73
  71. package/dist/core/Map/mapStore.js +0 -136
  72. package/dist/core/Map/types.d.ts +0 -72
  73. 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')
@@ -1,14 +1,25 @@
1
1
  <script lang="ts">
2
2
  import type { NodeState, TreeStoreValue } from './tree.model';
3
+ import type { Snippet } from 'svelte';
3
4
  import TreeNode from './TreeNode.svelte';
4
5
 
6
+ interface NodeSlotProps {
7
+ node: NodeState['node'];
8
+ state: {
9
+ checked: boolean;
10
+ indeterminate: boolean;
11
+ expanded: boolean;
12
+ };
13
+ }
14
+
5
15
  interface Props {
6
16
  nodeState: NodeState;
7
17
  store: TreeStoreValue;
8
18
  showIndeterminate?: boolean;
19
+ children?: Snippet<[NodeSlotProps]>;
9
20
  }
10
21
 
11
- let { nodeState, store, showIndeterminate = true }: Props = $props();
22
+ let { nodeState, store, showIndeterminate = true, children }: Props = $props();
12
23
 
13
24
  // Computed states
14
25
  let isChecked = $derived(store.state.checkedPaths.has(nodeState.path));
@@ -25,8 +36,8 @@
25
36
  .filter((node): node is NodeState => node !== undefined)
26
37
  );
27
38
 
28
- // Indentation based on level
29
- let indentStyle = $derived(`padding-left: ${nodeState.level * 1.5}rem`);
39
+ // Indentation based on level (20px per level for consistent visual hierarchy)
40
+ let indentStyle = $derived(`margin-left: ${nodeState.level * 20}px`);
30
41
 
31
42
  function handleToggle() {
32
43
  store.toggle(nodeState.path);
@@ -40,50 +51,52 @@
40
51
  </script>
41
52
 
42
53
  <div class="tree-node" style={indentStyle}>
43
- <div class="tree-node-content">
54
+ <div class="d-flex align-items-center gap-1">
44
55
  <!-- Expand/Collapse Button -->
45
56
  {#if hasChildren}
46
57
  <button
47
58
  type="button"
48
- class="btn btn-sm btn-link expand-toggle p-0"
59
+ class="expand-toggle"
60
+ style="width: 20px; height: 20px; font-size: 10px;"
49
61
  onclick={handleExpandToggle}
50
62
  aria-label={isExpanded ? 'Collapse' : 'Expand'}
51
63
  >
52
- {#if isExpanded}
53
- <i class="bi bi-chevron-down"></i>
54
- {:else}
55
- <i class="bi bi-chevron-right"></i>
56
- {/if}
64
+ {isExpanded ? '▼' : '▶'}
57
65
  </button>
58
66
  {:else}
59
- <span class="expand-placeholder"></span>
67
+ <div class="expand-placeholder"></div>
60
68
  {/if}
61
69
 
62
70
  <!-- Checkbox -->
63
- <div class="form-check">
71
+ <div class="form-check d-flex align-items-center flex-fill">
64
72
  <input
65
73
  type="checkbox"
66
- class="form-check-input"
74
+ class="form-check-input me-2 my-0"
67
75
  class:indeterminate={isIndeterminate}
68
76
  checked={isChecked}
69
77
  indeterminate={isIndeterminate}
70
78
  onchange={handleToggle}
71
79
  id={`checkbox-${nodeState.path}`}
72
80
  />
73
- <label class="form-check-label" for={`checkbox-${nodeState.path}`}>
81
+ <label class="form-check-label user-select-none mb-0 lh-1" for={`checkbox-${nodeState.path}`}>
74
82
  {#if nodeState.node.icon}
75
83
  <span class="node-icon">{nodeState.node.icon}</span>
76
84
  {/if}
77
85
  <span class="node-label">{nodeState.node.label}</span>
78
86
  </label>
79
87
  </div>
88
+
89
+ <!-- Slot for additional controls -->
90
+ <div class="ms-auto">
91
+ {@render children?.({ node: nodeState.node, state: { checked: isChecked, indeterminate: isIndeterminate, expanded: isExpanded } })}
92
+ </div>
80
93
  </div>
81
94
 
82
95
  <!-- Recursive Children -->
83
96
  {#if hasChildren && isExpanded}
84
97
  <div class="tree-node-children">
85
98
  {#each childNodes as childNode (childNode.path)}
86
- <TreeNode nodeState={childNode} {store} {showIndeterminate} />
99
+ <TreeNode nodeState={childNode} {store} {showIndeterminate} {children} />
87
100
  {/each}
88
101
  </div>
89
102
  {/if}
@@ -91,33 +104,26 @@
91
104
 
92
105
  <style>
93
106
  .tree-node {
94
- position: relative;
95
- }
96
-
97
- .tree-node-content {
98
- display: flex;
99
- align-items: center;
100
- gap: 0.5rem;
101
- padding: 0.25rem 0;
102
- min-height: 2rem;
107
+ display: block;
103
108
  }
104
109
 
105
110
  .expand-toggle {
106
- width: 1.5rem;
107
- height: 1.5rem;
108
- display: flex;
109
- align-items: center;
110
- justify-content: center;
111
+ width: 20px;
112
+ height: 20px;
113
+ font-size: 10px;
111
114
  border: none;
112
115
  background: none;
113
116
  cursor: pointer;
114
117
  color: #6c757d;
115
- text-decoration: none;
118
+ line-height: 1;
119
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
116
123
  }
117
124
 
118
125
  .expand-toggle:hover {
119
- color: #495057;
120
- background-color: #f8f9fa;
126
+ background-color: rgba(0, 0, 0, 0.05);
121
127
  border-radius: 0.25rem;
122
128
  }
123
129
 
@@ -127,21 +133,13 @@
127
133
  }
128
134
 
129
135
  .expand-placeholder {
130
- width: 1.5rem;
131
- height: 1.5rem;
136
+ width: 20px;
137
+ height: 20px;
132
138
  display: inline-block;
133
139
  }
134
140
 
135
- .form-check {
136
- display: flex;
137
- align-items: center;
138
- gap: 0.5rem;
139
- margin: 0;
140
- }
141
-
142
141
  .form-check-input {
143
142
  cursor: pointer;
144
- margin: 0;
145
143
  flex-shrink: 0;
146
144
  }
147
145
 
@@ -157,8 +155,6 @@
157
155
  display: flex;
158
156
  align-items: center;
159
157
  gap: 0.5rem;
160
- margin: 0;
161
- user-select: none;
162
158
  }
163
159
 
164
160
  .node-icon {
@@ -168,6 +164,5 @@
168
164
 
169
165
  .node-label {
170
166
  font-size: 0.875rem;
171
- line-height: 1.5;
172
167
  }
173
168
  </style>
@@ -1,9 +1,19 @@
1
1
  import type { NodeState, TreeStoreValue } from './tree.model';
2
+ import type { Snippet } from 'svelte';
2
3
  import TreeNode from './TreeNode.svelte';
4
+ interface NodeSlotProps {
5
+ node: NodeState['node'];
6
+ state: {
7
+ checked: boolean;
8
+ indeterminate: boolean;
9
+ expanded: boolean;
10
+ };
11
+ }
3
12
  interface Props {
4
13
  nodeState: NodeState;
5
14
  store: TreeStoreValue;
6
15
  showIndeterminate?: boolean;
16
+ children?: Snippet<[NodeSlotProps]>;
7
17
  }
8
18
  declare const TreeNode: import("svelte").Component<Props, {}, "">;
9
19
  type TreeNode = ReturnType<typeof TreeNode>;
@@ -1,19 +1,31 @@
1
1
  <script lang="ts">
2
2
  import type { TreeStoreValue } from './tree.model';
3
+ import type { Snippet } from 'svelte';
3
4
  import TreeNode from './TreeNode.svelte';
4
5
 
6
+ interface NodeSlotProps {
7
+ node: any;
8
+ state: {
9
+ checked: boolean;
10
+ indeterminate: boolean;
11
+ expanded: boolean;
12
+ };
13
+ }
14
+
5
15
  interface Props {
6
16
  store: TreeStoreValue;
7
17
  showIndeterminate?: boolean;
8
18
  showControls?: boolean;
9
19
  height?: string;
20
+ children?: Snippet<[NodeSlotProps]>;
10
21
  }
11
22
 
12
23
  let {
13
24
  store,
14
25
  showIndeterminate = true,
15
26
  showControls = true,
16
- height = '100%'
27
+ height = '100%',
28
+ children
17
29
  }: Props = $props();
18
30
 
19
31
  // Get root nodes
@@ -97,7 +109,7 @@
97
109
 
98
110
  <div class="tree-nodes">
99
111
  {#each rootNodes as rootNode (rootNode.path)}
100
- <TreeNode nodeState={rootNode} {store} {showIndeterminate} />
112
+ <TreeNode nodeState={rootNode} {store} {showIndeterminate} {children} />
101
113
  {/each}
102
114
  </div>
103
115
  </div>
@@ -1,9 +1,19 @@
1
1
  import type { TreeStoreValue } from './tree.model';
2
+ import type { Snippet } from 'svelte';
3
+ interface NodeSlotProps {
4
+ node: any;
5
+ state: {
6
+ checked: boolean;
7
+ indeterminate: boolean;
8
+ expanded: boolean;
9
+ };
10
+ }
2
11
  interface Props {
3
12
  store: TreeStoreValue;
4
13
  showIndeterminate?: boolean;
5
14
  showControls?: boolean;
6
15
  height?: string;
16
+ children?: Snippet<[NodeSlotProps]>;
7
17
  }
8
18
  declare const TreeView: import("svelte").Component<Props, {}, "">;
9
19
  type TreeView = ReturnType<typeof TreeView>;
@@ -28,6 +28,9 @@ export declare function getPathLevel(path: string, separator?: string): number;
28
28
  export declare function flattenTree<T = any>(nodes: TreeNode<T>[], config: TreeConfig<T>, parentPath?: string, level?: number): Map<string, NodeState>;
29
29
  /**
30
30
  * Calculate which nodes should be indeterminate
31
+ * A node is indeterminate if:
32
+ * - It has children AND
33
+ * - Some (but not all) of its direct children are checked OR indeterminate
31
34
  */
32
35
  export declare function calculateIndeterminateStates(nodes: Map<string, NodeState>, checkedPaths: Set<string>): Set<string>;
33
36
  /**