Based on thereact + typescriptimplementationtreecomponent

github

  • Unit testing support
  • Support on/off
  • Support for deselecting all
  • Support dynamic loading
  • Drag sort support

process line

  • 1.1 Creating a project and installing dependencies
  • 1.2 WebPack configuration initialization supports typescript
  • 2.1 Unit test environment construction
    1. Create a tree menu
    1. Create a tree menu
  • 5.1 Opening and closing the ICONS function
  • 5.2 Enabling or disabling the function -keyToNodeMap
  • 5.3 Enabling/Disabling -images.d.ts Image type declaration
  • 5.3 Opening and closing -onCollapse
  • 5.4 Select/Deselect/Select All functions -parent
  • 5.5 Loading Dynamic Loading
  • 5.6 Drag Sort

1. Initialize the project

  • Project preview

1.1 Creating a Project

mkdir crx-react-tree
cd crx-react-tree
npm init -y
touch .gitignore
Copy the code

1.2 Installation Dependencies

  • cnpm i react @types/react react-dom @types/react-dom -S
React core package @types/react type declaration The react dom render package renders react elements to the dom render declaration file on the pageCopy the code
  • cnpm i webpack webpack-cli webpack-dev-server -D
Webpack Core file webpack-CLI command line file webpack-dev-server Development serverCopy the code
  • cnpm i typescript ts-loader source-map-loader style-loader css-loader less-loader less file-loader url-loader html-webpack-plugin -D
Ts-loader loads ts source-map-loader translates source-map files. Debugging TS files style-loader inserts CSS files into pages. File-loader url-loader loads files Icon/binary/Image HTML-webpack-plugin Generates HTML filesCopy the code
  • cnpm i jest @types/jest ts-jest jest-junit enzyme @types/enzyme enzyme-adapter-react-16 @types/enzyme-adapter-react-16 -D
Unit test test coverage Jest @types/ JEST type declaration TS-Jest Run the TS version of the unit test file jest-junit unit test tool enzyme test react project tool @types/enzyme type declaration enzyme-adapter-react-16 @types/enzyme-adapter-react-16Copy the code
  • cnpm i axios express qs @types/qs -D
  • @typesThe first packages are typeScript declaration files that can be accessednode_modules/@types/XX/index.d.tsTo view the
Module name use
react React is a JavaScript library for creating user interfaces.
react-dom This package serves as the entry point to the DOM and server renderers for React. It is intended to be paired with the generic React package, which is shipped as react to npm.
webpack webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.
webpack-cli The official CLI of webpack
webpack-dev-server Use webpack with a development server that provides live reloading. This should be used for development only.
typescript TypeScript is a language for application-scale JavaScript.
ts-loader This is the TypeScript loader for webpack.
source-map-loader Extracts source maps from existing source files (from their sourceMappingURL).
style-loader Inject CSS into the DOM.
css-loader The css-loader interprets @import and url() like import/require() and will resolve them.
less-loader A Less loader for webpack. Compiles Less to CSS.
less This is the JavaScript, official, stable version of Less.
file-loader The file-loader resolves import/require() on a file into a url and emits the file into the output directory.
url-loader A loader for webpack which transforms files into base64 URIs.
html-webpack-plugin Plugin that simplifies creation of HTML files to serve your bundles
jest jest is a delightful JavaScript Testing Framework with a focus on simplicity.
jest-junit A Jest reporter that creates compatible junit xml files
ts-jest ts-jest is a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript.
enzyme JavaScript Testing utilities for React
enzyme-adapter-react-16 Enzyme is a JavaScript Testing utility for React that makes it easier to test your React Components’ output. You can also manipulate, traverse, and in some ways simulate runtime given the output.

1.3 support typescript

  • So first you need to generate onetsconfig.jsonFile to tellts-loaderHow do I compile TypeScript code
tsc --init
Copy the code

tsconfig.json

{
  "compilerOptions": {
    "target": "es5"."module": "commonjs"."jsx": "react"."outDir": "./dist"."rootDir": "./src"."noImplicitAny": true."esModuleInterop": true
  },
  "include": ["./src/**/*"."./typings/**/*"]}Copy the code
parameter meaning
target Convert es5
module Code specification
jsx The react mode generates react. CreateElement, which does not need to be converted before use. The output file has a.js extension
outDir Specify the output directory
rootDir Specify the root directory
sourceMap When compiling ts files into JS files, the corresponding sourceMap files are generated
noImplicitAny If true, the TypeScript compiler still generates JS files when it cannot infer the type, but it also reports an error
esModuleInterop Whether to translate the common.js module
include The directory to compile

1.3 support typescript

jest.config.js

module.exports = {
  verbose: true.// Redundancy displays detailed information
  clearMocks: true./ / remove away
  collectCoverage: true.// Collect test coverage information
  reporters: ["default"."jest-junit"].// The format of the reporter report defaults to unit tests
  moduleFileExtensions: ["js"."jsx"."ts"."tsx"].// Module file extension
  moduleDirectories: ["node_modules"].// Module directory
  transform: {
    // If the module is a.tsx ending file, it needs to be translated with ts-jest
    "^.+\\.tsx? $": "ts-jest",},/ / to unit test of regular matching: file in __tests__ directory, or by JSX | TSX at the end of the file
  testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx|tsx)$"};Copy the code

1.4 Enabling or Disabling the Function

src/index.tsx

import React from "react";
import ReactDOM from "react-dom";
import Tree from "./components/tree";
import data from "./data";

ReactDOM.render(<Tree data={data} />.document.getElementById("root"));
Copy the code

src/typings.tsx

// Declare an interface, named Treedata, that defines the type of a variable, the name of the property of the object, and the type of the property
export interface TreeData {
  name: string;
  key: string;
  type: string;
  collapsed: boolean; children? : TreeData[];/ /? : indicates an optional attribute, which can be given or not givenparent? : TreeData; checked? :boolean;
}

// Interface type, which can be used to decorate or constrain component property objects
export interface Props {
  data: TreeData;
  onCollapse: any;
  onCheck: any;
}
export interface State {
  data: TreeData;
}
export interface keyToNodeMap {
  [key: string]: TreeData;
}
Copy the code

src/components/tree.tsx

import React from "react";
import "./index.less";
import { TreeData, Props, State, keyToNodeMap } from ".. /typings";
import TreeNode from "./tree-node";

class Tree extends React.Component<Props.State> {
  keyToNodeMap: keyToNodeMap;
  constructor(props: Props) {
    super(props);
    this.state = { data: this.props.data };
  }
  componentDidMount() {
    this.buildKeyMap();
  }
  buildKeyMap = () = > {
    const data = this.state.data;
    this.keyToNodeMap = {};
    this.keyToNodeMap[data.key] = data;
    if (data.children && data.children.length > 0) {
      this.walk(data.children, data);
    }
    this.setState({ data: this.state.data });
  };
  walk = (children: TreeData[], data: TreeData) = > {
    children.forEach((item: TreeData) = > {
      item.parent = data;
      this.keyToNodeMap[item.key] = item;
      if (item.children && item.children.length > 0) {
        this.walk(item.children, item); }}); }; onCollapse =(key: string) = > {
    let data = this.keyToNodeMap[key];
    if(data) { data.collapsed = ! data.collapsed; data.children = data.children || [];// Lazy loading
      this.buildKeyMap(); }}; onCheck =(key: string) = > {
    let data: TreeData = this.keyToNodeMap[key];
    if(data) { data.checked = ! data.checked;if (data.checked) {
        this.checkChildren(data.children, true);
        this.checkParentCheckAll(data.parent);
      } else {
        this.checkChildren(data.children, false);
        this.checkParent(data.parent, false);
      }
      this.setState({ data: this.state.data }); }}; checkParentCheckAll =(parent: TreeData) = > {
    while (parent) {
      parent.checked = parent.children.every((item) = >item.checked); parent = parent.parent; }}; checkParent =(parent: TreeData, checked: boolean) = > {
    // It can be optimized to stop the recursive search by finding the same state as the current node instead of finding vertices
    while(parent) { parent.checked = checked; parent = parent.parent; }}; checkChildren =(children: Array<TreeData> = [], checked: boolean) = > {
    children.forEach((item: TreeData) = > {
      item.checked = checked;
      this.checkChildren(item.children, checked);
    });
  };
  render() {
    return (
      <div className="tree">
        <div className="tree-nodes">
          <TreeNode
            onCheck={this.onCheck}
            onCollapse={this.onCollapse}
            data={this.props.data}
          />
        </div>
      </div>); }}export default Tree;
Copy the code

src/components/tree-node/tsx

import React from "react";
import { TreeData, Props } from ".. /typings";
import closedFolder from ".. /assets/closed-folder.png";
import file from ".. /assets/file.png";
import openedFolder from ".. /assets/opened-folder.png";

class TreeNode extends React.Component<Props> {
  constructor(props: Props) {
    super(props);
  }
  render() {
    let {
      data: { name, children, collapsed = false, checked = false, key },
    } = this.props;
    let caret, icon;
    if (children) {
      if (children.length > 0) {
        caret = (
          <span
            className={`collapseThe ${collapsed ? "caret-right" : "caret-down` "}}onClick={()= > this.props.onCollapse(key)}
          />
        );
        icon = collapsed ? closedFolder : openedFolder;
      } else {
        caret = null; icon = file; }}else {
      caret = (
        <span
          className={`collapse caret-right`}
          onClick={()= > this.props.onCollapse(key)}
        />
      );
      icon = closedFolder;
    }
    return (
      <div className="tree-node">
        <div className="inner">
          {caret}
          <span className="content">
            <input
              type="checkbox"
              checked={checked}
              onChange={()= > this.props.onCheck(key)}
            />
            <img style={{ width: 20 }} src={icon} />
            {name}
          </span>
        </div>{children && children.length > 0 && ! collapsed && (<div className="children">
            {children.map((item: TreeData) => (
              <TreeNode
                onCheck={this.props.onCheck}
                onCollapse={this.props.onCollapse}
                key={item.key}
                data={item}
              />
            ))}
          </div>
        )}
      </div>); }}export default TreeNode;
Copy the code

index.less

.tree {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: 80%;
  overflow-x: hidden;
  overflow-y: auto;
  background-color: #eee;
  .tree-nodes {
    position: relative;
    overflow: hidden;
    .tree-node {
      .inner {
        color: # 000;
        font-size: 20px;
        position: relative;
        cursor: pointer;
        padding-left: 10px;
        .collapse {
          position: absolute;
          left: 0;
          cursor: pointer;
        }
        .caret-right:before {
          content: "\25B8";
        }
        .caret-down:before {
          content: "\25BE";
        }

        .content {
          display: inline-block;
          width: 100%;
          padding: 4px 5px; }}.children {
        padding-left: 20px; }}}}Copy the code

data.tsx

import { TreeData } from "./typings";
const data: TreeData = {
  name: "Father".key: "1".type: "folder".collapsed: false.children: [{name: 1 "son".key: "1-1".type: "folder".collapsed: false.children: [{name: "Grandson 1".key: "1-1-1".type: "folder".collapsed: false.children: [{name: "Contact 1".key: "1-1-1".type: "file".collapsed: false.children: [],},],},}, {name: "儿子2".key: "1-2".type: "folder".collapsed: true,}]};export default data;
Copy the code

src/typings/images.d.ts

declare module "*.svg";
declare module "*.png";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.gif";
declare module "*.bmp";
declare module "*.tiff";
Copy the code

1.5 Select, Deselect, or Select All

typings.tsx

export interface TreeData {
  name: string;
  key: string;
  type: string;
  collapsed: boolean; children? : TreeData[];/ /? : indicates an optional attribute, which can be given or not given+ parent? : TreeData; + checked? :boolean;
}
export interface Props {
  data: TreeData;
  onCollapse: any;
+  onCheck: any;
}
Copy the code

tree.tsx

buildKeyMap = () = > {
  const data = this.state.data;
  this.keyToNodeMap = {};
  this.keyToNodeMap[data.key] = data;
  if (data.children && data.children.length > 0) {
    this.walk(data.children, data);
  }
  this.setState({ data: this.state.data });
};
walk = (children: TreeData[], data: TreeData) = > {
  children.forEach((item: TreeData) = > {
    item.parent = data;
    this.keyToNodeMap[item.key] = item;
    if (item.children && item.children.length > 0) {
      this.walk(item.children, item); }}); }; onCheck =(key: string) = > {
  let data: TreeData = this.keyToNodeMap[key];
  if(data) { data.checked = ! data.checked;if (data.checked) {
      this.checkChildren(data.children, true);
      this.checkParentCheckAll(data.parent);
    } else {
      this.checkChildren(data.children, false);
      this.checkParent(data.parent, false);
    }
    this.setState({ data: this.state.data }); }}; checkParentCheckAll =(parent: TreeData) = > {
  while (parent) {
    parent.checked = parent.children.every((item) = >item.checked); parent = parent.parent; }}; checkParent =(parent: TreeData, checked: boolean) = > {
  // It can be optimized to stop the recursive search by finding the same state as the current node instead of finding vertices
  while(parent) { parent.checked = checked; parent = parent.parent; }}; checkChildren =(children: Array<TreeData> = [], checked: boolean) = > {
  children.forEach((item: TreeData) = > {
    item.checked = checked;
    this.checkChildren(item.children, checked);
  });
};
Copy the code
<TreeNode
  onCheck={this.onCheck}
  onCollapse={this.onCollapse}
  data={this.props.data}
/>
Copy the code

tree-node.tsx

<input
    type="checkbox"
    checked={checked}
    onChange={() = > this.props.onCheck(key)}
  />

render() {
  let {
    data: { name, children, collapsed = false, checked = false, key },
  } = this.props; }Copy the code

1.6 Loading Dynamic loading

1.7 Drag Sort