Skip to content

Instantly share code, notes, and snippets.

@b3b00
Last active January 11, 2024 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save b3b00/de955201c084dbe41b1baae861bfda00 to your computer and use it in GitHub Desktop.
Save b3b00/de955201c084dbe41b1baae861bfda00 to your computer and use it in GitHub Desktop.
svelte filterable TreeView
<script>
import TreeView from './TreeView.svelte';
import Node from './Node.svelte';
import {selectedNode} from './teststore.js'
let name = 'world';
$selectedNode = undefined
let root = {
id : 0,
name : 'root',
child : [
{
id : 1,
name : '1',
child : [
{
id : 2,
name : '1.1',
child : [
]
},
{
id : 3,
name : '1.2',
child : [
]
}
]
},
{
id : 4,
name : '2',
child : [
{
id : 5,
name : '2.1',
child : [
]
},
{
id : 6,
name : '2.2',
child : [
]
}
]
}
]
}
let accessor = (x) => {
if (x.child != undefined && x.child != null && Array.isArray(x.child)) {
return x.child;
}
return [];
}
let nodeId = (x) => x.id;
let nodefilter = (node, search) => {
if (search === undefined || search === null || search == '') {
return node;
}
var children = accessor(node);
if (children.length > 0) {
var filtered = children.map(x => nodefilter(x, search)).filter(x => x!= null);
if ( node.name.includes(search)) {
return node;
}
else if (filtered.length > 0) {
return {name:node.name,
id:node.id,
child:filtered
};
}
return null;
}
else {
if (node.name.includes(search)) {
return node;
}
return null;
}
}
$: {
console.log(selectedNode);
}
</script>
{#if $selectedNode}
<h1>clicked : id:&gt;{$selectedNode.id}&lt; - name:&gt;{$selectedNode.name}&lt;</h1>
{/if}
<TreeView selectable root={root} childrenAccessor={accessor} nodeTemplate={Node} filter={nodefilter} {nodeId}></TreeView>
import App from './App.svelte';
var app = new App({
target: document.body
});
export default app;
<script>
import {selectedNode} from './teststore.js'
export let data;
function clickNode(e) {
e.preventDefault(); // avoid deploy/collapse node when clicking node name
$selectedNode = data;
console.log(data);
}
</script>
<span style='cursor:pointer' role="link" tabindex={data.id} on:click={clickNode} on:keydown={clickNode}>id:&gt;{data.id}&lt; - name:&gt;{data.name}&lt;</span>
import { writable } from 'svelte/store';
export const selectedNode = writable({});
selectedNode.subscribe((selection) => {
});
selectedNode.update((selection) => selection);
<script>
import {onMount} from "svelte";
import {setContext} from 'svelte';
import TreeViewNode from "./TreeViewNode.svelte";
export let root = {};
export let nodeTemplate;
export let childrenAccessor;
export let filter = null;
export let nodeId;
export let selectable;
let search;
let currentRoot;
let selection = {}
let selectNode = (selectedNode, selected) => {
selection[nodeId(selectedNode)] = selected;
}
let getNodeSelection = () => selection;
let isNodeSelected = (node) => {
id = nodeId(node);
if (selection.hasOwnProperty(id)) {
return selection[id]
}
return false;
}
setContext('selectNode', selectNode);
setContext('getNodeSelection', getNodeSelection);
setContext('isNodeSelected', isNodeSelected);
$:{
console.log(selection);
search = search;
if (filter) {
currentRoot = filter(root, search);
}
}
onMount(async () => {
currentRoot = root;
})
</script>
<!-- TODO : add filter -->
<input type="text" bind:value={search}/>
<TreeViewNode {nodeId} {selectable} node={currentRoot} nodeTemplate={nodeTemplate} childAccessor={childrenAccessor}/>
<style>
.treemargin {
margin-left: 25px;
}
summary {
display: block;
}
/* Create a new custom triangle on the right side */
summary::before {
margin-left: 1ch;
display: inline-block;
content: '↘️';
transition: 0.2s;
}
details[open] > summary::before {
transform: rotate(180deg);
}
.leaf::before {
content: '🌿'
}
</style>
<script>
import {onMount} from "svelte";
import {getContext} from 'svelte';
export let node;
export let nodeTemplate;
export let childAccessor;
export let nodeId;
export let selectable;
export let selected = false;
let child;
let isNode;
let getNodeSelection = getContext('getNodeSelection');
let selectNode = getContext('selectNode');
let isNodeselected = getContext('isNodeSelected');
onMount(async () => {
child = childAccessor(node);
isNode = child && Array.isArray(child) && child.length > 0;
})
const handleSelect = () => {
console.log(`${nodeId(node)} :: ${selected}`)
selectNode(node,selected);
}
</script>
{#if isNode}
<details class="treemargin" style="text-align: left">
<summary >
{#if selectable}
<input type="checkbox" bind:value={selected} on:change={handleSelect}/>
{/if}
<svelte:component this={nodeTemplate} data={node}/>
</summary>
{#each node.child as subNode}
<svelte:self {selectable} {nodeId} node={subNode} {nodeTemplate} childAccessor={childAccessor}/>
{/each}
</details>
{:else}
<div class="treemargin leaf">
{#if selectable}
<input type="checkbox" bind:checked={selected} on:change={handleSelect}/>
{/if}
<svelte:component this={nodeTemplate} data={node}/>
</div>
{/if}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment