@smartnet360/svelte-components 0.0.22 → 0.0.23
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/utils/msi-parser.js +18 -1
- package/dist/cellular/CellularChartsView.svelte +293 -0
- package/dist/cellular/CellularChartsView.svelte.d.ts +7 -0
- package/dist/cellular/HierarchicalTree.svelte +469 -0
- package/dist/cellular/HierarchicalTree.svelte.d.ts +9 -0
- package/dist/cellular/SiteTree.svelte +286 -0
- package/dist/cellular/SiteTree.svelte.d.ts +11 -0
- package/dist/cellular/cellular-transforms.d.ts +25 -0
- package/dist/cellular/cellular-transforms.js +129 -0
- package/dist/cellular/cellular.model.d.ts +63 -0
- package/dist/cellular/cellular.model.js +6 -0
- package/dist/cellular/index.d.ts +11 -0
- package/dist/cellular/index.js +11 -0
- package/dist/cellular/mock-cellular-data.d.ts +13 -0
- package/dist/cellular/mock-cellular-data.js +241 -0
- package/dist/core/TreeChartView/TreeChartView.svelte +208 -0
- package/dist/core/TreeChartView/TreeChartView.svelte.d.ts +42 -0
- package/dist/core/TreeChartView/index.d.ts +7 -0
- package/dist/core/TreeChartView/index.js +7 -0
- package/dist/core/TreeView/TreeNode.svelte +173 -0
- package/dist/core/TreeView/TreeNode.svelte.d.ts +10 -0
- package/dist/core/TreeView/TreeView.svelte +163 -0
- package/dist/core/TreeView/TreeView.svelte.d.ts +10 -0
- package/dist/core/TreeView/index.d.ts +48 -0
- package/dist/core/TreeView/index.js +50 -0
- package/dist/core/TreeView/tree-utils.d.ts +56 -0
- package/dist/core/TreeView/tree-utils.js +194 -0
- package/dist/core/TreeView/tree.model.d.ts +104 -0
- package/dist/core/TreeView/tree.model.js +5 -0
- package/dist/core/TreeView/tree.store.d.ts +10 -0
- package/dist/core/TreeView/tree.store.js +225 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/package.json +1 -1
@@ -0,0 +1,163 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import type { TreeStoreValue } from './tree.model';
|
3
|
+
import TreeNode from './TreeNode.svelte';
|
4
|
+
|
5
|
+
interface Props {
|
6
|
+
store: TreeStoreValue;
|
7
|
+
showIndeterminate?: boolean;
|
8
|
+
showControls?: boolean;
|
9
|
+
height?: string;
|
10
|
+
}
|
11
|
+
|
12
|
+
let {
|
13
|
+
store,
|
14
|
+
showIndeterminate = true,
|
15
|
+
showControls = true,
|
16
|
+
height = '100%'
|
17
|
+
}: Props = $props();
|
18
|
+
|
19
|
+
// Get root nodes
|
20
|
+
let rootNodes = $derived(
|
21
|
+
store.state.rootPaths
|
22
|
+
.map(path => store.state.nodes.get(path))
|
23
|
+
.filter(node => node !== undefined)
|
24
|
+
);
|
25
|
+
|
26
|
+
// Stats
|
27
|
+
let totalNodes = $derived(store.state.nodes.size);
|
28
|
+
let checkedCount = $derived(store.state.checkedPaths.size);
|
29
|
+
let allExpanded = $derived(
|
30
|
+
Array.from(store.state.nodes.values()).every(
|
31
|
+
node => node.childPaths.length === 0 || store.state.expandedPaths.has(node.path)
|
32
|
+
)
|
33
|
+
);
|
34
|
+
|
35
|
+
function handleExpandAll() {
|
36
|
+
store.expandAll();
|
37
|
+
}
|
38
|
+
|
39
|
+
function handleCollapseAll() {
|
40
|
+
store.collapseAll();
|
41
|
+
}
|
42
|
+
|
43
|
+
function handleCheckAll() {
|
44
|
+
store.checkAll();
|
45
|
+
}
|
46
|
+
|
47
|
+
function handleUncheckAll() {
|
48
|
+
store.uncheckAll();
|
49
|
+
}
|
50
|
+
</script>
|
51
|
+
|
52
|
+
<div class="tree-view" style:height>
|
53
|
+
{#if showControls}
|
54
|
+
<div class="tree-controls">
|
55
|
+
<div class="btn-group btn-group-sm" role="group">
|
56
|
+
<button type="button" class="btn btn-outline-secondary" onclick={handleExpandAll}>
|
57
|
+
<i class="bi bi-arrows-expand"></i>
|
58
|
+
Expand All
|
59
|
+
</button>
|
60
|
+
<button type="button" class="btn btn-outline-secondary" onclick={handleCollapseAll}>
|
61
|
+
<i class="bi bi-arrows-collapse"></i>
|
62
|
+
Collapse All
|
63
|
+
</button>
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<div class="btn-group btn-group-sm ms-2" role="group">
|
67
|
+
<button type="button" class="btn btn-outline-primary" onclick={handleCheckAll}>
|
68
|
+
<i class="bi bi-check-square"></i>
|
69
|
+
Check All
|
70
|
+
</button>
|
71
|
+
<button type="button" class="btn btn-outline-primary" onclick={handleUncheckAll}>
|
72
|
+
<i class="bi bi-square"></i>
|
73
|
+
Uncheck All
|
74
|
+
</button>
|
75
|
+
</div>
|
76
|
+
|
77
|
+
<div class="tree-stats ms-auto">
|
78
|
+
<span class="badge bg-primary">{checkedCount} / {totalNodes} selected</span>
|
79
|
+
</div>
|
80
|
+
</div>
|
81
|
+
{/if}
|
82
|
+
|
83
|
+
<div class="tree-content">
|
84
|
+
<div class="tree-help-text">
|
85
|
+
<small class="text-muted">
|
86
|
+
<i class="bi bi-info-circle"></i>
|
87
|
+
Click checkboxes to select/deselect. Click arrows to expand/collapse.
|
88
|
+
</small>
|
89
|
+
</div>
|
90
|
+
|
91
|
+
<div class="tree-nodes">
|
92
|
+
{#each rootNodes as rootNode (rootNode.path)}
|
93
|
+
<TreeNode nodeState={rootNode} {store} {showIndeterminate} />
|
94
|
+
{/each}
|
95
|
+
</div>
|
96
|
+
</div>
|
97
|
+
</div>
|
98
|
+
|
99
|
+
<style>
|
100
|
+
.tree-view {
|
101
|
+
display: flex;
|
102
|
+
flex-direction: column;
|
103
|
+
background-color: #fff;
|
104
|
+
border: 1px solid #dee2e6;
|
105
|
+
border-radius: 0.375rem;
|
106
|
+
overflow: hidden;
|
107
|
+
}
|
108
|
+
|
109
|
+
.tree-controls {
|
110
|
+
display: flex;
|
111
|
+
align-items: center;
|
112
|
+
gap: 0.5rem;
|
113
|
+
padding: 0.75rem;
|
114
|
+
border-bottom: 1px solid #dee2e6;
|
115
|
+
background-color: #f8f9fa;
|
116
|
+
flex-shrink: 0;
|
117
|
+
}
|
118
|
+
|
119
|
+
.tree-stats {
|
120
|
+
display: flex;
|
121
|
+
align-items: center;
|
122
|
+
}
|
123
|
+
|
124
|
+
.tree-content {
|
125
|
+
flex: 1;
|
126
|
+
overflow-y: auto;
|
127
|
+
overflow-x: hidden;
|
128
|
+
}
|
129
|
+
|
130
|
+
.tree-help-text {
|
131
|
+
padding: 0.75rem 1rem;
|
132
|
+
border-bottom: 1px solid #e9ecef;
|
133
|
+
background-color: #f8f9fa;
|
134
|
+
}
|
135
|
+
|
136
|
+
.tree-help-text small {
|
137
|
+
display: flex;
|
138
|
+
align-items: center;
|
139
|
+
gap: 0.5rem;
|
140
|
+
}
|
141
|
+
|
142
|
+
.tree-nodes {
|
143
|
+
padding: 0.5rem;
|
144
|
+
}
|
145
|
+
|
146
|
+
/* Scrollbar styling */
|
147
|
+
.tree-content::-webkit-scrollbar {
|
148
|
+
width: 8px;
|
149
|
+
}
|
150
|
+
|
151
|
+
.tree-content::-webkit-scrollbar-track {
|
152
|
+
background: #f1f1f1;
|
153
|
+
}
|
154
|
+
|
155
|
+
.tree-content::-webkit-scrollbar-thumb {
|
156
|
+
background: #888;
|
157
|
+
border-radius: 4px;
|
158
|
+
}
|
159
|
+
|
160
|
+
.tree-content::-webkit-scrollbar-thumb:hover {
|
161
|
+
background: #555;
|
162
|
+
}
|
163
|
+
</style>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import type { TreeStoreValue } from './tree.model';
|
2
|
+
interface Props {
|
3
|
+
store: TreeStoreValue;
|
4
|
+
showIndeterminate?: boolean;
|
5
|
+
showControls?: boolean;
|
6
|
+
height?: string;
|
7
|
+
}
|
8
|
+
declare const TreeView: import("svelte").Component<Props, {}, "">;
|
9
|
+
type TreeView = ReturnType<typeof TreeView>;
|
10
|
+
export default TreeView;
|
@@ -0,0 +1,48 @@
|
|
1
|
+
/**
|
2
|
+
* TreeView - Generic hierarchical tree component
|
3
|
+
*
|
4
|
+
* Features:
|
5
|
+
* - JSON-driven tree structure
|
6
|
+
* - Flattened state management (O(1) lookups)
|
7
|
+
* - Cascading checkbox selection
|
8
|
+
* - Indeterminate states for partial selection
|
9
|
+
* - Expand/collapse functionality
|
10
|
+
* - LocalStorage persistence
|
11
|
+
* - Svelte store for reactive state
|
12
|
+
* - Bootstrap styling
|
13
|
+
*
|
14
|
+
* @example
|
15
|
+
* ```svelte
|
16
|
+
* <script>
|
17
|
+
* import { TreeView, createTreeStore } from './';
|
18
|
+
*
|
19
|
+
* const config = {
|
20
|
+
* nodes: [
|
21
|
+
* {
|
22
|
+
* id: 'site-a',
|
23
|
+
* label: 'Site A',
|
24
|
+
* icon: '📡',
|
25
|
+
* children: [
|
26
|
+
* { id: 'sector-1', label: 'Sector 1', children: [...] }
|
27
|
+
* ]
|
28
|
+
* }
|
29
|
+
* ],
|
30
|
+
* namespace: 'my-app',
|
31
|
+
* persistState: true
|
32
|
+
* };
|
33
|
+
*
|
34
|
+
* const treeStore = createTreeStore(config);
|
35
|
+
*
|
36
|
+
* $effect(() => {
|
37
|
+
* console.log('Checked paths:', $treeStore.state.checkedPaths);
|
38
|
+
* });
|
39
|
+
* </script>
|
40
|
+
*
|
41
|
+
* <TreeView store={treeStore} />
|
42
|
+
* ```
|
43
|
+
*/
|
44
|
+
export { default as TreeView } from './TreeView.svelte';
|
45
|
+
export { default as TreeNodeComponent } from './TreeNode.svelte';
|
46
|
+
export { createTreeStore } from './tree.store';
|
47
|
+
export type { TreeNode, TreeConfig, TreeState, NodeState, TreeStoreValue } from './tree.model';
|
48
|
+
export { getParentPath, getAncestorPaths, isAncestor, getPathLevel, flattenTree, calculateIndeterminateStates, getDescendantPaths, buildInitialState } from './tree-utils';
|
@@ -0,0 +1,50 @@
|
|
1
|
+
/**
|
2
|
+
* TreeView - Generic hierarchical tree component
|
3
|
+
*
|
4
|
+
* Features:
|
5
|
+
* - JSON-driven tree structure
|
6
|
+
* - Flattened state management (O(1) lookups)
|
7
|
+
* - Cascading checkbox selection
|
8
|
+
* - Indeterminate states for partial selection
|
9
|
+
* - Expand/collapse functionality
|
10
|
+
* - LocalStorage persistence
|
11
|
+
* - Svelte store for reactive state
|
12
|
+
* - Bootstrap styling
|
13
|
+
*
|
14
|
+
* @example
|
15
|
+
* ```svelte
|
16
|
+
* <script>
|
17
|
+
* import { TreeView, createTreeStore } from './';
|
18
|
+
*
|
19
|
+
* const config = {
|
20
|
+
* nodes: [
|
21
|
+
* {
|
22
|
+
* id: 'site-a',
|
23
|
+
* label: 'Site A',
|
24
|
+
* icon: '📡',
|
25
|
+
* children: [
|
26
|
+
* { id: 'sector-1', label: 'Sector 1', children: [...] }
|
27
|
+
* ]
|
28
|
+
* }
|
29
|
+
* ],
|
30
|
+
* namespace: 'my-app',
|
31
|
+
* persistState: true
|
32
|
+
* };
|
33
|
+
*
|
34
|
+
* const treeStore = createTreeStore(config);
|
35
|
+
*
|
36
|
+
* $effect(() => {
|
37
|
+
* console.log('Checked paths:', $treeStore.state.checkedPaths);
|
38
|
+
* });
|
39
|
+
* </script>
|
40
|
+
*
|
41
|
+
* <TreeView store={treeStore} />
|
42
|
+
* ```
|
43
|
+
*/
|
44
|
+
// Components
|
45
|
+
export { default as TreeView } from './TreeView.svelte';
|
46
|
+
export { default as TreeNodeComponent } from './TreeNode.svelte';
|
47
|
+
// Store
|
48
|
+
export { createTreeStore } from './tree.store';
|
49
|
+
// Utilities (for advanced usage)
|
50
|
+
export { getParentPath, getAncestorPaths, isAncestor, getPathLevel, flattenTree, calculateIndeterminateStates, getDescendantPaths, buildInitialState } from './tree-utils';
|
@@ -0,0 +1,56 @@
|
|
1
|
+
/**
|
2
|
+
* Tree utility functions
|
3
|
+
* Helper functions for path manipulation, tree flattening, and state management
|
4
|
+
*/
|
5
|
+
import type { TreeNode, NodeState, TreeState, TreeConfig } from './tree.model';
|
6
|
+
/**
|
7
|
+
* Get parent path from a node path
|
8
|
+
* @example getParentPath("site-a:sector-1:700", ":") => "site-a:sector-1"
|
9
|
+
*/
|
10
|
+
export declare function getParentPath(path: string, separator?: string): string;
|
11
|
+
/**
|
12
|
+
* Get all ancestor paths
|
13
|
+
* @example getAncestorPaths("a:b:c") => ["a", "a:b"]
|
14
|
+
*/
|
15
|
+
export declare function getAncestorPaths(path: string, separator?: string): string[];
|
16
|
+
/**
|
17
|
+
* Check if one path is ancestor of another
|
18
|
+
*/
|
19
|
+
export declare function isAncestor(ancestorPath: string, descendantPath: string, separator?: string): boolean;
|
20
|
+
/**
|
21
|
+
* Get depth level of a path
|
22
|
+
* @example getPathLevel("a:b:c") => 2
|
23
|
+
*/
|
24
|
+
export declare function getPathLevel(path: string, separator?: string): number;
|
25
|
+
/**
|
26
|
+
* Flatten tree structure into map of NodeState
|
27
|
+
*/
|
28
|
+
export declare function flattenTree<T = any>(nodes: TreeNode<T>[], config: TreeConfig<T>, parentPath?: string, level?: number): Map<string, NodeState>;
|
29
|
+
/**
|
30
|
+
* Calculate which nodes should be indeterminate
|
31
|
+
*/
|
32
|
+
export declare function calculateIndeterminateStates(nodes: Map<string, NodeState>, checkedPaths: Set<string>): Set<string>;
|
33
|
+
/**
|
34
|
+
* Get all descendant paths of a node
|
35
|
+
*/
|
36
|
+
export declare function getDescendantPaths(path: string, nodes: Map<string, NodeState>, separator?: string): string[];
|
37
|
+
/**
|
38
|
+
* Build initial TreeState from flattened nodes
|
39
|
+
*/
|
40
|
+
export declare function buildInitialState(nodes: Map<string, NodeState>, config: TreeConfig): TreeState;
|
41
|
+
/**
|
42
|
+
* LocalStorage key builder
|
43
|
+
*/
|
44
|
+
export declare function getStorageKey(namespace: string, key: string): string;
|
45
|
+
/**
|
46
|
+
* Save state to localStorage
|
47
|
+
*/
|
48
|
+
export declare function saveStateToStorage(namespace: string, state: TreeState): void;
|
49
|
+
/**
|
50
|
+
* Load state from localStorage
|
51
|
+
*/
|
52
|
+
export declare function loadStateFromStorage(namespace: string, state: TreeState): Partial<TreeState>;
|
53
|
+
/**
|
54
|
+
* Clear localStorage for a namespace
|
55
|
+
*/
|
56
|
+
export declare function clearStorageForNamespace(namespace: string): void;
|
@@ -0,0 +1,194 @@
|
|
1
|
+
/**
|
2
|
+
* Tree utility functions
|
3
|
+
* Helper functions for path manipulation, tree flattening, and state management
|
4
|
+
*/
|
5
|
+
/**
|
6
|
+
* Get parent path from a node path
|
7
|
+
* @example getParentPath("site-a:sector-1:700", ":") => "site-a:sector-1"
|
8
|
+
*/
|
9
|
+
export function getParentPath(path, separator = ':') {
|
10
|
+
const lastIndex = path.lastIndexOf(separator);
|
11
|
+
return lastIndex === -1 ? '' : path.substring(0, lastIndex);
|
12
|
+
}
|
13
|
+
/**
|
14
|
+
* Get all ancestor paths
|
15
|
+
* @example getAncestorPaths("a:b:c") => ["a", "a:b"]
|
16
|
+
*/
|
17
|
+
export function getAncestorPaths(path, separator = ':') {
|
18
|
+
const ancestors = [];
|
19
|
+
let current = path;
|
20
|
+
while (true) {
|
21
|
+
const parent = getParentPath(current, separator);
|
22
|
+
if (!parent)
|
23
|
+
break;
|
24
|
+
ancestors.push(parent);
|
25
|
+
current = parent;
|
26
|
+
}
|
27
|
+
return ancestors.reverse(); // Return from root to immediate parent
|
28
|
+
}
|
29
|
+
/**
|
30
|
+
* Check if one path is ancestor of another
|
31
|
+
*/
|
32
|
+
export function isAncestor(ancestorPath, descendantPath, separator = ':') {
|
33
|
+
return descendantPath.startsWith(ancestorPath + separator);
|
34
|
+
}
|
35
|
+
/**
|
36
|
+
* Get depth level of a path
|
37
|
+
* @example getPathLevel("a:b:c") => 2
|
38
|
+
*/
|
39
|
+
export function getPathLevel(path, separator = ':') {
|
40
|
+
return path.split(separator).length - 1;
|
41
|
+
}
|
42
|
+
/**
|
43
|
+
* Flatten tree structure into map of NodeState
|
44
|
+
*/
|
45
|
+
export function flattenTree(nodes, config, parentPath = '', level = 0) {
|
46
|
+
const map = new Map();
|
47
|
+
const separator = config.pathSeparator || ':';
|
48
|
+
for (const node of nodes) {
|
49
|
+
const path = parentPath ? `${parentPath}${separator}${node.id}` : node.id;
|
50
|
+
// Collect child paths
|
51
|
+
const childPaths = [];
|
52
|
+
if (node.children && node.children.length > 0) {
|
53
|
+
for (const child of node.children) {
|
54
|
+
childPaths.push(parentPath ? `${path}${separator}${child.id}` : `${path}${separator}${child.id}`);
|
55
|
+
}
|
56
|
+
}
|
57
|
+
const nodeState = {
|
58
|
+
path,
|
59
|
+
checked: node.defaultChecked ?? true,
|
60
|
+
indeterminate: false,
|
61
|
+
expanded: node.defaultExpanded ?? (config.defaultExpandAll || false),
|
62
|
+
node,
|
63
|
+
parentPath,
|
64
|
+
childPaths,
|
65
|
+
level
|
66
|
+
};
|
67
|
+
map.set(path, nodeState);
|
68
|
+
// Recursively flatten children
|
69
|
+
if (node.children && node.children.length > 0) {
|
70
|
+
const childMap = flattenTree(node.children, config, path, level + 1);
|
71
|
+
childMap.forEach((value, key) => map.set(key, value));
|
72
|
+
}
|
73
|
+
}
|
74
|
+
return map;
|
75
|
+
}
|
76
|
+
/**
|
77
|
+
* Calculate which nodes should be indeterminate
|
78
|
+
*/
|
79
|
+
export function calculateIndeterminateStates(nodes, checkedPaths) {
|
80
|
+
const indeterminate = new Set();
|
81
|
+
// For each node with children
|
82
|
+
nodes.forEach((nodeState, path) => {
|
83
|
+
if (nodeState.childPaths.length === 0)
|
84
|
+
return; // Leaf nodes can't be indeterminate
|
85
|
+
const checkedChildren = nodeState.childPaths.filter(childPath => checkedPaths.has(childPath));
|
86
|
+
const allChecked = checkedChildren.length === nodeState.childPaths.length;
|
87
|
+
const noneChecked = checkedChildren.length === 0;
|
88
|
+
// Indeterminate if some (but not all) children are checked
|
89
|
+
if (!allChecked && !noneChecked) {
|
90
|
+
indeterminate.add(path);
|
91
|
+
}
|
92
|
+
});
|
93
|
+
return indeterminate;
|
94
|
+
}
|
95
|
+
/**
|
96
|
+
* Get all descendant paths of a node
|
97
|
+
*/
|
98
|
+
export function getDescendantPaths(path, nodes, separator = ':') {
|
99
|
+
const descendants = [];
|
100
|
+
nodes.forEach((nodeState, nodePath) => {
|
101
|
+
if (nodePath !== path && nodePath.startsWith(path + separator)) {
|
102
|
+
descendants.push(nodePath);
|
103
|
+
}
|
104
|
+
});
|
105
|
+
return descendants;
|
106
|
+
}
|
107
|
+
/**
|
108
|
+
* Build initial TreeState from flattened nodes
|
109
|
+
*/
|
110
|
+
export function buildInitialState(nodes, config) {
|
111
|
+
const checkedPaths = new Set();
|
112
|
+
const expandedPaths = new Set();
|
113
|
+
const rootPaths = [];
|
114
|
+
nodes.forEach((nodeState, path) => {
|
115
|
+
if (nodeState.checked) {
|
116
|
+
checkedPaths.add(path);
|
117
|
+
}
|
118
|
+
if (nodeState.expanded) {
|
119
|
+
expandedPaths.add(path);
|
120
|
+
}
|
121
|
+
if (nodeState.level === 0) {
|
122
|
+
rootPaths.push(path);
|
123
|
+
}
|
124
|
+
});
|
125
|
+
const indeterminatePaths = calculateIndeterminateStates(nodes, checkedPaths);
|
126
|
+
return {
|
127
|
+
nodes,
|
128
|
+
checkedPaths,
|
129
|
+
expandedPaths,
|
130
|
+
indeterminatePaths,
|
131
|
+
rootPaths
|
132
|
+
};
|
133
|
+
}
|
134
|
+
/**
|
135
|
+
* LocalStorage key builder
|
136
|
+
*/
|
137
|
+
export function getStorageKey(namespace, key) {
|
138
|
+
return `${namespace}:${key}`;
|
139
|
+
}
|
140
|
+
/**
|
141
|
+
* Save state to localStorage
|
142
|
+
*/
|
143
|
+
export function saveStateToStorage(namespace, state) {
|
144
|
+
if (!namespace)
|
145
|
+
return;
|
146
|
+
try {
|
147
|
+
localStorage.setItem(getStorageKey(namespace, 'checked'), JSON.stringify(Array.from(state.checkedPaths)));
|
148
|
+
localStorage.setItem(getStorageKey(namespace, 'expanded'), JSON.stringify(Array.from(state.expandedPaths)));
|
149
|
+
}
|
150
|
+
catch (error) {
|
151
|
+
console.warn('Failed to save tree state to localStorage:', error);
|
152
|
+
}
|
153
|
+
}
|
154
|
+
/**
|
155
|
+
* Load state from localStorage
|
156
|
+
*/
|
157
|
+
export function loadStateFromStorage(namespace, state) {
|
158
|
+
if (!namespace)
|
159
|
+
return {};
|
160
|
+
try {
|
161
|
+
const checkedJson = localStorage.getItem(getStorageKey(namespace, 'checked'));
|
162
|
+
const expandedJson = localStorage.getItem(getStorageKey(namespace, 'expanded'));
|
163
|
+
const updates = {};
|
164
|
+
if (checkedJson) {
|
165
|
+
const checkedArray = JSON.parse(checkedJson);
|
166
|
+
updates.checkedPaths = new Set(checkedArray);
|
167
|
+
// Recalculate indeterminate states
|
168
|
+
updates.indeterminatePaths = calculateIndeterminateStates(state.nodes, updates.checkedPaths);
|
169
|
+
}
|
170
|
+
if (expandedJson) {
|
171
|
+
const expandedArray = JSON.parse(expandedJson);
|
172
|
+
updates.expandedPaths = new Set(expandedArray);
|
173
|
+
}
|
174
|
+
return updates;
|
175
|
+
}
|
176
|
+
catch (error) {
|
177
|
+
console.warn('Failed to load tree state from localStorage:', error);
|
178
|
+
return {};
|
179
|
+
}
|
180
|
+
}
|
181
|
+
/**
|
182
|
+
* Clear localStorage for a namespace
|
183
|
+
*/
|
184
|
+
export function clearStorageForNamespace(namespace) {
|
185
|
+
if (!namespace)
|
186
|
+
return;
|
187
|
+
try {
|
188
|
+
localStorage.removeItem(getStorageKey(namespace, 'checked'));
|
189
|
+
localStorage.removeItem(getStorageKey(namespace, 'expanded'));
|
190
|
+
}
|
191
|
+
catch (error) {
|
192
|
+
console.warn('Failed to clear tree state from localStorage:', error);
|
193
|
+
}
|
194
|
+
}
|
@@ -0,0 +1,104 @@
|
|
1
|
+
/**
|
2
|
+
* Generic TreeView Model
|
3
|
+
* Supports hierarchical tree structures with checkboxes, expand/collapse, and state persistence
|
4
|
+
*/
|
5
|
+
/**
|
6
|
+
* Individual tree node configuration
|
7
|
+
* @template T - Type of custom metadata
|
8
|
+
*/
|
9
|
+
export interface TreeNode<T = any> {
|
10
|
+
/** Unique identifier for this node */
|
11
|
+
id: string;
|
12
|
+
/** Display label */
|
13
|
+
label: string;
|
14
|
+
/** Optional child nodes */
|
15
|
+
children?: TreeNode<T>[];
|
16
|
+
/** Custom metadata (colors, icons, data, etc.) */
|
17
|
+
metadata?: T;
|
18
|
+
/** Initial checked state (default: true) */
|
19
|
+
defaultChecked?: boolean;
|
20
|
+
/** Initial expanded state (default: false) */
|
21
|
+
defaultExpanded?: boolean;
|
22
|
+
/** Optional icon class or emoji */
|
23
|
+
icon?: string;
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* State of a single node
|
27
|
+
*/
|
28
|
+
export interface NodeState {
|
29
|
+
/** Full path to this node (e.g., "site-a:sector-1:700") */
|
30
|
+
path: string;
|
31
|
+
/** Is this node checked? */
|
32
|
+
checked: boolean;
|
33
|
+
/** Is this node in indeterminate state? (some children checked) */
|
34
|
+
indeterminate: boolean;
|
35
|
+
/** Is this node expanded? (showing children) */
|
36
|
+
expanded: boolean;
|
37
|
+
/** Reference to original node config */
|
38
|
+
node: TreeNode;
|
39
|
+
/** Parent path (empty string for root nodes) */
|
40
|
+
parentPath: string;
|
41
|
+
/** Child paths */
|
42
|
+
childPaths: string[];
|
43
|
+
/** Depth level (0 = root) */
|
44
|
+
level: number;
|
45
|
+
}
|
46
|
+
/**
|
47
|
+
* Complete tree state
|
48
|
+
*/
|
49
|
+
export interface TreeState {
|
50
|
+
/** Flattened map of all nodes by path */
|
51
|
+
nodes: Map<string, NodeState>;
|
52
|
+
/** Set of checked node paths */
|
53
|
+
checkedPaths: Set<string>;
|
54
|
+
/** Set of expanded node paths */
|
55
|
+
expandedPaths: Set<string>;
|
56
|
+
/** Set of indeterminate node paths */
|
57
|
+
indeterminatePaths: Set<string>;
|
58
|
+
/** Root node paths */
|
59
|
+
rootPaths: string[];
|
60
|
+
}
|
61
|
+
/**
|
62
|
+
* TreeView configuration
|
63
|
+
*/
|
64
|
+
export interface TreeConfig<T = any> {
|
65
|
+
/** Root nodes */
|
66
|
+
nodes: TreeNode<T>[];
|
67
|
+
/** Expand all nodes by default */
|
68
|
+
defaultExpandAll?: boolean;
|
69
|
+
/** Path separator (default: ":") */
|
70
|
+
pathSeparator?: string;
|
71
|
+
/** LocalStorage namespace for persistence */
|
72
|
+
namespace?: string;
|
73
|
+
/** Enable localStorage persistence */
|
74
|
+
persistState?: boolean;
|
75
|
+
/** Show indeterminate checkbox states */
|
76
|
+
showIndeterminate?: boolean;
|
77
|
+
}
|
78
|
+
/**
|
79
|
+
* Store value exposed to consumers
|
80
|
+
*/
|
81
|
+
export interface TreeStoreValue {
|
82
|
+
/** Current tree state */
|
83
|
+
state: TreeState;
|
84
|
+
/** Config used */
|
85
|
+
config: TreeConfig;
|
86
|
+
/** Toggle a node's checked state */
|
87
|
+
toggle: (path: string) => void;
|
88
|
+
/** Expand/collapse a node */
|
89
|
+
toggleExpand: (path: string) => void;
|
90
|
+
/** Expand all nodes */
|
91
|
+
expandAll: () => void;
|
92
|
+
/** Collapse all nodes */
|
93
|
+
collapseAll: () => void;
|
94
|
+
/** Check all nodes */
|
95
|
+
checkAll: () => void;
|
96
|
+
/** Uncheck all nodes */
|
97
|
+
uncheckAll: () => void;
|
98
|
+
/** Get all checked paths */
|
99
|
+
getCheckedPaths: () => string[];
|
100
|
+
/** Get all checked leaf paths (nodes without children) */
|
101
|
+
getCheckedLeafPaths: () => string[];
|
102
|
+
/** Clear localStorage */
|
103
|
+
clearStorage: () => void;
|
104
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/**
|
2
|
+
* Tree Store
|
3
|
+
* Svelte writable store for managing tree state with persistence
|
4
|
+
*/
|
5
|
+
import { type Writable } from 'svelte/store';
|
6
|
+
import type { TreeConfig, TreeStoreValue } from './tree.model';
|
7
|
+
/**
|
8
|
+
* Create a tree store with state management and persistence
|
9
|
+
*/
|
10
|
+
export declare function createTreeStore<T = any>(config: TreeConfig<T>): Writable<TreeStoreValue>;
|