Provide flat, virtual snapshot of a tree

Published on webcomponents.org

<xtal-tree>

Provide flat, virtual snapshot of a tree.

<button disabled data-expand-cmd="allExpandedNodes">Expand All</button>
<p-d on="click" to="xtal-tree" prop="expandCmd" val="target.dataset.expandCmd" m="1" skip-init></p-d>
<button disabled data-expand-cmd="allCollapsedNodes">Collapse All</button>
<p-d on="click" to="xtal-tree" prop="expandCmd" val="target.dataset.expandCmd" m="1" skip-init></p-d>
<button disabled data-dir="asc">Sort Asc</button>
<p-d on="click" to="xtal-tree" prop="sorted" val="target.dataset.dir" m="1" skip-init></p-d>
<button disabled data-dir="desc">Sort Desc</button>
<p-d on="click" to="xtal-tree" prop="sorted" val="target.dataset.dir" m="1" skip-init></p-d>
<input disabled type="text" placeholder="Search">
<p-d-r on="input" to="xtal-split" prop="search" val="target.value"></p-d-r>
<p-d on="input" to="xtal-tree" prop="searchString" val="target.value"></p-d>

<!-- ================= Get Sample JSON with Tree Structure (File Directory), Pass to xtal-tree -->
<xtal-fetch-req fetch href="https://unpkg.com/xtal-tree@0.0.34/demo/directory.json" as="json"></xtal-fetch-req>
<!-- =================  Pass JSON object to xtal-tree for processing ========================= -->
<p-d on="fetch-complete" to="xtal-tree" prop="nodes" val="target.value" m="1"></p-d>

<!-- ================= Train xtal-tree how to expand / collapse nodes ========================= -->
<xtal-deco><script nomodule>({
    vals:{
      childrenFn: node => node.children,
      isOpenFn: node => node.expanded,
      levelSetterFn: function (nodes, level) {
        nodes.forEach(node => {
          node.style = 'margin-left:' + (level * 12) + 'px';
          if (node.children) this.levelSetterFn(node.children, level + 1)
        })
      },
      toggleNodeFn: node => {
        node.expanded = !node.expanded;
      },
      testNodeFn: (node, search) => {
        if (!search) return true;
        if (!node.nameLC) node.nameLC = node.name.toLowerCase();
        return node.nameLC.indexOf(search.toLowerCase()) > -1;
      },
      compareFn: (lhs, rhs) => {
        if (lhs.name < rhs.name) return -1;
        if (lhs.name > rhs.name) return 1;
        return 0;
      },
    },

    props:{
      expandCmd: '',
      fistVisibleIndex: -1
    },
    methods:{
      onPropsChange(name, newVal){
        switch(name){
          case 'expandCmd':
            this[this.expandCmd] = this.viewableNodes;
            break;
            
        }
      }
    }

})</script></xtal-deco>
<xtal-tree id="myTree"></xtal-tree>
<p-d on="viewable-nodes-changed" to="iron-list" prop="items" val="target.viewableNodes" m="1"></p-d>
<p-d on="viewable-nodes-changed" to="iron-list" prop="newFirstVisibleIndex" val="target.firstVisibleIndex" m="1"></p-d>
<!-- ==============  Styling of iron-list ================== -->
<style>
  div.node {
    cursor: pointer;
  }

  span.match {
    font-weight: bold;
    background-color: yellowgreen;
  }

  span[data-has-children="1"][data-is-expanded="1"]::after{
    content: "�";
  }

  span[data-has-children="1"][data-is-expanded="-1"]::after{
    content: "�";
  }

  span[data-has-children="-1"]::after{
    content: "�";
  }
</style>

<xtal-deco><script nomodule>
  ({
    props: {
      newFirstVisibleIndex: -1,
    },
    on:{
      click: function(e){
        if(!e.target.node) return;
        const firstVisible = this.firstVisibleIndex;
        console.log('firstVisible = ' + firstVisible);
        myTree.toggledNode = e.target.node;
        this.newFirstVisibleIndex = firstVisible;
      }
    },
    methods:{
      onPropsChange: function (name, newVal) {
        switch (name) {
          case 'newFirstVisibleIndex':
            if(!this.items || this.newFirstVisibleIndex < 0) return;
            this.scrollToIndex(this.newFirstVisibleIndex);
        }
      },
    }
  })
</script></xtal-deco>
<iron-list style="height:400px;overflow-x:hidden" id="nodeList" mutable-data p-d-if="p-d-r">
  <template>
    <div class="node" style$="[[item.style]]" p-d-if="p-d-r">
      <span node="[[item]]" p-d-if="p-d-r">
        <if-diff if="[[item.children]]" tag="hasChildren" m="1"></if-diff>
        <if-diff if="[[item.expanded]]" tag="isExpanded" m="1"></if-diff>
        <span data-has-children="-1" data-is-expanded="-1" node="[[item]]"></span>
      </span>
      <xtal-split node="[[item]]" text-content="[[item.name]]"></xtal-split>          
    </div>
  </template>
</iron-list>
<!-- Polyfill for retro browsers -->
<script src="https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<!-- End Polyfill for retro browsers -->

<!-- Polymer Elements -->
<script type="module" src="https://unpkg.com/@polymer/iron-list@3.0.1/iron-list.js?module"></script>
<!-- End Polymer Elements -->

<script type="module" src="https://unpkg.com/xtal-splitting@0.0.9/xtal-split.js?module"></script>
<script type="module" src="https://unpkg.com/xtal-fetch@0.0.57/xtal-fetch-req.js?module"></script>
<script type="module" src="https://unpkg.com/xtal-decorator@0.0.41/xtal-deco.js?module"></script>
<script type="module" src="https://unpkg.com/xtal-tree@0.0.46/xtal-tree.js?module"></script>
<script type="module" src="https://unpkg.com/if-diff@0.0.20/if-diff.js?module"></script>
<script type="module" src="https://unpkg.com/p-d.p-u@0.0.106/p-d.js?module"></script>
<script type="module" src="https://unpkg.com/p-d.p-u@0.0.106/p-d-r.js?module"></script>
``` -->

Often we want to take advantage of a nice flat list generator component, like dom-repeat, or iron-list, but we want to use it to display and manipulate tree data.

This scenario seems to come up so frequently with various components, that this component strives to genericize that requirement.

xtal-tree takes a "watcha-got?" approach to the data -- it allows the specific structure of the tree data to be pretty much anything, and passes no judgment on it. It doesn't accidentally overwrite anything it shouldn't, without specific permission from the developer. The user of xtal-tree, i.e. the developer, then needs to train xtal-tree how to interpret the data -- how to get the children, how to represent an open node vs a closed node, etc.

xtal-tree also takes a "whatcha-want?" approach to what is displayed. You can display the data as a classic tree, or as a treegrid, or as any other way you want. The only assumption xtal-tree makes is that you want to build the display from a flat list generator, like dom-repeat, iron-list, or a flat grid.

Think of xtal-tree as a reusable "View Model" component.

Install the Polymer-CLI

First, make sure you have the Polymer CLI and npm (packaged with Node.js) installed. Run npm install to install your element's dependencies, then run polymer serve to serve your element locally.

Viewing Your Element

$ polymer serve

Install

Link to this version
ImportedReleased 12 March 2019MIT License
Framework Support
Browser Independent
Install with
npm install xtal-tree"@0.0.63"
Run the above npm command in your project folder. If you have any issues installing, please contact the author.
Release notes - Version 0.0.63

Dependencies

  • @polymer/iron-list#3.0.2
  • if-diff#0.0.20
  • p-d.p-u#0.0.110
  • trans-render#0.0.83
  • xtal-decorator#0.0.41
  • xtal-element#0.0.37
  • xtal-fetch#0.0.57
  • xtal-splitting#0.0.9
  • event-switch#0.0.12