Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/src/pages/components/TreeView.svx
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,21 @@ Use `TreeView.showNode` to expand, select, and focus a specific node.
Convert flat data to a hierarchical structure using the `toHierarchy` utility.

<FileSource src="/framed/TreeView/TreeViewFlatArray" />

## Filter by text with search input

Combine a `Search` input with `filterTreeByText` to create an interactive searchable tree. Type to filter nodes by name (case-insensitive substring matching). By default, matching nodes and their ancestors are included to maintain valid tree structure.

<FileSource src="/framed/TreeView/TreeViewFilter" />

## Filter by ID

Use `filterTreeById` to filter nodes by their ID. Accepts either a single ID or an array of IDs. Click the buttons to filter by a single ID or multiple IDs. Matching nodes and their ancestors are included by default.

<FileSource src="/framed/TreeView/TreeViewFilterById" />

## Filter with custom predicate

Use `filterTreeNodes` with a predicate function to implement custom filtering logic. The example demonstrates filtering by file extension, filtering leaf nodes only, and using the `includeChildren` option to include all descendants of matching nodes. By default, matching nodes and their ancestors are included.

<FileSource src="/framed/TreeView/TreeViewFilterNodes" />
124 changes: 124 additions & 0 deletions docs/src/pages/framed/TreeView/TreeViewFilter.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<script>
import { filterTreeByText, Search, TreeView } from "carbon-components-svelte";

const allNodes = [
{
id: "1",
text: "Documents",
nodes: [
{
id: "1-1",
text: "Work",
nodes: [
{ id: "1-1-1", text: "Report.docx" },
{ id: "1-1-2", text: "Presentation.pptx" },
{ id: "1-1-3", text: "Budget.xlsx" },
{ id: "1-1-4", text: "Meeting Notes.txt" },
],
},
{
id: "1-2",
text: "Personal",
nodes: [
{ id: "1-2-1", text: "Resume.pdf" },
{ id: "1-2-2", text: "Cover Letter.pdf" },
{ id: "1-2-3", text: "Portfolio.pdf" },
],
},
{
id: "1-3",
text: "Projects",
nodes: [
{
id: "1-3-1",
text: "Website",
nodes: [
{ id: "1-3-1-1", text: "index.html" },
{ id: "1-3-1-2", text: "styles.css" },
{ id: "1-3-1-3", text: "script.js" },
],
},
{
id: "1-3-2",
text: "App",
nodes: [
{ id: "1-3-2-1", text: "main.py" },
{ id: "1-3-2-2", text: "config.json" },
],
},
],
},
],
},
{
id: "2",
text: "Pictures",
nodes: [
{ id: "2-1", text: "Vacation.jpg" },
{ id: "2-2", text: "Family.jpg" },
{ id: "2-3", text: "Birthday.png" },
],
},
{
id: "3",
text: "Music",
nodes: [
{
id: "3-1",
text: "Rock",
nodes: [
{ id: "3-1-1", text: "Song1.mp3" },
{ id: "3-1-2", text: "Song2.mp3" },
],
},
{
id: "3-2",
text: "Jazz",
nodes: [
{ id: "3-2-1", text: "Song3.mp3" },
{ id: "3-2-2", text: "Song4.mp3" },
],
},
{
id: "3-3",
text: "Classical",
nodes: [{ id: "3-3-1", text: "Symphony.mp3" }],
},
],
},
];

let searchValue = "";
let expandedIds = [];

$: filteredNodes =
searchValue.trim() === ""
? allNodes
: filterTreeByText(allNodes, searchValue);

$: if (searchValue.trim() !== "" && filteredNodes.length > 0) {
const extractIds = (nodes) => {
const ids = [];
for (const node of nodes) {
ids.push(node.id);
if (node.nodes) {
ids.push(...extractIds(node.nodes));
}
}
return ids;
};
expandedIds = extractIds(filteredNodes);
} else {
expandedIds = [];
}
</script>

<Search size="sm" placeholder="Search tree nodes..." bind:value={searchValue} />

<div style:margin-top="var(--cds-layout-01)">
{#if filteredNodes.length > 0}
<TreeView labelText="File System" nodes={filteredNodes} {expandedIds} />
{:else}
No matching nodes found for "{searchValue}"
{/if}
</div>
82 changes: 82 additions & 0 deletions docs/src/pages/framed/TreeView/TreeViewFilterById.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<script>
import {
Button,
ButtonSet,
filterTreeById,
TreeView,
} from "carbon-components-svelte";

const allNodes = [
{
id: "1",
text: "Documents",
nodes: [
{
id: "1-1",
text: "Work",
nodes: [
{ id: "1-1-1", text: "Report.docx" },
{ id: "1-1-2", text: "Presentation.pptx" },
{ id: "1-1-3", text: "Budget.xlsx" },
],
},
{
id: "1-2",
text: "Personal",
nodes: [
{ id: "1-2-1", text: "Resume.pdf" },
{ id: "1-2-2", text: "Cover Letter.pdf" },
],
},
],
},
{
id: "2",
text: "Pictures",
nodes: [
{ id: "2-1", text: "Vacation.jpg" },
{ id: "2-2", text: "Family.jpg" },
],
},
{
id: "3",
text: "Music",
nodes: [
{
id: "3-1",
text: "Rock",
nodes: [
{ id: "3-1-1", text: "Song1.mp3" },
{ id: "3-1-2", text: "Song2.mp3" },
],
},
],
},
];

let filteredNodes = allNodes;
let expandedIds = [];

function filterBySingleId() {
filteredNodes = filterTreeById(allNodes, "1-1-1");
expandedIds = ["1", "1-1", "1-1-1"];
}

function filterByMultipleIds() {
filteredNodes = filterTreeById(allNodes, ["1-1-1", "1-2-1", "2-1"]);
expandedIds = ["1", "1-1", "1-1-1", "1-2", "1-2-1", "2", "2-1"];
}

function resetFilter() {
filteredNodes = allNodes;
expandedIds = [];
}
</script>

<ButtonSet style="margin-bottom: var(--cds-layout-01)">
<Button size="sm" on:click={filterBySingleId}>Filter single ID</Button>
<Button size="sm" on:click={filterByMultipleIds}>Filter multiple IDs</Button>
<Button size="sm" kind="tertiary" on:click={resetFilter}>Reset</Button>
</ButtonSet>

<TreeView labelText="File System" nodes={filteredNodes} {expandedIds} />
114 changes: 114 additions & 0 deletions docs/src/pages/framed/TreeView/TreeViewFilterNodes.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<script>
import {
Button,
ButtonSet,
filterTreeNodes,
TreeView,
} from "carbon-components-svelte";

const allNodes = [
{
id: "1",
text: "Documents",
nodes: [
{
id: "1-1",
text: "Work",
nodes: [
{ id: "1-1-1", text: "Report.docx" },
{ id: "1-1-2", text: "Presentation.pptx" },
{ id: "1-1-3", text: "Budget.xlsx" },
],
},
{
id: "1-2",
text: "Personal",
nodes: [
{ id: "1-2-1", text: "Resume.pdf" },
{ id: "1-2-2", text: "Cover Letter.pdf" },
],
},
],
},
{
id: "2",
text: "Pictures",
nodes: [
{ id: "2-1", text: "Vacation.jpg" },
{ id: "2-2", text: "Family.jpg" },
],
},
{
id: "3",
text: "Music",
nodes: [
{
id: "3-1",
text: "Rock",
nodes: [
{ id: "3-1-1", text: "Song1.mp3" },
{ id: "3-1-2", text: "Song2.mp3" },
],
},
],
},
];

let filteredNodes = allNodes;
let expandedIds = [];

function filterByExtension() {
filteredNodes = filterTreeNodes(
allNodes,
(node) => node.text?.endsWith(".pdf") || node.text?.endsWith(".docx"),
);
expandedIds = ["1", "1-1", "1-1-1", "1-2", "1-2-1"];
}

function filterLeafNodes() {
filteredNodes = filterTreeNodes(
allNodes,
(node) => !node.nodes || node.nodes.length === 0,
);
expandedIds = [
"1",
"1-1",
"1-1-1",
"1-1-2",
"1-1-3",
"1-2",
"1-2-1",
"1-2-2",
"2",
"2-1",
"2-2",
"3",
"3-1",
"3-1-1",
"3-1-2",
];
}

function filterWithChildren() {
filteredNodes = filterTreeNodes(allNodes, (node) => node.id === "1-1", {
includeChildren: true,
});
expandedIds = ["1", "1-1", "1-1-1", "1-1-2", "1-1-3"];
}

function resetFilter() {
filteredNodes = allNodes;
expandedIds = [];
}
</script>

<ButtonSet style="margin-bottom: var(--cds-layout-01)">
<Button size="sm" on:click={filterByExtension}>
Filter by extension (.pdf, .docx)
</Button>
<Button size="sm" on:click={filterLeafNodes}>Filter leaf nodes</Button>
<Button size="sm" on:click={filterWithChildren}>Filter with children</Button>
<Button size="sm" kind="tertiary" on:click={resetFilter}>Reset</Button>
</ButtonSet>

<TreeView labelText="File System" nodes={filteredNodes} {expandedIds} />
5 changes: 5 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,9 @@ export { default as SideNavMenu } from "./UIShell/SideNavMenu.svelte";
export { default as SideNavMenuItem } from "./UIShell/SideNavMenuItem.svelte";
export { default as SkipToContent } from "./UIShell/SkipToContent.svelte";
export { default as UnorderedList } from "./UnorderedList/UnorderedList.svelte";
export {
filterTreeById,
filterTreeByText,
filterTreeNodes,
} from "./utils/filterTreeNodes";
export { toHierarchy } from "./utils/toHierarchy";
Loading