Giter Site home page Giter Site logo

marchaos / react-virtualized-sticky-tree Goto Github PK

View Code? Open in Web Editor NEW
149.0 7.0 12.0 1.46 MB

A React component for efficiently rendering tree like structures with support for position: sticky

License: MIT License

JavaScript 100.00%
tree react virtual virtualized virtualizedlist position sticky-headers stickyheader sticky

react-virtualized-sticky-tree's Introduction

react-virtualized-sticky-tree

A React component for efficiently rendering tree like structures with support for position: sticky. react-virtualized-sticky-tree uses a similar API to react-virtualized.

Demo

https://marchaos.github.io/react-virtualized-sticky-tree/

Getting Started

npm install react-virtualized-sticky-tree --save

Usage

Basic Example

import { StickyTree } from 'react-virtualized-sticky-tree';

const tree = {
  root: { name: 'Root', children: ['child1', 'child2', 'child3'], depth: 0 },
  child1: { name: 'Child 1', children: ['child4'], depth: 1 },
  child2: { name: 'Child 2', depth: 2 },
  child3: { name: 'Child 3', depth: 2 },
  child4: { name: 'Child 4', depth: 3 },
};

const getChildren = (id) => {
  if (tree[id].children) {
    return tree[id].children.map(id => ({ id, height: 30, isSticky: true }));
  }
};

const rowRenderer = ({ id, style }) => {
  const node = tree[id];
  return <div style={style}>{node.name}</div>
};

render() {
  return (
      <StickyTree
        root={{ id: 'root', height: 30 }}
        width={width}
        height={height}
        getChildren={getChildren}
        rowRenderer={rowRenderer}
        renderRoot={true}
        overscanRowCount={20}
      />
  );
)

Nested Sticky Header Styles

StickyTree renders the component within a nested structure so that the header's position may be 'stuck' at different levels (see demo). When passing the root node or items in the children array, specifying isSticky: true will make the item sticky.

Every nested sticky level should have a top which is at the bottom of the sticky level above it. For example. If your root node is 30px high and has a top of 0, the next sticky node should have a top of 30px. The z-index of the node should also be lower than the nodes above it (so that it is scrolled out of view underneath its parent node). If your root node is z-index 4, then the node below could be 3, below that 2 and so on.

An implementation of this would look like:

const getChildren = (id) => {
    if (shouldBeSticky(id)) {
      return tree[id].children.map(childId => ({
         id: childId, 
         isSticky: true,
         stickyTop: tree[childId].depth * 10,
         zIndex: 30 - tree[childId].depth, 
         height: 10
      }))
    }
    return tree[id].children.map(childId => ({ id: childId, isSticky: false, height: 10 }))
};

/**
 * Here, style will include the styles to make the node sticky in the right position. 
 */
const rowRenderer = ({ id, style }) => {
  return <div className="row" style={style}>{mytree[id].name}</div>;
};

Be sure to pass a sticky root node to StickyTree if it should be sticky

<StickyTree
    className="treee"
    root={{ id: 'root', isSticky: true, stickyTop: 0, zIndex: 3, height: 10 }}
    rowRenderer={rowRenderer}
    getChildren={getChildren}
/>

Dynamic Height Container

If the containing element of your tree has a dynamic height, you can use react-measure to provide the width and height to sticky-tree so that it can resize to the available width.

For Simplicity, react-virtualized-sticky-tree includes a component which uses react-measure to achieve this:

import { AutoSizedStickyTree } from 'react-virtualized-sticky-tree';

<AutoSizedStickyTree
    className="tree"
    root={{ id: 'root', isSticky: true, stickyTop: 0, zIndex: 3, height: 30 }}
    rowRenderer={rowRenderer}
    getChildren={getChildren}
    ...
/>

If you want to do this yourself, you can install react-measure:

npm install react-measure --save

as a HOC:

const MeasuredTree = withContentRect('bounds')(({ measureRef, measure, contentRect }) => (
  <div ref={measureRef} className="sticky-wrapper">
    <StickyTree
      root={{id: 0}}
      getChildren={getChildren}
      rowRenderer={rowRenderer}
      renderRoot={true}
      width={contentRect.bounds.width}
      height={contentRect.bounds.height}
      overscanRowCount={20}
    />
  </div>
));

or within render()

<Measure
    bounds={true}
    onResize={(contentRect) => {this.setState({ dimensions: contentRect.bounds });}}
>
    {({ measureRef }) => 
          <div ref={measureRef} className="sticky-tree-wrapper">
              <StickyTree
                  width={this.state.dimensions.width}
                  height={this.state.dimensions.height}
                  root={{id: 0 }}
                  renderRoot={true}
                  rowRenderer={this.rowRenderer}
                  getChildren={this.getChildren}
                  overscanRowCount={20}
              />
          </div>
    }
</Measure>

Supported Browsers

  • Tested with Chrome 59+
  • Tested with Safari 11+
  • Tested with Firefox 54+

Rendering tree structures is supported in all modern browsers. For position: sticky, See http://caniuse.com/#search=position%3Asticky

react-virtualized-sticky-tree's People

Contributors

dependabot[bot] avatar dottiedot avatar jpnz4 avatar marchaos avatar melalj avatar nerdcowboy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-virtualized-sticky-tree's Issues

Always getting SCROLL_REASON.OBSERVED even after setting through setScrollTop

This looks like minor miss. Is that intended to call this.props.onScroll always with SCROLL_REASON.OBSERVED (from onScroll handler) ?

this.props.onScroll({ scrollTop, scrollLeft, scrollReason: SCROLL_REASON.OBSERVED });

In my one of the use case, I wanted to track when user is actually scrolling. So this differentiation will be crucial for me

Cannot read property 'top' of undefined

Hey,

I have a sticky tree with a search/filter function at the top. When typing in the filter it will reduce the number of nodes that match the searched text. The list is displayed with an auto scroll as the number of nodes is a few thousand.

I get an error when I scroll to the bottom of the list and then type in my filter box at StickyTree.backwardSearch (StickyTree.js:1012).

In this method the search position comes in at the position the scroll was at before the list got filtered. However the node list in this function is the updated list with the amount of nodes that were filtered. The searchPosition is higher then the node list length and when it starts the loop, it will use an index that doesn't exist in the node array.

Ill try get a PR in today.

DOM not utilising entire window Height: Video

Link to the Sample Youtube video

As you could see from the video, At the end of the scroll, the Entire assigned height is used, however during mid-scroll only half of the window size renders children DOMs.
I am providing a height of calc(100vh - 6.6em) to StickTree props
What could possibly be the reason?
Also, there is some noticable jitter during scroll, any particular way to avoid it?

The last node from list does not render in DOM

Observed the issue where last node does not render in DOM.

As per my observation in spacial cases like above, during forwardSearch this.currentPos stays at same value which then prevents call to this.storeRenderTree to trigger re-render.

Things which are causing render of last node -

  • Increase in overscanRowCount from 1 to 2 (Cant use in my case as that will be costly to me)
  • Change height of leaf nodes from last couple of rows (tried with 430 than 130 and it worked)
  • Once you scroll to the last > scroll back up > This renders the last node. (This is not a fix)

Not sure of any better way to handle this so that it will work for all cases. Please add your thoughts as well. It might be related to the issue https://github.com/marchaos/react-virtualized-sticky-tree/issues/15

Cannot read properties of undefined (reading 'id')

I use code from example, and have problem. I don't know how resolve them.

Screen:

image

My code:

import React from "react"
import ReactDom from "react-dom"

import { StickyTree } from 'react-virtualized-sticky-tree';

import { Button } from '@material-ui/core';

const tree = {
  root: { name: 'Root', children: ['child1', 'child2', 'child3'], depth: 0 },
  child1: { name: 'Child 1', children: ['child4'], depth: 1 },
  child2: { name: 'Child 2', depth: 2 },
  child3: { name: 'Child 3', depth: 2 },
  child4: { name: 'Child 4', depth: 3 },
};

const getChildren = (id) => {
  if (tree[id].children) {
    return tree[id].children.map(id => ({ id, height: 30, isSticky: true }));
  }
};

const rowRenderer = ({ id, style }) => {
  const node = tree[id];
  return <div style={style}>{node.name}</div>
};

function App() {
    return (<>
      <Button color="primary">Hello World</Button>
      <StickyTree
        root={{ id: 'root', height: 30 }}
        width={400}
        height={30}
        getChildren={getChildren}
        rowRenderer={rowRenderer}
        renderRoot={true}
        overscanRowCount={20}
      />
    </>)
}

ReactDom.render(<App />, document.getElementById('app'))

package.json:

{
  "name": "mapa",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --mode production",
    "start": "sh server.sh && webpack-dev-server --mode development --hot",
    "service": "webpack-dev-server --mode development"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.17.2",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-react": "^7.16.7",
    "babel-loader": "^8.2.3",
    "css-loader": "^6.6.0",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^5.5.0",
    "style-loader": "^3.3.1",
    "webpack": "^5.68.0",
    "webpack-cli": "^4.9.2"
  },
  "dependencies": {
    "@emotion/react": "^11.7.1",
    "@emotion/styled": "^11.6.0",
    "@material-ui/core": "^4.12.3",
    "@mui/material": "^5.4.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-virtualized-sticky-tree": "^3.0.0-beta13",
    "webpack-dev-server": "^4.7.4"
  }
}

List Missing

I'm Using WindowsScroller with Autosizer, i scroll through my list first then filter it, when it's filtered the list shows empty but the list is in the element just not visible, when i scroll then it shows the list, sometimes it just completely show no items, even on element nothing, unless i filter it again then the other issue occur.

transformY rather than top

Is there a technical limitation for why you can't transformY as it should be better in terms of performance... Would position sticky not work with transformY?

Custom renderer for parent-node and list-node

First of all, thanks for this library! It is exactly what I was looking for!

I'm currently trying to add some tree lines (with offsets), but it is currently rather difficult to do so.

What would make it easier is if we could provide a custom renderer for the parent-node and list-node divs. That way I could add some SVG lines or divs myself.

Is that something you are willing to add? I could also do a PR if needed.

Update component lifecycle methods

This library uses lifecycle methods such as componentWillMount which are no longer recommended to use. This generates warnings for users such as below:

image

Would it be possible to update the lifecycle methods as recommended?

Quick scroll missing render

Hi,

I came across an issue from the last update. When I scroll quickly to the bottom of my tree it will show a blank list or a partial list. This is happening in the 'setScrollTopAndClosestNode' method where its checking if the scroll is at the bottom. This prevents an update from happening and the list is returning nodes that are a lot further up. If I remove this check then it renders as expected.

// If we are scrolled to the bottom, no need to do any more work.
                if (this.elem.scrollTop >= this.elem.scrollHeight - this.elem.offsetHeight) {
                    return;
                }

Issue while using scrollNodeIntoView for implementing jump to functionality

Came across the weird case where calling scrollNodeIntoView after manual scrolling hides row randomly. The row gets rendered again when scroll.

Created a below reduce test case to showcase the issue -
Sandbox - reduce test case

  • Steps to reproduce
  1. Click on button "jump to child 10" >Auto scroll should move you at bottom > Everything works fine.
  2. Scroll back top and then scroll down till end manually
  3. Now, click on "jump to child 10" > See Row "Child 8" gets disappeared

This looks strange. One observation though, if you increase the overscanRowCount to say 20 (in above case) it stops. Could you please have a look here ?.

I will update if i see any unusual thing.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.