Ужасно простая реализация дерева со сверхспособностями Svelte.

Дерево имеет видимую глубину, текстовый фильтр и функции переключения, написанные настолько лаконично и масштабируемо, насколько это возможно в Svelte.

Итак, у нас есть следующие компоненты:

- tree - just wrapping it all together
- root - to allow multiple roots in a tree
- node - a svelte:self component that iterates itself
- open - svg for open arrow
- closed - svg for closed arrow
- store.js - a store to hold keyword results for any use (e.g count)

корень

  • предоставить входные данные для дерева фильтров
  • передать данные узлу и сообщить узлу, что это корень
  • передать visibleDepth, чтобы проверить, должен ли узел быть видимым
<input type=range bind:value={visibleDepth} min=1 max={4}>      
   {visibleDepth}
<input type=text bind:value={text}/>
   {$results.length}
<ul>
   {#if visibleDepth}
     <Node bind:node={data} {visibleDepth} {text} root/>
   {/if}
</ul>

узел

начнем с разметки, далее скрипт

  • обернуть по логике отображения
  • нажмите, чтобы переключить
  • стиль, если он соответствует фильтру
  • если он открыт и имеет дочерние элементы, повторяющие компонент
{#if  !text || visible || childVisible || root}
  <li on:click={toggle} style="padding-left:{level*1}rem" transition:slide>
 <svelte:component this={state}/> <!-- arrow -->
   <span style={visible ? 'color: red':''}>{level}:   {node.data</span>
</li>
{/if}
{#if state === Open && node.children}
  {#each node.children as child}
   <svelte:self node={child} {visibleDepth} {text} level={level+1} bind:visible={childVisible}/>
  {/each}
{/if}

вот его скриптовая часть

  • функция фильтрации по вводу текста
  • передать в магазин, если элемент соответствует
  • проверить, какую стрелку отображать — отфильтрованную или нет
  • рассчитать, видны ли
  • сбросить хранилище результатов, если фильтр не указан
const filtered = (item) => {
  let match = text.length === 0 || item.data.toLowerCase().includes(text)
  if(match) {results.set([...new Set([...$results, item.data])]) } else {results.set($results.filter(i=> i !=  item.data)) }
  return match
  }
 export let root
 let state 
 $:  state = level < visibleDepth ? Open : Closed
 function toggle() { 
  state = state === Open ? Closed : Open
 }  
$:  visible = text != ''  ? filtered(node) : false
 $: text === '' ? results.set([]) : ''

https://svelte.dev/repl/407903af00114bd181f2bb314b46358d?version=3.44.1