Re-work toHierarchy utility

Refactor `toHiearchy` to be more generic, performant

- Use callback to "pick" generic parent ID property instead of requiring that `pid` be hardcoded`
- Account for edge cases of an invalid parent ID
- Use Map to store node children for lookups
- Use one pass instead of removing empty nodes at the very end
- DX: use generics to type `toHierarchy`
- Make `toHierarchy` even more generic (reusable with `RecursiveList`)

Co-Authored-By: Bram <bramhavers@gmail.com>
This commit is contained in:
Eric Liu 2024-12-07 13:54:06 -08:00
commit 5f1e8de1e1
29 changed files with 414 additions and 273 deletions

View file

@ -24,7 +24,7 @@
}
},
"..": {
"version": "0.86.2",
"version": "0.86.1",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",

View file

@ -17770,7 +17770,7 @@
{
"name": "nodes",
"kind": "let",
"description": "Provide a nested array of nodes to render",
"description": "Provide an array of nodes to render",
"type": "Array<TreeNode>",
"value": "[]",
"isFunction": false,
@ -17875,18 +17875,6 @@
"constant": false,
"reactive": false
},
{
"name": "toHierarchy",
"kind": "function",
"description": "Create a nested array from a flat array",
"type": "(flatArray: TreeNode[] & { pid?: any }[]) => TreeNode[]",
"value": "() => {\n return th(flatArray);\n}",
"isFunction": true,
"isFunctionDeclaration": true,
"isRequired": false,
"constant": false,
"reactive": false
},
{
"name": "expandNodes",
"kind": "function",

View file

@ -37,4 +37,11 @@ Set `type` to `"ordered"` to use the ordered list variant.
Set `type` to `"ordered-native"` to use the native styles for an ordered list.
<FileSource src="/framed/RecursiveList/RecursiveListOrderedNative" />
<FileSource src="/framed/RecursiveList/RecursiveListOrderedNative" />
## Flat data structure
If working with a flat data structure, use the `toHierarchy` utility
to convert a flat data structure into a hierarchical array accepted by the `nodes` prop.
<FileSource src="/framed/RecursiveList/RecursiveListFlatArray" />

View file

@ -64,7 +64,6 @@ Expanded nodes can be set using `expandedIds`.
<FileSource src="/framed/TreeView/TreeViewExpanded" />
## Initial multiple selected nodes
Initial multiple selected nodes can be set using `selectedIds`.
@ -111,10 +110,7 @@ If a matching node is found, it will be expanded, selected, and focused.
## Flat data structure
Use the `toHierarchy` method to provide a flat data structure to the `nodes` property.
If working with a flat data structure, use the `toHierarchy` utility
to convert a flat data structure into a hierarchical array accepted by the `nodes` prop.
This method will transform a flat array of objects into the hierarchical array as expected by `nodes`.
The child objects in the flat array need to have a `pid` property to reference its parent.
When `pid` is not provided the object is assumed to be at the root of the tree.
<FileSource src="/framed/TreeView/TreeViewFlatArray" />
<FileSource src="/framed/TreeView/TreeViewFlatArray" />

View file

@ -0,0 +1,20 @@
<script>
import { RecursiveList, toHierarchy } from "carbon-components-svelte";
const nodesFlat = [
{ id: 1, text: "Item 1" },
{ id: 2, text: "Item 1a", pid: 1 },
{ id: 3, html: "<h5>HTML content</h5>", pid: 2 },
{ id: 4, text: "Item 2" },
{ id: 5, href: "https://svelte.dev/", pid: 4 },
{
id: 6,
href: "https://svelte.dev/",
text: "Link with custom text",
pid: 4,
},
{ id: 7, text: "Item 3" },
];
</script>
<RecursiveList nodes={toHierarchy(nodesFlat, (node) => node.pid)} />

View file

@ -1,69 +1,28 @@
<script>
import { TreeView, toHierarchy } from "carbon-components-svelte";
import WatsonMachineLearning from "carbon-icons-svelte/lib/WatsonMachineLearning.svelte";
import Analytics from "carbon-icons-svelte/lib/Analytics.svelte";
import Blockchain from "carbon-icons-svelte/lib/Blockchain.svelte";
import DataBase from "carbon-icons-svelte/lib/DataBase.svelte";
import SignalStrength from "carbon-icons-svelte/lib/SignalStrength.svelte";
let activeId = "";
let selectedIds = [];
let nodesFlat = [
{ id: 0, text: "AI / Machine learning", icon: WatsonMachineLearning },
{ id: 1, text: "Analytics", icon: Analytics },
{ id: 2, text: "IBM Analytics Engine", pid: 1, icon: Analytics },
{ id: 3, text: "Apache Spark", pid: 2, icon: Analytics },
{ id: 4, text: "Hadoop", icon: Analytics, pid: 2 },
{ id: 5, text: "IBM Cloud SQL Query", icon: Analytics, pid: 1 },
{ id: 6, text: "IBM Db2 Warehouse on Cloud", icon: Analytics, pid: 1 },
{ id: 7, text: "Blockchain", icon: Blockchain },
{ id: 8, text: "IBM Blockchain Platform", icon: Blockchain, pid: 7 },
{ id: 9, text: "Databases", icon: DataBase },
{
id: 10,
text: "IBM Cloud Databases for Elasticsearch",
icon: DataBase,
pid: 9,
},
{
id: 11,
text: "IBM Cloud Databases for Enterprise DB",
icon: DataBase,
pid: 9,
},
{ id: 12, text: "IBM Cloud Databases for MongoDB", icon: DataBase, pid: 9 },
{
id: 13,
text: "IBM Cloud Databases for PostgreSQL",
icon: DataBase,
pid: 9,
},
{ id: 14, text: "Integration", icon: SignalStrength, disabled: true },
{
id: 15,
text: "IBM API Connect",
icon: SignalStrength,
disabled: true,
pid: 14,
},
{ id: 0, text: "AI / Machine learning", icon: Analytics },
{ id: 1, text: "Analytics" },
{ id: 2, text: "IBM Analytics Engine", pid: 1 },
{ id: 3, text: "Apache Spark", pid: 2 },
{ id: 4, text: "Hadoop", pid: 2 },
{ id: 5, text: "IBM Cloud SQL Query", pid: 1 },
{ id: 6, text: "IBM Db2 Warehouse on Cloud", pid: 1 },
{ id: 7, text: "Blockchain" },
{ id: 8, text: "IBM Blockchain Platform", pid: 7 },
{ id: 9, text: "Databases" },
{ id: 10, text: "IBM Cloud Databases for Elasticsearch", pid: 9 },
{ id: 11, text: "IBM Cloud Databases for Enterprise DB", pid: 9 },
{ id: 12, text: "IBM Cloud Databases for MongoDB", pid: 9 },
{ id: 13, text: "IBM Cloud Databases for PostgreSQL", pid: 9 },
{ id: 14, text: "Integration", disabled: true },
{ id: 15, text: "IBM API Connect", disabled: true, pid: 14 },
];
</script>
<TreeView
labelText="Cloud Products"
nodes={toHierarchy(nodesFlat)}
bind:activeId
bind:selectedIds
on:select={({ detail }) => console.log("select", detail)}
on:toggle={({ detail }) => console.log("toggle", detail)}
on:focus={({ detail }) => console.log("focus", detail)}
nodes={toHierarchy(nodesFlat, (node) => node.pid)}
/>
<div>Active node id: {activeId}</div>
<div>Selected ids: {JSON.stringify(selectedIds)}</div>
<style>
div {
margin-top: var(--cds-spacing-05);
}
</style>