@smartnet360/svelte-components 0.0.101 → 0.0.103
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.
- package/dist/apps/antenna-pattern/index.d.ts +1 -0
- package/dist/apps/antenna-pattern/index.js +1 -0
- package/dist/apps/antenna-pattern/utils/load-static-antennas.d.ts +17 -0
- package/dist/apps/antenna-pattern/utils/load-static-antennas.js +83 -0
- package/dist/apps/site-check/SiteCheck.svelte +4 -6
- package/dist/core/Charts/ChartCard.svelte +122 -12
- package/dist/core/Charts/ChartCard.svelte.d.ts +2 -0
- package/dist/core/Charts/ChartComponent.svelte +8 -6
- package/dist/core/CoverageMap/ai/AITools.d.ts +117 -0
- package/dist/core/CoverageMap/ai/AITools.js +380 -0
- package/dist/core/CoverageMap/core/CoverageCalculator.d.ts +138 -0
- package/dist/core/CoverageMap/core/CoverageCalculator.js +375 -0
- package/dist/core/CoverageMap/core/GridCalculator.d.ts +115 -0
- package/dist/core/CoverageMap/core/GridCalculator.js +484 -0
- package/dist/core/CoverageMap/core/PathLossModels.d.ts +253 -0
- package/dist/core/CoverageMap/core/PathLossModels.js +380 -0
- package/dist/core/CoverageMap/core/SignalProcessor.d.ts +288 -0
- package/dist/core/CoverageMap/core/SignalProcessor.js +424 -0
- package/dist/core/CoverageMap/data/AntennaStore.d.ts +165 -0
- package/dist/core/CoverageMap/data/AntennaStore.js +327 -0
- package/dist/core/CoverageMap/data/SiteStore.d.ts +155 -0
- package/dist/core/CoverageMap/data/SiteStore.js +355 -0
- package/dist/core/CoverageMap/index.d.ts +74 -0
- package/dist/core/CoverageMap/index.js +103 -0
- package/dist/core/CoverageMap/types.d.ts +252 -0
- package/dist/core/CoverageMap/types.js +7 -0
- package/dist/core/CoverageMap/utils/geoUtils.d.ts +223 -0
- package/dist/core/CoverageMap/utils/geoUtils.js +374 -0
- package/dist/core/CoverageMap/utils/rfUtils.d.ts +329 -0
- package/dist/core/CoverageMap/utils/rfUtils.js +434 -0
- package/dist/core/CoverageMap/visualization/ColorSchemes.d.ts +149 -0
- package/dist/core/CoverageMap/visualization/ColorSchemes.js +377 -0
- package/dist/core/TreeView/index.d.ts +4 -4
- package/dist/core/TreeView/index.js +5 -5
- package/dist/core/TreeView/tree-utils.d.ts +12 -0
- package/dist/core/TreeView/tree-utils.js +115 -6
- package/dist/core/TreeView/tree.store.svelte.d.ts +94 -0
- package/dist/core/TreeView/tree.store.svelte.js +274 -0
- package/dist/map-v2/features/cells/controls/CellFilterControl.svelte +16 -27
- package/dist/map-v2/features/cells/utils/cellGeoJSON.js +1 -0
- package/dist/map-v2/features/repeaters/controls/RepeaterFilterControl.svelte +33 -42
- package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +12 -19
- package/dist/map-v3/core/components/Map.svelte +4 -0
- package/dist/map-v3/core/stores/map.store.svelte.js +2 -0
- package/dist/map-v3/demo/DemoMap.svelte +31 -5
- package/dist/map-v3/demo/demo-cells.js +51 -22
- package/dist/map-v3/features/cells/components/CellFilterControl.svelte +24 -30
- package/dist/map-v3/features/cells/layers/CellsLayer.svelte +29 -9
- package/dist/map-v3/features/cells/logic/geometry.js +3 -0
- package/dist/map-v3/features/cells/stores/cell.data.svelte.d.ts +27 -0
- package/dist/map-v3/features/cells/stores/cell.data.svelte.js +65 -0
- package/dist/map-v3/features/coverage/index.d.ts +12 -0
- package/dist/map-v3/features/coverage/index.js +16 -0
- package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte +198 -0
- package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte.d.ts +10 -0
- package/dist/map-v3/features/coverage/logic/coloring.d.ts +28 -0
- package/dist/map-v3/features/coverage/logic/coloring.js +77 -0
- package/dist/map-v3/features/coverage/logic/geometry.d.ts +33 -0
- package/dist/map-v3/features/coverage/logic/geometry.js +112 -0
- package/dist/map-v3/features/coverage/stores/coverage.data.svelte.d.ts +46 -0
- package/dist/map-v3/features/coverage/stores/coverage.data.svelte.js +95 -0
- package/dist/map-v3/features/coverage/stores/coverage.display.svelte.d.ts +33 -0
- package/dist/map-v3/features/coverage/stores/coverage.display.svelte.js +90 -0
- package/dist/map-v3/features/coverage/types.d.ts +52 -0
- package/dist/map-v3/features/coverage/types.js +7 -0
- package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +14 -20
- package/dist/map-v3/features/selection/components/FeatureSelectionControl.svelte +82 -65
- package/dist/map-v3/features/selection/components/FeatureSelectionControl.svelte.d.ts +5 -9
- package/dist/map-v3/features/selection/index.d.ts +1 -2
- package/dist/map-v3/features/selection/index.js +0 -1
- package/dist/map-v3/features/selection/stores/selection.store.svelte.d.ts +44 -15
- package/dist/map-v3/features/selection/stores/selection.store.svelte.js +163 -40
- package/dist/map-v3/features/selection/types.d.ts +4 -2
- package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +23 -33
- package/dist/map-v3/index.d.ts +4 -0
- package/dist/map-v3/index.js +5 -0
- package/package.json +2 -2
- package/dist/core/TreeView/tree.store.d.ts +0 -10
- package/dist/core/TreeView/tree.store.js +0 -320
- package/dist/map-v3/features/selection/layers/SelectionHighlightLayers.svelte +0 -209
- package/dist/map-v3/features/selection/layers/SelectionHighlightLayers.svelte.d.ts +0 -13
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree Store - Svelte 5 Runes Implementation
|
|
3
|
+
* Reactive state management for tree with persistence
|
|
4
|
+
*
|
|
5
|
+
* This is a modernized implementation using Svelte 5 runes ($state, $derived)
|
|
6
|
+
* with SvelteMap and SvelteSet for reactive collections.
|
|
7
|
+
*
|
|
8
|
+
* BREAKING CHANGE from previous version:
|
|
9
|
+
* - createTreeStore() now returns TreeStoreValue directly (not a Svelte store)
|
|
10
|
+
* - Use `treeStore` directly instead of `$treeStore`
|
|
11
|
+
* - Pass `store={treeStore}` instead of `store={$treeStore}`
|
|
12
|
+
*/
|
|
13
|
+
import type { TreeConfig, TreeState, TreeStoreValue } from './tree.model';
|
|
14
|
+
/**
|
|
15
|
+
* TreeStore class using Svelte 5 runes for reactive state management
|
|
16
|
+
*/
|
|
17
|
+
declare class TreeStore<T = any> {
|
|
18
|
+
#private;
|
|
19
|
+
constructor(config: TreeConfig<T>);
|
|
20
|
+
/** Get current tree state (reactive) */
|
|
21
|
+
get state(): TreeState;
|
|
22
|
+
/** Get config */
|
|
23
|
+
get config(): TreeConfig<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Toggle a node's checked state (with cascading)
|
|
26
|
+
*
|
|
27
|
+
* Logic:
|
|
28
|
+
* 1. Toggle the clicked node
|
|
29
|
+
* 2. Cascade DOWN to all descendants (check/uncheck all children)
|
|
30
|
+
* 3. Propagate UP to all ancestors (update based on their children's states)
|
|
31
|
+
*/
|
|
32
|
+
toggle: (path: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Toggle expanded/collapsed state
|
|
35
|
+
*/
|
|
36
|
+
toggleExpand: (path: string) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Expand all nodes
|
|
39
|
+
*/
|
|
40
|
+
expandAll: () => void;
|
|
41
|
+
/**
|
|
42
|
+
* Collapse all nodes
|
|
43
|
+
*/
|
|
44
|
+
collapseAll: () => void;
|
|
45
|
+
/**
|
|
46
|
+
* Check all nodes
|
|
47
|
+
*/
|
|
48
|
+
checkAll: () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Uncheck all nodes
|
|
51
|
+
*/
|
|
52
|
+
uncheckAll: () => void;
|
|
53
|
+
/**
|
|
54
|
+
* Get all checked paths
|
|
55
|
+
*/
|
|
56
|
+
getCheckedPaths: () => string[];
|
|
57
|
+
/**
|
|
58
|
+
* Get only checked leaf paths (nodes without children)
|
|
59
|
+
*/
|
|
60
|
+
getCheckedLeafPaths: () => string[];
|
|
61
|
+
/**
|
|
62
|
+
* Clear localStorage
|
|
63
|
+
*/
|
|
64
|
+
clearStorage: () => void;
|
|
65
|
+
/**
|
|
66
|
+
* Convert to TreeStoreValue for backward compatibility
|
|
67
|
+
* This allows the store to work with existing components expecting the old interface
|
|
68
|
+
*/
|
|
69
|
+
toStoreValue(): TreeStoreValue;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a tree store with state management and persistence
|
|
73
|
+
*
|
|
74
|
+
* This function maintains backward compatibility with the original API.
|
|
75
|
+
* It returns an object that behaves like the old Svelte store but uses
|
|
76
|
+
* Svelte 5 runes internally for reactivity.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```svelte
|
|
80
|
+
* <script>
|
|
81
|
+
* import { createTreeStore } from './';
|
|
82
|
+
*
|
|
83
|
+
* const treeStore = createTreeStore({
|
|
84
|
+
* nodes: [...],
|
|
85
|
+
* namespace: 'my-app',
|
|
86
|
+
* persistState: true
|
|
87
|
+
* });
|
|
88
|
+
* </script>
|
|
89
|
+
*
|
|
90
|
+
* <TreeView store={treeStore} />
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function createTreeStore<T = any>(config: TreeConfig<T>): TreeStoreValue;
|
|
94
|
+
export { TreeStore };
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree Store - Svelte 5 Runes Implementation
|
|
3
|
+
* Reactive state management for tree with persistence
|
|
4
|
+
*
|
|
5
|
+
* This is a modernized implementation using Svelte 5 runes ($state, $derived)
|
|
6
|
+
* with SvelteMap and SvelteSet for reactive collections.
|
|
7
|
+
*
|
|
8
|
+
* BREAKING CHANGE from previous version:
|
|
9
|
+
* - createTreeStore() now returns TreeStoreValue directly (not a Svelte store)
|
|
10
|
+
* - Use `treeStore` directly instead of `$treeStore`
|
|
11
|
+
* - Pass `store={treeStore}` instead of `store={$treeStore}`
|
|
12
|
+
*/
|
|
13
|
+
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
|
14
|
+
import { flattenTreeToSvelteMap, buildInitialState, calculateIndeterminateStates, getDescendantPaths, getAncestorPaths, saveStateToStorage, loadStateFromStorage, clearStorageForNamespace } from './tree-utils';
|
|
15
|
+
/**
|
|
16
|
+
* TreeStore class using Svelte 5 runes for reactive state management
|
|
17
|
+
*/
|
|
18
|
+
class TreeStore {
|
|
19
|
+
// Reactive state using $state
|
|
20
|
+
#state = $state({
|
|
21
|
+
nodes: new SvelteMap(),
|
|
22
|
+
checkedPaths: new SvelteSet(),
|
|
23
|
+
expandedPaths: new SvelteSet(),
|
|
24
|
+
indeterminatePaths: new SvelteSet(),
|
|
25
|
+
rootPaths: []
|
|
26
|
+
});
|
|
27
|
+
#config;
|
|
28
|
+
#separator;
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.#config = config;
|
|
31
|
+
this.#separator = config.pathSeparator || ':';
|
|
32
|
+
// Flatten tree structure using SvelteMap
|
|
33
|
+
const nodesMap = flattenTreeToSvelteMap(config.nodes, config);
|
|
34
|
+
// Build initial state
|
|
35
|
+
const initialState = buildInitialState(nodesMap, config);
|
|
36
|
+
// Set initial state
|
|
37
|
+
this.#state = initialState;
|
|
38
|
+
// Load persisted state if enabled
|
|
39
|
+
if (config.persistState && config.namespace) {
|
|
40
|
+
const persistedState = loadStateFromStorage(config.namespace, this.#state);
|
|
41
|
+
if (persistedState.checkedPaths) {
|
|
42
|
+
this.#state.checkedPaths = persistedState.checkedPaths;
|
|
43
|
+
}
|
|
44
|
+
if (persistedState.expandedPaths) {
|
|
45
|
+
this.#state.expandedPaths = persistedState.expandedPaths;
|
|
46
|
+
}
|
|
47
|
+
if (persistedState.indeterminatePaths) {
|
|
48
|
+
this.#state.indeterminatePaths = persistedState.indeterminatePaths;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Get current tree state (reactive) */
|
|
53
|
+
get state() {
|
|
54
|
+
return this.#state;
|
|
55
|
+
}
|
|
56
|
+
/** Get config */
|
|
57
|
+
get config() {
|
|
58
|
+
return this.#config;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Persist state to localStorage if enabled
|
|
62
|
+
*/
|
|
63
|
+
#persistState() {
|
|
64
|
+
if (this.#config.persistState && this.#config.namespace) {
|
|
65
|
+
saveStateToStorage(this.#config.namespace, this.#state);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Toggle a node's checked state (with cascading)
|
|
70
|
+
*
|
|
71
|
+
* Logic:
|
|
72
|
+
* 1. Toggle the clicked node
|
|
73
|
+
* 2. Cascade DOWN to all descendants (check/uncheck all children)
|
|
74
|
+
* 3. Propagate UP to all ancestors (update based on their children's states)
|
|
75
|
+
*/
|
|
76
|
+
toggle = (path) => {
|
|
77
|
+
const nodeState = this.#state.nodes.get(path);
|
|
78
|
+
if (!nodeState) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const newChecked = !this.#state.checkedPaths.has(path);
|
|
82
|
+
// STEP 0: If singleRootSelect mode and this is a root node being checked, uncheck all other roots
|
|
83
|
+
if (this.#config.singleRootSelect && newChecked && nodeState.level === 0) {
|
|
84
|
+
// Uncheck all root nodes and their descendants
|
|
85
|
+
this.#state.rootPaths.forEach(rootPath => {
|
|
86
|
+
if (rootPath !== path) {
|
|
87
|
+
this.#state.checkedPaths.delete(rootPath);
|
|
88
|
+
// Also uncheck all descendants of this root
|
|
89
|
+
const rootDescendants = getDescendantPaths(rootPath, this.#state.nodes, this.#separator);
|
|
90
|
+
rootDescendants.forEach(descendantPath => {
|
|
91
|
+
this.#state.checkedPaths.delete(descendantPath);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// STEP 0.5: If singleLevel1Select mode and this is a Level 1 node being checked, uncheck sibling Level 1 nodes
|
|
97
|
+
if (this.#config.singleLevel1Select && newChecked && nodeState.level === 1) {
|
|
98
|
+
const parentPath = nodeState.parentPath;
|
|
99
|
+
// Find and uncheck all Level 1 siblings (same parent, same level, different path)
|
|
100
|
+
this.#state.nodes.forEach((node, nodePath) => {
|
|
101
|
+
if (node.level === 1 &&
|
|
102
|
+
node.parentPath === parentPath &&
|
|
103
|
+
nodePath !== path) {
|
|
104
|
+
this.#state.checkedPaths.delete(nodePath);
|
|
105
|
+
// Also uncheck all descendants of this sibling
|
|
106
|
+
const siblingDescendants = getDescendantPaths(nodePath, this.#state.nodes, this.#separator);
|
|
107
|
+
siblingDescendants.forEach(descendantPath => {
|
|
108
|
+
this.#state.checkedPaths.delete(descendantPath);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// STEP 1: Update this node
|
|
114
|
+
if (newChecked) {
|
|
115
|
+
this.#state.checkedPaths.add(path);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this.#state.checkedPaths.delete(path);
|
|
119
|
+
}
|
|
120
|
+
// STEP 2: CASCADE DOWN - Update all descendants to match
|
|
121
|
+
const descendants = getDescendantPaths(path, this.#state.nodes, this.#separator);
|
|
122
|
+
descendants.forEach(descendantPath => {
|
|
123
|
+
if (newChecked) {
|
|
124
|
+
this.#state.checkedPaths.add(descendantPath);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.#state.checkedPaths.delete(descendantPath);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
// STEP 3: PROPAGATE UP - Update all ancestors based on their children
|
|
131
|
+
const ancestorPaths = getAncestorPaths(path, this.#separator);
|
|
132
|
+
// Process ancestors from deepest to shallowest (reverse order)
|
|
133
|
+
for (let i = ancestorPaths.length - 1; i >= 0; i--) {
|
|
134
|
+
const ancestorPath = ancestorPaths[i];
|
|
135
|
+
const ancestor = this.#state.nodes.get(ancestorPath);
|
|
136
|
+
if (!ancestor)
|
|
137
|
+
continue;
|
|
138
|
+
// Count how many direct children are checked
|
|
139
|
+
const checkedChildrenCount = ancestor.childPaths.filter(childPath => this.#state.checkedPaths.has(childPath)).length;
|
|
140
|
+
const totalChildren = ancestor.childPaths.length;
|
|
141
|
+
// Update ancestor based on children states
|
|
142
|
+
if (checkedChildrenCount === totalChildren) {
|
|
143
|
+
this.#state.checkedPaths.add(ancestorPath);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.#state.checkedPaths.delete(ancestorPath);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// STEP 4: Recalculate indeterminate states
|
|
150
|
+
const newIndeterminatePaths = calculateIndeterminateStates(this.#state.nodes, this.#state.checkedPaths);
|
|
151
|
+
this.#state.indeterminatePaths.clear();
|
|
152
|
+
newIndeterminatePaths.forEach(p => this.#state.indeterminatePaths.add(p));
|
|
153
|
+
this.#persistState();
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Toggle expanded/collapsed state
|
|
157
|
+
*/
|
|
158
|
+
toggleExpand = (path) => {
|
|
159
|
+
if (this.#state.expandedPaths.has(path)) {
|
|
160
|
+
this.#state.expandedPaths.delete(path);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.#state.expandedPaths.add(path);
|
|
164
|
+
}
|
|
165
|
+
this.#persistState();
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Expand all nodes
|
|
169
|
+
*/
|
|
170
|
+
expandAll = () => {
|
|
171
|
+
this.#state.nodes.forEach((nodeState, path) => {
|
|
172
|
+
if (nodeState.childPaths.length > 0) {
|
|
173
|
+
this.#state.expandedPaths.add(path);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
this.#persistState();
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Collapse all nodes
|
|
180
|
+
*/
|
|
181
|
+
collapseAll = () => {
|
|
182
|
+
this.#state.expandedPaths.clear();
|
|
183
|
+
this.#persistState();
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Check all nodes
|
|
187
|
+
*/
|
|
188
|
+
checkAll = () => {
|
|
189
|
+
this.#state.nodes.forEach((_, path) => {
|
|
190
|
+
this.#state.checkedPaths.add(path);
|
|
191
|
+
});
|
|
192
|
+
// Clear indeterminate states when all checked
|
|
193
|
+
this.#state.indeterminatePaths.clear();
|
|
194
|
+
this.#persistState();
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Uncheck all nodes
|
|
198
|
+
*/
|
|
199
|
+
uncheckAll = () => {
|
|
200
|
+
this.#state.checkedPaths.clear();
|
|
201
|
+
this.#state.indeterminatePaths.clear();
|
|
202
|
+
this.#persistState();
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Get all checked paths
|
|
206
|
+
*/
|
|
207
|
+
getCheckedPaths = () => {
|
|
208
|
+
return Array.from(this.#state.checkedPaths);
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Get only checked leaf paths (nodes without children)
|
|
212
|
+
*/
|
|
213
|
+
getCheckedLeafPaths = () => {
|
|
214
|
+
return Array.from(this.#state.checkedPaths).filter(path => {
|
|
215
|
+
const node = this.#state.nodes.get(path);
|
|
216
|
+
return node && node.childPaths.length === 0;
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Clear localStorage
|
|
221
|
+
*/
|
|
222
|
+
clearStorage = () => {
|
|
223
|
+
if (this.#config.namespace) {
|
|
224
|
+
clearStorageForNamespace(this.#config.namespace);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Convert to TreeStoreValue for backward compatibility
|
|
229
|
+
* This allows the store to work with existing components expecting the old interface
|
|
230
|
+
*/
|
|
231
|
+
toStoreValue() {
|
|
232
|
+
return {
|
|
233
|
+
state: this.#state,
|
|
234
|
+
config: this.#config,
|
|
235
|
+
toggle: this.toggle,
|
|
236
|
+
toggleExpand: this.toggleExpand,
|
|
237
|
+
expandAll: this.expandAll,
|
|
238
|
+
collapseAll: this.collapseAll,
|
|
239
|
+
checkAll: this.checkAll,
|
|
240
|
+
uncheckAll: this.uncheckAll,
|
|
241
|
+
getCheckedPaths: this.getCheckedPaths,
|
|
242
|
+
getCheckedLeafPaths: this.getCheckedLeafPaths,
|
|
243
|
+
clearStorage: this.clearStorage
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Create a tree store with state management and persistence
|
|
249
|
+
*
|
|
250
|
+
* This function maintains backward compatibility with the original API.
|
|
251
|
+
* It returns an object that behaves like the old Svelte store but uses
|
|
252
|
+
* Svelte 5 runes internally for reactivity.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```svelte
|
|
256
|
+
* <script>
|
|
257
|
+
* import { createTreeStore } from './';
|
|
258
|
+
*
|
|
259
|
+
* const treeStore = createTreeStore({
|
|
260
|
+
* nodes: [...],
|
|
261
|
+
* namespace: 'my-app',
|
|
262
|
+
* persistState: true
|
|
263
|
+
* });
|
|
264
|
+
* </script>
|
|
265
|
+
*
|
|
266
|
+
* <TreeView store={treeStore} />
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
export function createTreeStore(config) {
|
|
270
|
+
const store = new TreeStore(config);
|
|
271
|
+
return store.toStoreValue();
|
|
272
|
+
}
|
|
273
|
+
// Also export the class for advanced usage
|
|
274
|
+
export { TreeStore };
|
|
@@ -11,14 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { onMount } from 'svelte';
|
|
14
|
-
import type { Writable } from 'svelte/store';
|
|
15
14
|
import MapControl from '../../../shared/controls/MapControl.svelte';
|
|
16
|
-
import TreeView from '../../../../core/TreeView
|
|
17
|
-
import { createTreeStore } from '../../../../core/TreeView/tree.store';
|
|
15
|
+
import { TreeView, createTreeStore, type TreeStoreValue } from '../../../../core/TreeView';
|
|
18
16
|
import { buildCellTree, getFilteredCells } from '../utils/cellTree';
|
|
19
17
|
import type { CellStoreContext } from '../stores/cellStoreContext.svelte';
|
|
20
18
|
import type { CellGroupingField, CellGroupingLabels } from '../types';
|
|
21
|
-
import type { TreeStoreValue } from '../../../../core/TreeView/tree.model';
|
|
22
19
|
|
|
23
20
|
interface Props {
|
|
24
21
|
/** Cell store context */
|
|
@@ -53,7 +50,7 @@
|
|
|
53
50
|
initiallyCollapsed = true
|
|
54
51
|
}: Props = $props();
|
|
55
52
|
|
|
56
|
-
let treeStore = $state<
|
|
53
|
+
let treeStore = $state<TreeStoreValue | null>(null);
|
|
57
54
|
let level1 = $state<Exclude<CellGroupingField, 'none'>>(store.groupingConfig.level1);
|
|
58
55
|
let level2 = $state<CellGroupingField>(store.groupingConfig.level2);
|
|
59
56
|
|
|
@@ -109,29 +106,21 @@
|
|
|
109
106
|
persistState: true,
|
|
110
107
|
defaultExpandAll: false
|
|
111
108
|
});
|
|
109
|
+
}
|
|
112
110
|
|
|
113
|
-
|
|
111
|
+
// Sync tree selection with cell store (reactive via $effect)
|
|
112
|
+
$effect(() => {
|
|
114
113
|
if (treeStore) {
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Avoid unnecessary updates if the set hasn't changed
|
|
124
|
-
if (
|
|
125
|
-
newCheckedPaths.size !== checkedPaths.size ||
|
|
126
|
-
![...newCheckedPaths].every((path) => checkedPaths.has(path))
|
|
127
|
-
) {
|
|
128
|
-
checkedPaths = newCheckedPaths;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return () => unsub();
|
|
114
|
+
const newCheckedPaths = new Set(treeStore.getCheckedPaths());
|
|
115
|
+
// Only update if changed to avoid infinite loops
|
|
116
|
+
if (
|
|
117
|
+
newCheckedPaths.size !== checkedPaths.size ||
|
|
118
|
+
![...newCheckedPaths].every((path) => checkedPaths.has(path))
|
|
119
|
+
) {
|
|
120
|
+
checkedPaths = newCheckedPaths;
|
|
121
|
+
}
|
|
133
122
|
}
|
|
134
|
-
}
|
|
123
|
+
});
|
|
135
124
|
|
|
136
125
|
onMount(() => {
|
|
137
126
|
rebuildTree();
|
|
@@ -235,9 +224,9 @@
|
|
|
235
224
|
</div>
|
|
236
225
|
|
|
237
226
|
<!-- Tree View -->
|
|
238
|
-
{#if treeStore
|
|
227
|
+
{#if treeStore}
|
|
239
228
|
<div class="cell-filter-tree">
|
|
240
|
-
<TreeView store={
|
|
229
|
+
<TreeView store={treeStore} showControls={false}>
|
|
241
230
|
{#snippet children({ node, state })}
|
|
242
231
|
<!-- Color picker for all leaf nodes (nodes with cells) -->
|
|
243
232
|
{#if node.metadata?.type === 'leafGroup'}
|
|
@@ -42,6 +42,7 @@ export function cellsToGeoJSON(cells, currentZoom, baseRadius = 500, groupColorM
|
|
|
42
42
|
// Build feature with styling properties
|
|
43
43
|
return {
|
|
44
44
|
type: 'Feature',
|
|
45
|
+
id: Number(cell.cellName), // Numeric ID for feature-state (cellName is numeric string)
|
|
45
46
|
geometry: arc.geometry,
|
|
46
47
|
properties: {
|
|
47
48
|
// Cell identification
|
|
@@ -10,13 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { onMount } from 'svelte';
|
|
13
|
-
import type { Writable } from 'svelte/store';
|
|
14
13
|
import MapControl from '../../../shared/controls/MapControl.svelte';
|
|
15
|
-
import TreeView from '../../../../core/TreeView
|
|
16
|
-
import { createTreeStore } from '../../../../core/TreeView/tree.store';
|
|
14
|
+
import { TreeView, createTreeStore, type TreeStoreValue } from '../../../../core/TreeView';
|
|
17
15
|
import { buildRepeaterTree, getFilteredRepeaters } from '../utils/repeaterTree';
|
|
18
16
|
import type { RepeaterStoreContext } from '../stores/repeaterStoreContext.svelte';
|
|
19
|
-
import type { TreeStoreValue } from '../../../../core/TreeView/tree.model';
|
|
20
17
|
|
|
21
18
|
interface Props {
|
|
22
19
|
/** Repeater store context */
|
|
@@ -42,7 +39,7 @@
|
|
|
42
39
|
initiallyCollapsed = true
|
|
43
40
|
}: Props = $props();
|
|
44
41
|
|
|
45
|
-
let treeStore = $state<
|
|
42
|
+
let treeStore = $state<TreeStoreValue | null>(null);
|
|
46
43
|
|
|
47
44
|
// Rebuild tree when repeaters change
|
|
48
45
|
function rebuildTree() {
|
|
@@ -56,45 +53,39 @@
|
|
|
56
53
|
persistState: true,
|
|
57
54
|
defaultExpandAll: false
|
|
58
55
|
});
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Sync tree selection with repeater store (reactive via $effect)
|
|
59
|
+
$effect(() => {
|
|
61
60
|
if (treeStore) {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const isVisible = store.isTechBandVisible(tech, fband);
|
|
86
|
-
|
|
87
|
-
// Only toggle if state needs to change
|
|
88
|
-
if (shouldBeVisible !== isVisible) {
|
|
89
|
-
store.toggleTechBand(tech, fband);
|
|
90
|
-
}
|
|
61
|
+
const checkedPaths = treeStore.getCheckedPaths();
|
|
62
|
+
const checkedPathsSet = new Set(checkedPaths);
|
|
63
|
+
const selectedTechBands = getFilteredRepeaters(checkedPathsSet);
|
|
64
|
+
|
|
65
|
+
// Get all tech:fband combinations from repeaters
|
|
66
|
+
const allTechBands = new Set<string>();
|
|
67
|
+
for (const repeater of store.repeaters) {
|
|
68
|
+
const key = `${repeater.tech}:${repeater.fband}`;
|
|
69
|
+
allTechBands.add(key);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// If only root checked, show all
|
|
73
|
+
if (selectedTechBands.size === 0 && checkedPathsSet.has('root')) {
|
|
74
|
+
// All visible - no toggle needed
|
|
75
|
+
} else {
|
|
76
|
+
// Toggle visibility based on selection
|
|
77
|
+
for (const techBand of allTechBands) {
|
|
78
|
+
const [tech, fband] = techBand.split(':');
|
|
79
|
+
const shouldBeVisible = selectedTechBands.has(techBand);
|
|
80
|
+
const isVisible = store.isTechBandVisible(tech, fband);
|
|
81
|
+
|
|
82
|
+
if (shouldBeVisible !== isVisible) {
|
|
83
|
+
store.toggleTechBand(tech, fband);
|
|
91
84
|
}
|
|
92
85
|
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return () => unsub();
|
|
86
|
+
}
|
|
96
87
|
}
|
|
97
|
-
}
|
|
88
|
+
});
|
|
98
89
|
|
|
99
90
|
// Handle color changes from tree
|
|
100
91
|
function handleColorChange(nodeId: string, color: string) {
|
|
@@ -117,9 +108,9 @@
|
|
|
117
108
|
</script>
|
|
118
109
|
|
|
119
110
|
<MapControl {position} {title} {icon} {iconOnlyWhenCollapsed} collapsible={true} {initiallyCollapsed}>
|
|
120
|
-
{#if treeStore
|
|
111
|
+
{#if treeStore}
|
|
121
112
|
<div class="repeater-filter-tree">
|
|
122
|
-
<TreeView store={
|
|
113
|
+
<TreeView store={treeStore} showControls={false}>
|
|
123
114
|
{#snippet children({ node, state })}
|
|
124
115
|
<!-- Color picker for leaf nodes (tech:fband combinations) -->
|
|
125
116
|
{#if node.metadata?.color}
|
|
@@ -10,13 +10,10 @@
|
|
|
10
10
|
* <SiteFilterControl {store} position="top-left" />
|
|
11
11
|
*/
|
|
12
12
|
import { onMount } from 'svelte';
|
|
13
|
-
import type { Writable } from 'svelte/store';
|
|
14
13
|
import MapControl from '../../../shared/controls/MapControl.svelte';
|
|
15
|
-
import TreeView from '../../../../core/TreeView
|
|
16
|
-
import { createTreeStore } from '../../../../core/TreeView/tree.store';
|
|
14
|
+
import { TreeView, createTreeStore, type TreeStoreValue } from '../../../../core/TreeView';
|
|
17
15
|
import { buildSiteTree, getFilteredSites } from '../utils/siteTreeUtils';
|
|
18
16
|
import type { SiteStoreContext } from '../stores/siteStoreContext.svelte';
|
|
19
|
-
import type { TreeStoreValue } from '../../../../core/TreeView/tree.model';
|
|
20
17
|
|
|
21
18
|
interface Props {
|
|
22
19
|
/** Site store instance */
|
|
@@ -42,7 +39,7 @@
|
|
|
42
39
|
initiallyCollapsed = true
|
|
43
40
|
}: Props = $props();
|
|
44
41
|
|
|
45
|
-
let treeStore = $state<
|
|
42
|
+
let treeStore = $state<TreeStoreValue | null>(null);
|
|
46
43
|
|
|
47
44
|
onMount(() => {
|
|
48
45
|
if (store.allSites.length > 0) {
|
|
@@ -54,19 +51,15 @@
|
|
|
54
51
|
persistState: true,
|
|
55
52
|
defaultExpandAll: false
|
|
56
53
|
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// Update the site store directly
|
|
65
|
-
store.filteredSites = newFilteredSites;
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
return () => unsub();
|
|
69
|
-
}
|
|
57
|
+
// Sync tree selection with site store (reactive via $effect)
|
|
58
|
+
$effect(() => {
|
|
59
|
+
if (treeStore) {
|
|
60
|
+
const checkedPaths = treeStore.getCheckedPaths();
|
|
61
|
+
const newFilteredSites = getFilteredSites(checkedPaths, store.allSites);
|
|
62
|
+
store.filteredSites = newFilteredSites;
|
|
70
63
|
}
|
|
71
64
|
});
|
|
72
65
|
|
|
@@ -76,9 +69,9 @@
|
|
|
76
69
|
</script>
|
|
77
70
|
|
|
78
71
|
<MapControl {position} {title} {icon} {iconOnlyWhenCollapsed} collapsible={true} {initiallyCollapsed}>
|
|
79
|
-
{#if treeStore
|
|
72
|
+
{#if treeStore}
|
|
80
73
|
<div class="site-filter-tree">
|
|
81
|
-
<TreeView store={
|
|
74
|
+
<TreeView store={treeStore} showControls={false}>
|
|
82
75
|
{#snippet children({ node, state })}
|
|
83
76
|
<!-- Custom node rendering with color picker for leaf nodes -->
|
|
84
77
|
{#if node.metadata?.type === 'featureGroup'}
|
|
@@ -73,6 +73,10 @@
|
|
|
73
73
|
<div bind:this={mapContainer} class="map-instance"></div>
|
|
74
74
|
{#if mapStore.loaded}
|
|
75
75
|
{@render children?.()}
|
|
76
|
+
{:else}
|
|
77
|
+
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
|
|
78
|
+
Loading map...
|
|
79
|
+
</div>
|
|
76
80
|
{/if}
|
|
77
81
|
</div>
|
|
78
82
|
|
|
@@ -8,11 +8,13 @@ export class MapStore {
|
|
|
8
8
|
init(container, options) {
|
|
9
9
|
if (this.map)
|
|
10
10
|
return;
|
|
11
|
+
console.log('[MapStore] Initializing map with options:', options);
|
|
11
12
|
this.map = new mapboxgl.Map({
|
|
12
13
|
container,
|
|
13
14
|
...options
|
|
14
15
|
});
|
|
15
16
|
this.map.on('load', () => {
|
|
17
|
+
console.log('[MapStore] Map loaded!');
|
|
16
18
|
this.loaded = true;
|
|
17
19
|
});
|
|
18
20
|
}
|