@smartnet360/svelte-components 0.0.53 → 0.0.55
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/core/TreeView/TreeNode.svelte +40 -45
- package/dist/core/TreeView/TreeNode.svelte.d.ts +10 -0
- package/dist/core/TreeView/TreeView.svelte +14 -2
- package/dist/core/TreeView/TreeView.svelte.d.ts +10 -0
- package/dist/core/TreeView/tree-utils.d.ts +3 -0
- package/dist/core/TreeView/tree-utils.js +33 -9
- package/dist/core/TreeView/tree.store.js +49 -24
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/map-v2/core/controls/MapStyleControl.svelte +289 -0
- package/dist/map-v2/core/controls/MapStyleControl.svelte.d.ts +24 -0
- package/dist/{map → map-v2/core}/hooks/useMapbox.d.ts +1 -1
- package/dist/{map → map-v2/core}/hooks/useMapbox.js +1 -1
- package/dist/map-v2/core/index.d.ts +11 -0
- package/dist/map-v2/core/index.js +14 -0
- package/dist/map-v2/core/providers/MapboxProvider.svelte +140 -0
- package/dist/map-v2/core/providers/MapboxProvider.svelte.d.ts +33 -0
- package/dist/{map → map-v2/core}/stores/mapStore.d.ts +2 -2
- package/dist/{map → map-v2/core}/stores/mapStore.js +2 -2
- package/dist/map-v2/core/types.d.ts +13 -0
- package/dist/map-v2/core/types.js +7 -0
- package/dist/map-v2/demo/DemoMap.svelte +63 -0
- package/dist/{map → map-v2}/demo/DemoMap.svelte.d.ts +3 -4
- package/dist/map-v2/demo/demo-data.d.ts +8 -0
- package/dist/map-v2/demo/demo-data.js +128 -0
- package/dist/map-v2/demo/index.d.ts +7 -0
- package/dist/map-v2/demo/index.js +9 -0
- package/dist/{map → map-v2/features/sites}/controls/SiteFilterControl.svelte +27 -41
- package/dist/{map → map-v2/features/sites}/controls/SiteFilterControl.svelte.d.ts +4 -6
- package/dist/map-v2/features/sites/controls/SiteSizeSlider.svelte +185 -0
- package/dist/map-v2/features/sites/controls/SiteSizeSlider.svelte.d.ts +20 -0
- package/dist/map-v2/features/sites/index.d.ts +14 -0
- package/dist/map-v2/features/sites/index.js +16 -0
- package/dist/map-v2/features/sites/layers/SitesLayer.svelte +277 -0
- package/dist/map-v2/features/sites/layers/SitesLayer.svelte.d.ts +12 -0
- package/dist/map-v2/features/sites/stores/siteStore.d.ts +18 -0
- package/dist/map-v2/features/sites/stores/siteStore.js +36 -0
- package/dist/map-v2/features/sites/stores/siteStoreContext.svelte.d.ts +29 -0
- package/dist/map-v2/features/sites/stores/siteStoreContext.svelte.js +73 -0
- package/dist/map-v2/features/sites/types.d.ts +36 -0
- package/dist/map-v2/features/sites/types.js +4 -0
- package/dist/map-v2/features/sites/utils/siteGeoJSON.d.ts +31 -0
- package/dist/map-v2/features/sites/utils/siteGeoJSON.js +34 -0
- package/dist/map-v2/features/sites/utils/siteTreeUtils.d.ts +14 -0
- package/dist/{map → map-v2/features/sites}/utils/siteTreeUtils.js +3 -50
- package/dist/map-v2/index.d.ts +10 -0
- package/dist/map-v2/index.js +22 -0
- package/dist/{map → map-v2/shared}/controls/MapControl.svelte +1 -1
- package/dist/map-v2/shared/index.d.ts +7 -0
- package/dist/map-v2/shared/index.js +9 -0
- package/package.json +1 -1
- package/dist/map/demo/DemoMap.svelte +0 -98
- package/dist/map/demo/demo-data.d.ts +0 -12
- package/dist/map/demo/demo-data.js +0 -220
- package/dist/map/hooks/useCellData.d.ts +0 -14
- package/dist/map/hooks/useCellData.js +0 -29
- package/dist/map/index.d.ts +0 -27
- package/dist/map/index.js +0 -47
- package/dist/map/layers/CellsLayer.svelte +0 -242
- package/dist/map/layers/CellsLayer.svelte.d.ts +0 -21
- package/dist/map/layers/CoverageLayer.svelte +0 -37
- package/dist/map/layers/CoverageLayer.svelte.d.ts +0 -9
- package/dist/map/layers/LayerBase.d.ts +0 -42
- package/dist/map/layers/LayerBase.js +0 -58
- package/dist/map/layers/SitesLayer.svelte +0 -282
- package/dist/map/layers/SitesLayer.svelte.d.ts +0 -19
- package/dist/map/providers/CellDataProvider.svelte +0 -43
- package/dist/map/providers/CellDataProvider.svelte.d.ts +0 -12
- package/dist/map/providers/MapboxProvider.svelte +0 -38
- package/dist/map/providers/MapboxProvider.svelte.d.ts +0 -9
- package/dist/map/providers/providerHelpers.d.ts +0 -17
- package/dist/map/providers/providerHelpers.js +0 -26
- package/dist/map/stores/cellDataStore.d.ts +0 -21
- package/dist/map/stores/cellDataStore.js +0 -53
- package/dist/map/stores/interactions.d.ts +0 -20
- package/dist/map/stores/interactions.js +0 -33
- package/dist/map/types.d.ts +0 -115
- package/dist/map/types.js +0 -10
- package/dist/map/utils/geojson.d.ts +0 -20
- package/dist/map/utils/geojson.js +0 -78
- package/dist/map/utils/math.d.ts +0 -40
- package/dist/map/utils/math.js +0 -95
- package/dist/map/utils/siteTreeUtils.d.ts +0 -27
- /package/dist/{map → map-v2/shared}/controls/MapControl.svelte.d.ts +0 -0
- /package/dist/{map → map-v2/shared}/utils/mapboxHelpers.d.ts +0 -0
- /package/dist/{map → map-v2/shared}/utils/mapboxHelpers.js +0 -0
|
@@ -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(`
|
|
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="
|
|
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="
|
|
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
|
-
{
|
|
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
|
-
<
|
|
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
|
-
|
|
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:
|
|
107
|
-
height:
|
|
108
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
131
|
-
height:
|
|
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
|
/**
|
|
@@ -76,21 +76,45 @@ export function flattenTree(nodes, config, parentPath = '', level = 0) {
|
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* Calculate which nodes should be indeterminate
|
|
79
|
+
* A node is indeterminate if:
|
|
80
|
+
* - It has children AND
|
|
81
|
+
* - Some (but not all) of its direct children are checked OR indeterminate
|
|
79
82
|
*/
|
|
80
83
|
export function calculateIndeterminateStates(nodes, checkedPaths) {
|
|
81
84
|
const indeterminate = new Set();
|
|
82
|
-
// For each node with children
|
|
83
|
-
|
|
85
|
+
// For each node with children, check from deepest to shallowest
|
|
86
|
+
// This ensures we calculate indeterminate state correctly
|
|
87
|
+
const nodesArray = Array.from(nodes.entries());
|
|
88
|
+
// Sort by level (deepest first) to ensure we process children before parents
|
|
89
|
+
nodesArray.sort((a, b) => b[1].level - a[1].level);
|
|
90
|
+
for (const [path, nodeState] of nodesArray) {
|
|
91
|
+
// Skip leaf nodes - they can't be indeterminate
|
|
84
92
|
if (nodeState.childPaths.length === 0)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
continue;
|
|
94
|
+
// Count direct children that are either checked or indeterminate
|
|
95
|
+
let checkedCount = 0;
|
|
96
|
+
let indeterminateCount = 0;
|
|
97
|
+
for (const childPath of nodeState.childPaths) {
|
|
98
|
+
if (checkedPaths.has(childPath)) {
|
|
99
|
+
checkedCount++;
|
|
100
|
+
}
|
|
101
|
+
else if (indeterminate.has(childPath)) {
|
|
102
|
+
indeterminateCount++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const totalChildren = nodeState.childPaths.length;
|
|
106
|
+
const affectedChildren = checkedCount + indeterminateCount;
|
|
107
|
+
// Node is indeterminate if:
|
|
108
|
+
// 1. Some children are checked/indeterminate, but not all
|
|
109
|
+
// 2. At least one child is indeterminate (even if all are checked/indeterminate)
|
|
110
|
+
if (affectedChildren > 0 && affectedChildren < totalChildren) {
|
|
91
111
|
indeterminate.add(path);
|
|
92
112
|
}
|
|
93
|
-
|
|
113
|
+
else if (indeterminateCount > 0) {
|
|
114
|
+
// If any child is indeterminate, parent is indeterminate
|
|
115
|
+
indeterminate.add(path);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
94
118
|
return indeterminate;
|
|
95
119
|
}
|
|
96
120
|
/**
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Svelte writable store for managing tree state with persistence
|
|
4
4
|
*/
|
|
5
5
|
import { writable } from 'svelte/store';
|
|
6
|
-
import { flattenTree, buildInitialState, calculateIndeterminateStates, getDescendantPaths, getParentPath, saveStateToStorage, loadStateFromStorage, clearStorageForNamespace } from './tree-utils';
|
|
6
|
+
import { flattenTree, buildInitialState, calculateIndeterminateStates, getDescendantPaths, getParentPath, getAncestorPaths, saveStateToStorage, loadStateFromStorage, clearStorageForNamespace } from './tree-utils';
|
|
7
7
|
import { log } from '../logger';
|
|
8
8
|
/**
|
|
9
9
|
* Create a tree store with state management and persistence
|
|
@@ -70,6 +70,11 @@ export function createTreeStore(config) {
|
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Toggle a node's checked state (with cascading)
|
|
73
|
+
*
|
|
74
|
+
* Logic:
|
|
75
|
+
* 1. Toggle the clicked node
|
|
76
|
+
* 2. Cascade DOWN to all descendants (check/uncheck all children)
|
|
77
|
+
* 3. Propagate UP to all ancestors (update based on their children's states)
|
|
73
78
|
*/
|
|
74
79
|
function toggle(path) {
|
|
75
80
|
log('🔄 Toggling node', { path });
|
|
@@ -81,16 +86,17 @@ export function createTreeStore(config) {
|
|
|
81
86
|
}
|
|
82
87
|
const newChecked = !state.checkedPaths.has(path);
|
|
83
88
|
const newCheckedPaths = new Set(state.checkedPaths);
|
|
84
|
-
|
|
89
|
+
log('📌 Toggle action', { path, newChecked });
|
|
90
|
+
// STEP 1: Update this node
|
|
85
91
|
if (newChecked) {
|
|
86
92
|
newCheckedPaths.add(path);
|
|
87
93
|
}
|
|
88
94
|
else {
|
|
89
95
|
newCheckedPaths.delete(path);
|
|
90
96
|
}
|
|
91
|
-
//
|
|
97
|
+
// STEP 2: CASCADE DOWN - Update all descendants to match
|
|
92
98
|
const descendants = getDescendantPaths(path, state.nodes, separator);
|
|
93
|
-
log('
|
|
99
|
+
log('⬇️ Cascading to descendants', {
|
|
94
100
|
path,
|
|
95
101
|
descendantCount: descendants.length,
|
|
96
102
|
newChecked
|
|
@@ -103,29 +109,48 @@ export function createTreeStore(config) {
|
|
|
103
109
|
newCheckedPaths.delete(descendantPath);
|
|
104
110
|
}
|
|
105
111
|
});
|
|
106
|
-
// Update ancestors
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
// STEP 3: PROPAGATE UP - Update all ancestors based on their children
|
|
113
|
+
// We need to update from the deepest ancestor up to the root
|
|
114
|
+
const ancestorPaths = getAncestorPaths(path, separator);
|
|
115
|
+
log('⬆️ Propagating to ancestors', {
|
|
116
|
+
path,
|
|
117
|
+
ancestorCount: ancestorPaths.length,
|
|
118
|
+
ancestors: ancestorPaths
|
|
119
|
+
});
|
|
120
|
+
// Process ancestors from deepest to shallowest (reverse order)
|
|
121
|
+
// This ensures we calculate states correctly as we go up
|
|
122
|
+
for (let i = ancestorPaths.length - 1; i >= 0; i--) {
|
|
123
|
+
const ancestorPath = ancestorPaths[i];
|
|
124
|
+
const ancestor = state.nodes.get(ancestorPath);
|
|
125
|
+
if (!ancestor)
|
|
126
|
+
continue;
|
|
127
|
+
// Count how many direct children are checked
|
|
128
|
+
const checkedChildrenCount = ancestor.childPaths.filter(childPath => newCheckedPaths.has(childPath)).length;
|
|
129
|
+
const totalChildren = ancestor.childPaths.length;
|
|
130
|
+
log('👨👧👦 Checking ancestor children', {
|
|
131
|
+
ancestorPath,
|
|
132
|
+
checkedChildrenCount,
|
|
133
|
+
totalChildren
|
|
134
|
+
});
|
|
135
|
+
// Update ancestor based on children states:
|
|
136
|
+
// - All children checked → check parent
|
|
137
|
+
// - No children checked → uncheck parent
|
|
138
|
+
// - Some children checked → uncheck parent (will show indeterminate)
|
|
139
|
+
if (checkedChildrenCount === totalChildren) {
|
|
140
|
+
newCheckedPaths.add(ancestorPath);
|
|
141
|
+
log('✅ All children checked, checking parent', { ancestorPath });
|
|
120
142
|
}
|
|
121
|
-
else
|
|
122
|
-
newCheckedPaths.delete(
|
|
143
|
+
else {
|
|
144
|
+
newCheckedPaths.delete(ancestorPath);
|
|
145
|
+
if (checkedChildrenCount > 0) {
|
|
146
|
+
log('➖ Some children checked, parent will be indeterminate', { ancestorPath });
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
log('❌ No children checked, unchecking parent', { ancestorPath });
|
|
150
|
+
}
|
|
123
151
|
}
|
|
124
|
-
// If some checked, parent state depends on whether we're checking or unchecking
|
|
125
|
-
// For better UX: leave parent as-is (will show indeterminate)
|
|
126
|
-
currentPath = parentPath;
|
|
127
152
|
}
|
|
128
|
-
// Recalculate indeterminate states
|
|
153
|
+
// STEP 4: Recalculate indeterminate states
|
|
129
154
|
const newIndeterminatePaths = calculateIndeterminateStates(state.nodes, newCheckedPaths);
|
|
130
155
|
log('✅ Toggle complete', {
|
|
131
156
|
path,
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// This approach keeps the main index clean and allows for easy expansion
|
|
3
3
|
// Core components (Desktop orchestration + Charts + TreeView)
|
|
4
4
|
export * from './core/index.js';
|
|
5
|
-
// Map components (Mapbox cellular visualization)
|
|
6
|
-
export * from './map/index.js';
|
|
5
|
+
// Map components (Mapbox cellular visualization - v2)
|
|
6
|
+
export * from './map-v2/index.js';
|
|
7
7
|
// Complete applications
|
|
8
8
|
export * from './apps/index.js';
|