npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-smart-tree-pro

v1.0.9

Published

Drag & drop hierarchical list made as a react component with Editable node

Downloads

16

Readme

Table of Contents

Demo

Demo

Installation

npm install -save react-smart-tree-pro

Usage

import { createRoot } from 'react-dom/client';

import SamrtTree, { SamrtTreeProps } from '../index';
import '../styles/index.css';

const styles: CSSProperties = {
  position: "relative",
  padding: "10px 15px",
  fontSize: "20px",
  border: "1px solid #f9fafa",
  background: "#f9fafa",
  cursor: "pointer",
};

const handlerStyles: CSSProperties = {
  position:"absolute",
  top: 0,
  left: 0,
  marginRight:"10px",
  width: "15px",
  height: "15px",
 
  cursor: "hand",
};

const names = [
  { id: 0, text: 'Andy' },
  {
    id: 1,
    text: 'Harry',
    
    isEditMode:true,
    children: [{ id: 2, text: 'David' }],
  },
  {
    id: 3,
    text: 'Lisa',
    children: [{ id: 4, text: 'Richard' }],
  },
];

const grocery = [
  { id: 0, text: 'Apples', type: 'fruits' },
  {
    id: 1,
    text: 'Fruit box',
    type: 'box',
    accepts: ['fruits'],
    children: [{ id: 2, text: 'Bananas', type: 'fruits' }],
  },
  {
    id: 3,
    text: 'Box',
    type: 'box',
    accepts: ['fruits', 'sweets'],
    children: [{ id: 4, text: 'Candy', type: 'sweets' }],
  },
];

class Example extends Component {
  refSamrtTree: SamrtTree | undefined;

  state = {
    example: 0,
    defaultCollapsed: false,
    isCollapsingAllowed: true,
    isDraggingAllowed: true,
    isEditableAlloed:false 
    
  };

  collapse = (collapseCase: 0 | 1 | 2) => {
    if (this.refSamrtTree) {
      switch (collapseCase) {
        case 0:
          this.refSamrtTree.collapse('NONE');
          break;
        case 1:
          this.refSamrtTree.collapse('ALL');
          break;
        case 2:
          this.refSamrtTree.collapse([1]);
          break;
      }
    }
  };

  isCollapsed = () => {
    const form = document.querySelector('form');
    const checkbox = form?.querySelector<HTMLInputElement>('input[type=checkbox]');
    return checkbox?.checked;
  };

  toggleDefaultCollapsed = () => {
    const { defaultCollapsed } = this.state;
    this.setState({ defaultCollapsed: !defaultCollapsed });
  };

  toggleCollapsingAllowed = () => {
    const { isCollapsingAllowed } = this.state;
    this.setState({ isCollapsingAllowed: !isCollapsingAllowed });
  };

  toggleDraggingAllowed = () => {
    const { isDraggingAllowed } = this.state;
    this.setState({ isDraggingAllowed: !isDraggingAllowed });
  };
  toggleEditAllowed = () => {
    const { isEditableAlloed } = this.state;
    this.setState({ isEditableAlloed: !isEditableAlloed });
  };
  renderItem: SamrtTreeProps['renderItem'] = ({ item, collapseIcon, handler }) => {
    const { isDraggingAllowed } = this.state;

    return (
      <div style={isDraggingAllowed ? styles : { ...styles, cursor: 'inherit' }} className=''>
        {handler}
        {collapseIcon}
        {item.text}
      </div>
    );
  };

  renderExampleOne = (title: string) => {
    const { defaultCollapsed, isCollapsingAllowed, isDraggingAllowed, isEditableAlloed } = this.state;

    return (
      <div>
        <h2>{title}</h2>
        <SamrtTree
          items={grocery}
          collapsed={defaultCollapsed}
          disableCollapse={!isCollapsingAllowed}
          disableDrag={!isDraggingAllowed}
          renderItem={this.renderItem}
        isEditable={isEditableAlloed}
          ref={el => this.refSamrtTree = el} 
          handler={<span style={handlerStyles} className='SamrtTree-icon-drag-btn' />}
        />

        <br/>
        <button type="button" onClick={() => this.collapse(0)}>Expand all</button>
        <button type="button" onClick={() => this.collapse(1)}>Collapse all</button>
        <button type="button" onClick={() => this.collapse(2)}>Collapse Harry only</button>
        <form style={{ display: "inline-block" }}>
          <label>
            <input type="checkbox" onChange={this.toggleDefaultCollapsed}/>
            Collapsed by default
          </label>
          <label>
            <input type="checkbox" onChange={this.toggleCollapsingAllowed}/>
            Is collapsing allowed
          </label>
          <label>
            <input type="checkbox" onChange={this.toggleDraggingAllowed}/>
            Is dragging allowed
          </label>
     
        </form>
      </div>
    );
  };

  renderExampleTwo = (title: string) => {
    return (
      <div>
        <h2>{title}</h2>
        <SamrtTree
          items={names}
        isEditable={this.state.isEditableAlloed}
          renderItem={this.renderItem}
       
        />
      </div>
    );
  };

  confirmChange: SamrtTreeProps['confirmChange'] = ({ dragItem, destinationParent }) => {
    // move to root level
    if (!destinationParent) return true;

    return (destinationParent.accepts || []).indexOf(dragItem.type) > -1;
  };

  renderExampleThree = (title: string) => {
    return (
      <div>
        <h2>{title}</h2>
        <SamrtTree
          items={grocery}
          isEditable={this.state.isEditableAlloed}
          renderItem={this.renderItem}
          confirmChange={this.confirmChange}
          handler={<span style={handlerStyles} className='SamrtTree-icon-drag-btn' />}
        />
      </div>
    );
  };

  getExamples = () => {
    return [
      { title: 'Basic example', renderer: this.renderExampleOne },
      { title: 'Example with handlers', renderer: this.renderExampleTwo },
      { title: 'Example with confirmChange', renderer: this.renderExampleThree },
    ]
  }

  onExampleChange = (e: ChangeEvent<HTMLSelectElement>) => {
    this.setState({ example: +e.target.value });
  };

  render() {
    const { example } = this.state;
    const examples = this.getExamples();

    return (
      <div>
        <select onChange={this.onExampleChange} value={example}>
          {examples.map(({ title }, i) => (
            <option key={i} value={i}>{title}</option>
          ))}
        </select>

        <hr/>

        {examples[example].renderer(examples[example].title)}
        <label>
          <br></br>
            <input type="checkbox" onChange={this.toggleEditAllowed} checked={this.state.isEditableAlloed}/>
            Is Editable
          </label>
      </div>
      
    );
  }
}

createRoot(document.getElementById('app')).render(<Example />);

Options

| Property | Type | Default | Description | |----------|------|---------|-------------| | childrenProp | string | "children" | Name of a property for children. | | className | string | undefined | Extra class name which can be passed to a root element. | | collapsed | boolean | false | Makes groups collapsed by default. | | confirmChange | function | () => true | Callback which has a single parameter with keys: dragItem - item which is being dragged, destinationParent - item where dragItem is about to land (or null if moving to root). Let function return false if this movement should not happen. | | disableCollapse | boolean | false | Disable toggling a collapsed state of items with children. If you need to set a specific initial state, then it is still possible to do so with the public method collapse. | | disableDrag | boolean or function | false | Disable dragging. Pass boolean to apply to all items. Pass a callback to target individual items. It has a single parameter with keys: item - item from your array, index - number, index of the item, depth - number, depth of the item. | | group | string or number | random string | Different group names may be passed if you have more than one SamrtTree component on a page and want some extra styles for portal instances. | | handler | node | undefined | If you pass it, it will get wrapped with drag handlers and you may use it in your render method. | | idProp | string | "id" | Name of a property for id. | | items | array | [] | Array of items. Every item must be of shape {id: @uniq} to distinguish elements. | | maxDepth | number | 10 | Maximum available level of nesting. | | onChange | function | () => {} | Callback which has a single parameter with keys: items - new array after position was changed, dragItem - item which has been moved, targetPath - array of numbers, those numbers are indices and they make path to a location, to where item has been moved. | | onCollapseChange | function | () => {} | Callback which has a single parameter with one of two possible keys: openIds - array of ids which are open if collapsed is set to ture, or closedIds - array of ids which are closed if collapsed is set to false. Note: this callback is triggered not only when user explicitly opens or closes an item, but when implicit events happen as well, like when the only child of open item is moved out. | | onDragEnd | function | () => {} | Callback which has no parameters. It is invoked when dragging ends via drop or cancel. | | onDragStart | function | () => {} | Callback which has a single parameter with keys: dragItem - item which has been moved. | | renderCollapseIcon | function | () => <DefaultIcon /> | Function for rendering collapse icon. Has a single parameter with keys: isCollapsed - boolean, true if this group has children and is collapsed, item - item from your array. | | renderItem | function | ({item}) => String(item) | Function for rendering every item. Has a single parameter with keys: item - item from your array, index - number, index of the item, depth - number, depth of the item, collapseIcon - node, icon for items with children (allows you to collapse the group), handler - node, which you have passed via the same property, but wrapped with some additional events, isDraggable - boolean, tells if dragging is allowed for this item (see disableDrag prop for details). | | threshold | number | 30 | Amount of pixels which mouse should move horizontally before increasing/decreasing level (nesting) of current element. |

Public methods

| Method | Accepts | Description | |--------|---------|-------------| | collapse | string or array | "NONE" - expand all groups; "ALL" - collapse all groups; [] - collapse all groups with ids from given array. |

Todo

  • add touch
  • cover with tests

PS: Please, make an issue or create a PR if you need any more functionality.

License

ISC