first commit
This commit is contained in:
commit
16b56350fb
|
@ -0,0 +1,38 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules
|
||||||
|
jspm_packages
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
dist
|
||||||
|
coverage
|
|
@ -0,0 +1,28 @@
|
||||||
|
bower_components/
|
||||||
|
*.cfg
|
||||||
|
node_modules/
|
||||||
|
nohup.out
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
.ipr
|
||||||
|
.iws
|
||||||
|
*~
|
||||||
|
~*
|
||||||
|
*.diff
|
||||||
|
*.log
|
||||||
|
*.patch
|
||||||
|
*.bak
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.project
|
||||||
|
.*proj
|
||||||
|
.svn/
|
||||||
|
*.swp
|
||||||
|
out/
|
||||||
|
.build
|
||||||
|
node_modules
|
||||||
|
_site
|
||||||
|
sea-modules
|
||||||
|
spm_modules
|
||||||
|
.cache
|
||||||
|
coverage
|
|
@ -0,0 +1,22 @@
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
language: node_js
|
||||||
|
|
||||||
|
node_js:
|
||||||
|
- "6"
|
||||||
|
|
||||||
|
service_name: travis-ci
|
||||||
|
repo_token: add
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- NODE_ENV=travisci
|
||||||
|
- NPM_CONFIG_PROGRESS="false"
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- npm install -g bee-tools
|
||||||
|
|
||||||
|
script: npm test
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- npm run coveralls
|
|
@ -0,0 +1,43 @@
|
||||||
|
# bee-table
|
||||||
|
|
||||||
|
[![npm version](https://img.shields.io/npm/v/bee-table.svg)](https://www.npmjs.com/package/bee-table)
|
||||||
|
[![Build Status](https://img.shields.io/travis/tinper-bee/bee-table/master.svg)](https://travis-ci.org/tinper-bee/bee-table)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/tinper-bee/bee-table/badge.svg?branch=master)](https://coveralls.io/github/tinper-bee/bee-table?branch=master)
|
||||||
|
[![devDependency Status](https://img.shields.io/david/dev/tinper-bee/bee-table.svg)](https://david-dm.org/tinper-bee/bee-table#info=devDependencies)
|
||||||
|
[![NPM downloads](http://img.shields.io/npm/dm/bee-table.svg?style=flat)](https://npmjs.org/package/bee-table)
|
||||||
|
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tinper-bee/bee-table.svg)](http://isitmaintained.com/project/tinper-bee/bee-table "Average time to resolve an issue")
|
||||||
|
[![Percentage of issues still open](http://isitmaintained.com/badge/open/tinper-bee/bee-table.svg)](http://isitmaintained.com/project/tinper-bee/bee-table "Percentage of issues still open")
|
||||||
|
|
||||||
|
## Browser Support
|
||||||
|
|
||||||
|
|![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png)|
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| IE 9+ ✔ | Chrome 31.0+ ✔ | Firefox 31.0+ ✔ | Opera 30.0+ ✔ | Safari 7.0+ ✔ |
|
||||||
|
|
||||||
|
|
||||||
|
react bee-table component for tinper-bee
|
||||||
|
|
||||||
|
some description...
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
|参数|说明|类型|默认值|
|
||||||
|
|:--|:---:|:--:|---:|
|
||||||
|
|
||||||
|
#### 开发调试
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ npm install -g bee-tools
|
||||||
|
$ git clone https://github.com/tinper-bee/bee-table
|
||||||
|
$ cd bee-table
|
||||||
|
$ npm install
|
||||||
|
$ npm run dev
|
||||||
|
```
|
|
@ -0,0 +1,43 @@
|
||||||
|
# bee-dropdown
|
||||||
|
|
||||||
|
[![npm version](https://img.shields.io/npm/v/bee-dropdown.svg)](https://www.npmjs.com/package/bee-dropdown)
|
||||||
|
[![Build Status](https://img.shields.io/travis/tinper-bee/bee-dropdown/master.svg)](https://travis-ci.org/tinper-bee/bee-dropdown)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/tinper-bee/bee-dropdown/badge.svg?branch=master)](https://coveralls.io/github/tinper-bee/bee-dropdown?branch=master)
|
||||||
|
[![devDependency Status](https://img.shields.io/david/dev/tinper-bee/bee-dropdown.svg)](https://david-dm.org/tinper-bee/bee-dropdown#info=devDependencies)
|
||||||
|
[![NPM downloads](http://img.shields.io/npm/dm/<%= packageName%>.svg?style=flat)](https://npmjs.org/package/<%= packageName%>)
|
||||||
|
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tinper-bee/<%= packageName%>.svg)](http://isitmaintained.com/project/tinper-bee/<%= packageName%> "Average time to resolve an issue")
|
||||||
|
[![Percentage of issues still open](http://isitmaintained.com/badge/open/tinper-bee/<%= packageName%>.svg)](http://isitmaintained.com/project/tinper-bee/<%= packageName%> "Percentage of issues still open")
|
||||||
|
|
||||||
|
## Browser Support
|
||||||
|
|
||||||
|
|![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png)|
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| IE 9+ ✔ | Chrome 31.0+ ✔ | Firefox 31.0+ ✔ | Opera 30.0+ ✔ | Safari 7.0+ ✔ |
|
||||||
|
|
||||||
|
|
||||||
|
react bee-dropdown component for tinper-bee
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
|参数|说明|类型|默认值|
|
||||||
|
|:--|:---:|:--:|---:|
|
||||||
|
|
||||||
|
#### develop
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ npm install -g bee-tools
|
||||||
|
$ git clone https://github.com/tinper-bee/bee-dropdown
|
||||||
|
$ cd bee-dropdown
|
||||||
|
$ npm install
|
||||||
|
$ npm run dev
|
||||||
|
```
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Table from '../src/index';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
class Demo extends Component {render(){return( <Table/> )}}
|
||||||
|
export default Demo;
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import "../node_modules/tinper-bee-core/scss/index.scss";
|
||||||
|
@import "../src/Table.scss";
|
||||||
|
@import "../node_modules/bee-panel/src/Panel.scss";
|
||||||
|
@import "../node_modules/bee-layout/src/Layout.scss";
|
||||||
|
@import "../node_modules/bee-button/src/Button.scss";
|
||||||
|
@import "../node_modules/bee-transition/src/Transition.scss";
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Atom One Dark by Daniel Gamage
|
||||||
|
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
|
||||||
|
|
||||||
|
base: #282c34
|
||||||
|
mono-1: #abb2bf
|
||||||
|
mono-2: #818896
|
||||||
|
mono-3: #5c6370
|
||||||
|
hue-1: #56b6c2
|
||||||
|
hue-2: #61aeee
|
||||||
|
hue-3: #c678dd
|
||||||
|
hue-4: #98c379
|
||||||
|
hue-5: #e06c75
|
||||||
|
hue-5-2: #be5046
|
||||||
|
hue-6: #d19a66
|
||||||
|
hue-6-2: #e6c07b
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 0.5em;
|
||||||
|
color: #abb2bf;
|
||||||
|
background: #282c34;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #5c6370;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-doctag,
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-formula {
|
||||||
|
color: #c678dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-section,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-deletion,
|
||||||
|
.hljs-subst {
|
||||||
|
color: #e06c75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-literal {
|
||||||
|
color: #56b6c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-addition,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-meta-string {
|
||||||
|
color: #98c379;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-class .hljs-title {
|
||||||
|
color: #e6c07b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-selector-class,
|
||||||
|
.hljs-selector-attr,
|
||||||
|
.hljs-selector-pseudo,
|
||||||
|
.hljs-number {
|
||||||
|
color: #d19a66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-link,
|
||||||
|
.hljs-meta,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-title {
|
||||||
|
color: #61aeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @title 这是标题
|
||||||
|
* @description 这是描述
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class Demo1 extends Component {
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
欢迎使用老赵DEMO系统
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
|
||||||
|
import { Con, Row, Col } from 'bee-layout';
|
||||||
|
import { Panel } from 'bee-panel';
|
||||||
|
import Button from 'bee-button';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import Table from '../src';
|
||||||
|
|
||||||
|
|
||||||
|
const CARET = <i className="uf uf-chevronarrowdown"></i>;
|
||||||
|
|
||||||
|
const CARETUP = <i className="uf uf-chevronarrowup"></i>;
|
||||||
|
|
||||||
|
|
||||||
|
{demolist}
|
||||||
|
|
||||||
|
class Demo extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
open: false
|
||||||
|
}
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
handleClick() {
|
||||||
|
this.setState({ open: !this.state.open })
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { title, example, code, desc } = this.props;
|
||||||
|
let caret = this.state.open ? CARETUP : CARET;
|
||||||
|
let text = this.state.open ? "隐藏代码" : "查看代码";
|
||||||
|
|
||||||
|
const footer = (
|
||||||
|
<Button shape="block" onClick={ this.handleClick }>
|
||||||
|
{ caret }
|
||||||
|
{ text }
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
const header = (
|
||||||
|
<Row>
|
||||||
|
<Col md={11}>
|
||||||
|
{ example }
|
||||||
|
</Col>
|
||||||
|
<Col md={1}>
|
||||||
|
<Button shape="icon" onClick={ this.handleClick }>
|
||||||
|
{ caret }
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Col md={12} >
|
||||||
|
<h3>{ title }</h3>
|
||||||
|
<p>{ desc }</p>
|
||||||
|
<Panel collapsible headerContent expanded={ this.state.open } colors='bordered' header={ header } footer={footer} footerStyle = {{padding: 0}}>
|
||||||
|
<pre><code className="hljs javascript">{ code }</code></pre>
|
||||||
|
</Panel>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DemoGroup extends Component {
|
||||||
|
constructor(props){
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
{DemoArray.map((child,index) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Demo example= {child.example} title= {child.title} code= {child.code} desc= {child.desc} key= {index}/>
|
||||||
|
)
|
||||||
|
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(<DemoGroup/>, document.getElementById('tinperBeeDemo'));
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Table
|
||||||
|
## 代码演示
|
||||||
|
## API
|
||||||
|
|参数|说明|类型|默认值|
|
||||||
|
|:---|:-----|:----|:------|
|
|
@ -0,0 +1,5 @@
|
||||||
|
## Table
|
||||||
|
## Code display
|
||||||
|
## API
|
||||||
|
|Property|Description|Type|Default|
|
||||||
|
|:---|:-----|:----|:------|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<title>tinper-bee demo</title>
|
||||||
|
<link rel="stylesheet" href="./dist/demo.css">
|
||||||
|
<link rel="stylesheet" href="./demo/atom-one-dark.css">
|
||||||
|
</head>
|
||||||
|
<body style="background: #eceff1">
|
||||||
|
<div id="tinperBeeDemo"></div>
|
||||||
|
<script src="//cdn.jsdelivr.net/highlight.js/9.8.0/highlight.min.js"></script>
|
||||||
|
<script>
|
||||||
|
hljs.initHighlightingOnLoad();
|
||||||
|
</script>
|
||||||
|
<script src="./node_modules/console-polyfill/index.js"></script>
|
||||||
|
<script src="./node_modules/es5-shim/es5-shim.min.js"></script>
|
||||||
|
<script src="./node_modules/es5-shim/es5-sham.min.js"></script>
|
||||||
|
<script src="./node_modules/react/dist/react-with-addons.js"></script>
|
||||||
|
<script src="./node_modules/react-dom/dist/react-dom.js"></script>
|
||||||
|
<script src="./dist/demo.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"name": "bee-table",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Table ui component for react",
|
||||||
|
"keywords": [
|
||||||
|
"react",
|
||||||
|
"react-component",
|
||||||
|
"bee-table",
|
||||||
|
"iuap-design",
|
||||||
|
"tinper-bee",
|
||||||
|
"Table"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/tinper-bee/bee-table.git",
|
||||||
|
"author": "Yonyou FED",
|
||||||
|
"repository": "http://github.com/tinper-bee/bee-table",
|
||||||
|
"bugs": "https://github.com/tinper-bee/bee-table.git/issues",
|
||||||
|
"license": "MIT",
|
||||||
|
|
||||||
|
"main":"./build/index.js",
|
||||||
|
"config":{
|
||||||
|
"port": 3000
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "bee-tools run start",
|
||||||
|
"build": "bee-tools run build",
|
||||||
|
"lint": "bee-tools run lint",
|
||||||
|
"test": "bee-tools run test",
|
||||||
|
"chrome": "bee-tools run chrome",
|
||||||
|
"coveralls": "bee-tools run coverage",
|
||||||
|
"browsers": "bee-tools run browsers",
|
||||||
|
"pub": "bee-tools run pub"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"classnames": "^2.2.5",
|
||||||
|
"tinper-bee-core": "latest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"chai": "^3.5.0",
|
||||||
|
"enzyme": "^2.4.1",
|
||||||
|
"react": "~0.14.0",
|
||||||
|
"react-addons-test-utils": "15.3.2",
|
||||||
|
"react-dom": "~0.14.0",
|
||||||
|
"console-polyfill": "~0.2.1",
|
||||||
|
"es5-shim": "~4.1.10",
|
||||||
|
"bee-panel": "latest",
|
||||||
|
"bee-layout": "latest",
|
||||||
|
"bee-button": "latest"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
export default class Column extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
colSpan: PropTypes.number,
|
||||||
|
title: PropTypes.node,
|
||||||
|
dataIndex: PropTypes.string,
|
||||||
|
width: PropTypes.oneOfType([
|
||||||
|
PropTypes.number,
|
||||||
|
PropTypes.string,
|
||||||
|
]),
|
||||||
|
fixed: PropTypes.oneOf([
|
||||||
|
true,
|
||||||
|
'left',
|
||||||
|
'right',
|
||||||
|
]),
|
||||||
|
render: PropTypes.func,
|
||||||
|
onCellClick: PropTypes.func,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
export default class ColumnGroup extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
title: PropTypes.node,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
import React from 'react';
|
||||||
|
import Column from './Column';
|
||||||
|
import ColumnGroup from './ColumnGroup';
|
||||||
|
|
||||||
|
export default class ColumnManager {
|
||||||
|
_cached = {}
|
||||||
|
|
||||||
|
constructor(columns, elements) {
|
||||||
|
this.columns = columns || this.normalize(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
isAnyColumnsFixed() {
|
||||||
|
return this._cache('isAnyColumnsFixed', () => {
|
||||||
|
return this.columns.some(column => !!column.fixed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isAnyColumnsLeftFixed() {
|
||||||
|
return this._cache('isAnyColumnsLeftFixed', () => {
|
||||||
|
return this.columns.some(
|
||||||
|
column => column.fixed === 'left' || column.fixed === true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isAnyColumnsRightFixed() {
|
||||||
|
return this._cache('isAnyColumnsRightFixed', () => {
|
||||||
|
return this.columns.some(
|
||||||
|
column => column.fixed === 'right'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
leftColumns() {
|
||||||
|
return this._cache('leftColumns', () => {
|
||||||
|
return this.groupedColumns().filter(
|
||||||
|
column => column.fixed === 'left' || column.fixed === true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rightColumns() {
|
||||||
|
return this._cache('rightColumns', () => {
|
||||||
|
return this.groupedColumns().filter(
|
||||||
|
column => column.fixed === 'right'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
leafColumns() {
|
||||||
|
return this._cache('leafColumns', () =>
|
||||||
|
this._leafColumns(this.columns)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
leftLeafColumns() {
|
||||||
|
return this._cache('leftLeafColumns', () =>
|
||||||
|
this._leafColumns(this.leftColumns())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
rightLeafColumns() {
|
||||||
|
return this._cache('rightLeafColumns', () =>
|
||||||
|
this._leafColumns(this.rightColumns())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add appropriate rowspan and colspan to column
|
||||||
|
groupedColumns() {
|
||||||
|
return this._cache('groupedColumns', () => {
|
||||||
|
const _groupColumns = (columns, currentRow = 0, parentColumn = {}, rows = []) => {
|
||||||
|
// track how many rows we got
|
||||||
|
rows[currentRow] = rows[currentRow] || [];
|
||||||
|
const grouped = [];
|
||||||
|
const setRowSpan = column => {
|
||||||
|
const rowSpan = rows.length - currentRow;
|
||||||
|
if (column &&
|
||||||
|
!column.children && // parent columns are supposed to be one row
|
||||||
|
rowSpan > 1 &&
|
||||||
|
(!column.rowSpan || column.rowSpan < rowSpan)
|
||||||
|
) {
|
||||||
|
column.rowSpan = rowSpan;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
columns.forEach((column, index) => {
|
||||||
|
const newColumn = { ...column };
|
||||||
|
rows[currentRow].push(newColumn);
|
||||||
|
parentColumn.colSpan = parentColumn.colSpan || 0;
|
||||||
|
if (newColumn.children && newColumn.children.length > 0) {
|
||||||
|
newColumn.children = _groupColumns(newColumn.children, currentRow + 1, newColumn, rows);
|
||||||
|
parentColumn.colSpan = parentColumn.colSpan + newColumn.colSpan;
|
||||||
|
} else {
|
||||||
|
parentColumn.colSpan++;
|
||||||
|
}
|
||||||
|
// update rowspan to all same row columns
|
||||||
|
for (let i = 0; i < rows[currentRow].length - 1; ++i) {
|
||||||
|
setRowSpan(rows[currentRow][i]);
|
||||||
|
}
|
||||||
|
// last column, update rowspan immediately
|
||||||
|
if (index + 1 === columns.length) {
|
||||||
|
setRowSpan(newColumn);
|
||||||
|
}
|
||||||
|
grouped.push(newColumn);
|
||||||
|
});
|
||||||
|
return grouped;
|
||||||
|
};
|
||||||
|
return _groupColumns(this.columns);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize(elements) {
|
||||||
|
const columns = [];
|
||||||
|
React.Children.forEach(elements, element => {
|
||||||
|
if (!this.isColumnElement(element)) return;
|
||||||
|
const column = { ...element.props };
|
||||||
|
if (element.key) {
|
||||||
|
column.key = element.key;
|
||||||
|
}
|
||||||
|
if (element.type === ColumnGroup) {
|
||||||
|
column.children = this.normalize(column.children);
|
||||||
|
}
|
||||||
|
columns.push(column);
|
||||||
|
});
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
isColumnElement(element) {
|
||||||
|
return element && (element.type === Column || element.type === ColumnGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(columns, elements) {
|
||||||
|
this.columns = columns || this.normalize(elements);
|
||||||
|
this._cached = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache(name, fn) {
|
||||||
|
if (name in this._cached) {
|
||||||
|
return this._cached[name];
|
||||||
|
}
|
||||||
|
this._cached[name] = fn();
|
||||||
|
return this._cached[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
_leafColumns(columns) {
|
||||||
|
const leafColumns = [];
|
||||||
|
columns.forEach(column => {
|
||||||
|
if (!column.children) {
|
||||||
|
leafColumns.push(column);
|
||||||
|
} else {
|
||||||
|
leafColumns.push(...this._leafColumns(column.children));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return leafColumns;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import shallowequal from 'shallowequal';
|
||||||
|
|
||||||
|
const ExpandIcon = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
record: PropTypes.object,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
expandable: PropTypes.any,
|
||||||
|
expanded: PropTypes.bool,
|
||||||
|
needIndentSpaced: PropTypes.bool,
|
||||||
|
onExpand: PropTypes.func,
|
||||||
|
},
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
return !shallowequal(nextProps, this.props);
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
const { expandable, prefixCls, onExpand, needIndentSpaced, expanded, record } = this.props;
|
||||||
|
if (expandable) {
|
||||||
|
const expandClassName = expanded ? 'expanded' : 'collapsed';
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`${prefixCls}-expand-icon ${prefixCls}-${expandClassName}`}
|
||||||
|
onClick={(e) => onExpand(!expanded, record, e)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (needIndentSpaced) {
|
||||||
|
return <span className={`${prefixCls}-expand-icon ${prefixCls}-spaced`} />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ExpandIcon;
|
|
@ -0,0 +1,705 @@
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import TableRow from './TableRow';
|
||||||
|
import TableHeader from './TableHeader';
|
||||||
|
import { measureScrollbar, debounce, warningOnce } from './utils';
|
||||||
|
import shallowequal from 'shallowequal';
|
||||||
|
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||||
|
import ColumnManager from './ColumnManager';
|
||||||
|
import createStore from './createStore';
|
||||||
|
|
||||||
|
const Table = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
data: PropTypes.array,
|
||||||
|
expandIconAsCell: PropTypes.bool,
|
||||||
|
defaultExpandAllRows: PropTypes.bool,
|
||||||
|
expandedRowKeys: PropTypes.array,
|
||||||
|
defaultExpandedRowKeys: PropTypes.array,
|
||||||
|
useFixedHeader: PropTypes.bool,
|
||||||
|
columns: PropTypes.array,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
bodyStyle: PropTypes.object,
|
||||||
|
style: PropTypes.object,
|
||||||
|
rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||||
|
rowClassName: PropTypes.func,
|
||||||
|
expandedRowClassName: PropTypes.func,
|
||||||
|
childrenColumnName: PropTypes.string,
|
||||||
|
onExpand: PropTypes.func,
|
||||||
|
onExpandedRowsChange: PropTypes.func,
|
||||||
|
indentSize: PropTypes.number,
|
||||||
|
onRowClick: PropTypes.func,
|
||||||
|
onRowDoubleClick: PropTypes.func,
|
||||||
|
expandIconColumnIndex: PropTypes.number,
|
||||||
|
showHeader: PropTypes.bool,
|
||||||
|
title: PropTypes.func,
|
||||||
|
footer: PropTypes.func,
|
||||||
|
emptyText: PropTypes.func,
|
||||||
|
scroll: PropTypes.object,
|
||||||
|
rowRef: PropTypes.func,
|
||||||
|
getBodyWrapper: PropTypes.func,
|
||||||
|
children: PropTypes.node,
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
useFixedHeader: false,
|
||||||
|
expandIconAsCell: false,
|
||||||
|
defaultExpandAllRows: false,
|
||||||
|
defaultExpandedRowKeys: [],
|
||||||
|
rowKey: 'key',
|
||||||
|
rowClassName: () => '',
|
||||||
|
expandedRowClassName: () => '',
|
||||||
|
onExpand() {},
|
||||||
|
onExpandedRowsChange() {},
|
||||||
|
onRowClick() {},
|
||||||
|
onRowDoubleClick() {},
|
||||||
|
prefixCls: 'rc-table',
|
||||||
|
bodyStyle: {},
|
||||||
|
style: {},
|
||||||
|
childrenColumnName: 'children',
|
||||||
|
indentSize: 15,
|
||||||
|
expandIconColumnIndex: 0,
|
||||||
|
showHeader: true,
|
||||||
|
scroll: {},
|
||||||
|
rowRef: () => null,
|
||||||
|
getBodyWrapper: body => body,
|
||||||
|
emptyText: () => 'No Data',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
const props = this.props;
|
||||||
|
let expandedRowKeys = [];
|
||||||
|
let rows = [...props.data];
|
||||||
|
this.columnManager = new ColumnManager(props.columns, props.children);
|
||||||
|
this.store = createStore({ currentHoverKey: null });
|
||||||
|
|
||||||
|
if (props.defaultExpandAllRows) {
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
const row = rows[i];
|
||||||
|
expandedRowKeys.push(this.getRowKey(row, i));
|
||||||
|
rows = rows.concat(row[props.childrenColumnName] || []);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expandedRowKeys = props.expandedRowKeys || props.defaultExpandedRowKeys;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
expandedRowKeys,
|
||||||
|
data: props.data,
|
||||||
|
currentHoverKey: null,
|
||||||
|
scrollPosition: 'left',
|
||||||
|
fixedColumnsHeadRowsHeight: [],
|
||||||
|
fixedColumnsBodyRowsHeight: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.resetScrollY();
|
||||||
|
if (this.columnManager.isAnyColumnsFixed()) {
|
||||||
|
this.syncFixedTableRowHeight();
|
||||||
|
this.resizeEvent = addEventListener(
|
||||||
|
window, 'resize', debounce(this.syncFixedTableRowHeight, 150)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if ('data' in nextProps) {
|
||||||
|
this.setState({
|
||||||
|
data: nextProps.data,
|
||||||
|
});
|
||||||
|
if (!nextProps.data || nextProps.data.length === 0) {
|
||||||
|
this.resetScrollY();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ('expandedRowKeys' in nextProps) {
|
||||||
|
this.setState({
|
||||||
|
expandedRowKeys: nextProps.expandedRowKeys,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (nextProps.columns && nextProps.columns !== this.props.columns) {
|
||||||
|
this.columnManager.reset(nextProps.columns);
|
||||||
|
} else if (nextProps.children !== this.props.children) {
|
||||||
|
this.columnManager.reset(null, nextProps.children);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.syncFixedTableRowHeight();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.resizeEvent) {
|
||||||
|
this.resizeEvent.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onExpandedRowsChange(expandedRowKeys) {
|
||||||
|
if (!this.props.expandedRowKeys) {
|
||||||
|
this.setState({ expandedRowKeys });
|
||||||
|
}
|
||||||
|
this.props.onExpandedRowsChange(expandedRowKeys);
|
||||||
|
},
|
||||||
|
|
||||||
|
onExpanded(expanded, record, e, index) {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
const info = this.findExpandedRow(record);
|
||||||
|
if (typeof info !== 'undefined' && !expanded) {
|
||||||
|
this.onRowDestroy(record, index);
|
||||||
|
} else if (!info && expanded) {
|
||||||
|
const expandedRows = this.getExpandedRows().concat();
|
||||||
|
expandedRows.push(this.getRowKey(record, index));
|
||||||
|
this.onExpandedRowsChange(expandedRows);
|
||||||
|
}
|
||||||
|
this.props.onExpand(expanded, record);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRowDestroy(record, rowIndex) {
|
||||||
|
const expandedRows = this.getExpandedRows().concat();
|
||||||
|
const rowKey = this.getRowKey(record, rowIndex);
|
||||||
|
let index = -1;
|
||||||
|
expandedRows.forEach((r, i) => {
|
||||||
|
if (r === rowKey) {
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (index !== -1) {
|
||||||
|
expandedRows.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.onExpandedRowsChange(expandedRows);
|
||||||
|
},
|
||||||
|
|
||||||
|
getRowKey(record, index) {
|
||||||
|
const rowKey = this.props.rowKey;
|
||||||
|
const key = (typeof rowKey === 'function') ?
|
||||||
|
rowKey(record, index) : record[rowKey];
|
||||||
|
warningOnce(
|
||||||
|
key !== undefined,
|
||||||
|
'Each record in table should have a unique `key` prop,' +
|
||||||
|
'or set `rowKey` to an unique primary key.'
|
||||||
|
);
|
||||||
|
return key;
|
||||||
|
},
|
||||||
|
|
||||||
|
getExpandedRows() {
|
||||||
|
return this.props.expandedRowKeys || this.state.expandedRowKeys;
|
||||||
|
},
|
||||||
|
|
||||||
|
getHeader(columns, fixed) {
|
||||||
|
const { showHeader, expandIconAsCell, prefixCls } = this.props;
|
||||||
|
const rows = this.getHeaderRows(columns);
|
||||||
|
|
||||||
|
if (expandIconAsCell && fixed !== 'right') {
|
||||||
|
rows[0].unshift({
|
||||||
|
key: 'rc-table-expandIconAsCell',
|
||||||
|
className: `${prefixCls}-expand-icon-th`,
|
||||||
|
title: '',
|
||||||
|
rowSpan: rows.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const trStyle = fixed ? this.getHeaderRowStyle(columns, rows) : null;
|
||||||
|
|
||||||
|
return showHeader ? (
|
||||||
|
<TableHeader
|
||||||
|
prefixCls={prefixCls}
|
||||||
|
rows={rows}
|
||||||
|
rowStyle={trStyle}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getHeaderRows(columns, currentRow = 0, rows) {
|
||||||
|
rows = rows || [];
|
||||||
|
rows[currentRow] = rows[currentRow] || [];
|
||||||
|
|
||||||
|
columns.forEach(column => {
|
||||||
|
if (column.rowSpan && rows.length < column.rowSpan) {
|
||||||
|
while (rows.length < column.rowSpan) {
|
||||||
|
rows.push([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cell = {
|
||||||
|
key: column.key,
|
||||||
|
className: column.className || '',
|
||||||
|
children: column.title,
|
||||||
|
};
|
||||||
|
if (column.children) {
|
||||||
|
this.getHeaderRows(column.children, currentRow + 1, rows);
|
||||||
|
}
|
||||||
|
if ('colSpan' in column) {
|
||||||
|
cell.colSpan = column.colSpan;
|
||||||
|
}
|
||||||
|
if ('rowSpan' in column) {
|
||||||
|
cell.rowSpan = column.rowSpan;
|
||||||
|
}
|
||||||
|
if (cell.colSpan !== 0) {
|
||||||
|
rows[currentRow].push(cell);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return rows.filter(row => row.length > 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
getExpandedRow(key, content, visible, className, fixed) {
|
||||||
|
const { prefixCls, expandIconAsCell } = this.props;
|
||||||
|
let colCount;
|
||||||
|
if (fixed === 'left') {
|
||||||
|
colCount = this.columnManager.leftLeafColumns().length;
|
||||||
|
} else if (fixed === 'right') {
|
||||||
|
colCount = this.columnManager.rightLeafColumns().length;
|
||||||
|
} else {
|
||||||
|
colCount = this.columnManager.leafColumns().length;
|
||||||
|
}
|
||||||
|
const columns = [{
|
||||||
|
key: 'extra-row',
|
||||||
|
render: () => ({
|
||||||
|
props: {
|
||||||
|
colSpan: colCount,
|
||||||
|
},
|
||||||
|
children: fixed !== 'right' ? content : ' ',
|
||||||
|
}),
|
||||||
|
}];
|
||||||
|
if (expandIconAsCell && fixed !== 'right') {
|
||||||
|
columns.unshift({
|
||||||
|
key: 'expand-icon-placeholder',
|
||||||
|
render: () => null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
columns={columns}
|
||||||
|
visible={visible}
|
||||||
|
className={className}
|
||||||
|
key={`${key}-extra-row`}
|
||||||
|
prefixCls={`${prefixCls}-expanded-row`}
|
||||||
|
indent={1}
|
||||||
|
expandable={false}
|
||||||
|
store={this.store}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getRowsByData(data, visible, indent, columns, fixed) {
|
||||||
|
const props = this.props;
|
||||||
|
const childrenColumnName = props.childrenColumnName;
|
||||||
|
const expandedRowRender = props.expandedRowRender;
|
||||||
|
const expandRowByClick = props.expandRowByClick;
|
||||||
|
const { fixedColumnsBodyRowsHeight } = this.state;
|
||||||
|
let rst = [];
|
||||||
|
const rowClassName = props.rowClassName;
|
||||||
|
const rowRef = props.rowRef;
|
||||||
|
const expandedRowClassName = props.expandedRowClassName;
|
||||||
|
const needIndentSpaced = props.data.some(record => record[childrenColumnName]);
|
||||||
|
const onRowClick = props.onRowClick;
|
||||||
|
const onRowDoubleClick = props.onRowDoubleClick;
|
||||||
|
|
||||||
|
const expandIconAsCell = fixed !== 'right' ? props.expandIconAsCell : false;
|
||||||
|
const expandIconColumnIndex = fixed !== 'right' ? props.expandIconColumnIndex : -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const record = data[i];
|
||||||
|
const key = this.getRowKey(record, i);
|
||||||
|
const childrenColumn = record[childrenColumnName];
|
||||||
|
const isRowExpanded = this.isRowExpanded(record, i);
|
||||||
|
let expandedRowContent;
|
||||||
|
if (expandedRowRender && isRowExpanded) {
|
||||||
|
expandedRowContent = expandedRowRender(record, i, indent);
|
||||||
|
}
|
||||||
|
const className = rowClassName(record, i, indent);
|
||||||
|
|
||||||
|
const onHoverProps = {};
|
||||||
|
if (this.columnManager.isAnyColumnsFixed()) {
|
||||||
|
onHoverProps.onHover = this.handleRowHover;
|
||||||
|
}
|
||||||
|
|
||||||
|
const height = (fixed && fixedColumnsBodyRowsHeight[i]) ?
|
||||||
|
fixedColumnsBodyRowsHeight[i] : null;
|
||||||
|
|
||||||
|
|
||||||
|
let leafColumns;
|
||||||
|
if (fixed === 'left') {
|
||||||
|
leafColumns = this.columnManager.leftLeafColumns();
|
||||||
|
} else if (fixed === 'right') {
|
||||||
|
leafColumns = this.columnManager.rightLeafColumns();
|
||||||
|
} else {
|
||||||
|
leafColumns = this.columnManager.leafColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
rst.push(
|
||||||
|
<TableRow
|
||||||
|
indent={indent}
|
||||||
|
indentSize={props.indentSize}
|
||||||
|
needIndentSpaced={needIndentSpaced}
|
||||||
|
className={className}
|
||||||
|
record={record}
|
||||||
|
expandIconAsCell={expandIconAsCell}
|
||||||
|
onDestroy={this.onRowDestroy}
|
||||||
|
index={i}
|
||||||
|
visible={visible}
|
||||||
|
expandRowByClick={expandRowByClick}
|
||||||
|
onExpand={this.onExpanded}
|
||||||
|
expandable={childrenColumn || expandedRowRender}
|
||||||
|
expanded={isRowExpanded}
|
||||||
|
prefixCls={`${props.prefixCls}-row`}
|
||||||
|
childrenColumnName={childrenColumnName}
|
||||||
|
columns={leafColumns}
|
||||||
|
expandIconColumnIndex={expandIconColumnIndex}
|
||||||
|
onRowClick={onRowClick}
|
||||||
|
onRowDoubleClick={onRowDoubleClick}
|
||||||
|
height={height}
|
||||||
|
{...onHoverProps}
|
||||||
|
key={key}
|
||||||
|
hoverKey={key}
|
||||||
|
ref={rowRef(record, i, indent)}
|
||||||
|
store={this.store}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const subVisible = visible && isRowExpanded;
|
||||||
|
|
||||||
|
if (expandedRowContent && isRowExpanded) {
|
||||||
|
rst.push(this.getExpandedRow(
|
||||||
|
key, expandedRowContent, subVisible, expandedRowClassName(record, i, indent), fixed
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (childrenColumn) {
|
||||||
|
rst = rst.concat(this.getRowsByData(
|
||||||
|
childrenColumn, subVisible, indent + 1, columns, fixed
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rst;
|
||||||
|
},
|
||||||
|
|
||||||
|
getRows(columns, fixed) {
|
||||||
|
return this.getRowsByData(this.state.data, true, 0, columns, fixed);
|
||||||
|
},
|
||||||
|
|
||||||
|
getColGroup(columns, fixed) {
|
||||||
|
let cols = [];
|
||||||
|
if (this.props.expandIconAsCell && fixed !== 'right') {
|
||||||
|
cols.push(
|
||||||
|
<col
|
||||||
|
className={`${this.props.prefixCls}-expand-icon-col`}
|
||||||
|
key="rc-table-expand-icon-col"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let leafColumns;
|
||||||
|
if (fixed === 'left') {
|
||||||
|
leafColumns = this.columnManager.leftLeafColumns();
|
||||||
|
} else if (fixed === 'right') {
|
||||||
|
leafColumns = this.columnManager.rightLeafColumns();
|
||||||
|
} else {
|
||||||
|
leafColumns = this.columnManager.leafColumns();
|
||||||
|
}
|
||||||
|
cols = cols.concat(leafColumns.map(c => {
|
||||||
|
return <col key={c.key} style={{ width: c.width, minWidth: c.width }} />;
|
||||||
|
}));
|
||||||
|
return <colgroup>{cols}</colgroup>;
|
||||||
|
},
|
||||||
|
|
||||||
|
getLeftFixedTable() {
|
||||||
|
return this.getTable({
|
||||||
|
columns: this.columnManager.leftColumns(),
|
||||||
|
fixed: 'left',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getRightFixedTable() {
|
||||||
|
return this.getTable({
|
||||||
|
columns: this.columnManager.rightColumns(),
|
||||||
|
fixed: 'right',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getTable(options = {}) {
|
||||||
|
const { columns, fixed } = options;
|
||||||
|
const { prefixCls, scroll = {}, getBodyWrapper } = this.props;
|
||||||
|
let { useFixedHeader } = this.props;
|
||||||
|
const bodyStyle = { ...this.props.bodyStyle };
|
||||||
|
const headStyle = {};
|
||||||
|
|
||||||
|
let tableClassName = '';
|
||||||
|
if (scroll.x || fixed) {
|
||||||
|
tableClassName = `${prefixCls}-fixed`;
|
||||||
|
bodyStyle.overflowX = bodyStyle.overflowX || 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scroll.y) {
|
||||||
|
// maxHeight will make fixed-Table scrolling not working
|
||||||
|
// so we only set maxHeight to body-Table here
|
||||||
|
if (fixed) {
|
||||||
|
bodyStyle.height = bodyStyle.height || scroll.y;
|
||||||
|
} else {
|
||||||
|
bodyStyle.maxHeight = bodyStyle.maxHeight || scroll.y;
|
||||||
|
}
|
||||||
|
bodyStyle.overflowY = bodyStyle.overflowY || 'scroll';
|
||||||
|
useFixedHeader = true;
|
||||||
|
|
||||||
|
// Add negative margin bottom for scroll bar overflow bug
|
||||||
|
const scrollbarWidth = measureScrollbar();
|
||||||
|
if (scrollbarWidth > 0) {
|
||||||
|
(fixed ? bodyStyle : headStyle).marginBottom = `-${scrollbarWidth}px`;
|
||||||
|
(fixed ? bodyStyle : headStyle).paddingBottom = '0px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderTable = (hasHead = true, hasBody = true) => {
|
||||||
|
const tableStyle = {};
|
||||||
|
if (!fixed && scroll.x) {
|
||||||
|
// not set width, then use content fixed width
|
||||||
|
if (scroll.x === true) {
|
||||||
|
tableStyle.tableLayout = 'fixed';
|
||||||
|
} else {
|
||||||
|
tableStyle.width = scroll.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tableBody = hasBody ? getBodyWrapper(
|
||||||
|
<tbody className={`${prefixCls}-tbody`}>
|
||||||
|
{this.getRows(columns, fixed)}
|
||||||
|
</tbody>
|
||||||
|
) : null;
|
||||||
|
return (
|
||||||
|
<table className={tableClassName} style={tableStyle}>
|
||||||
|
{this.getColGroup(columns, fixed)}
|
||||||
|
{hasHead ? this.getHeader(columns, fixed) : null}
|
||||||
|
{tableBody}
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let headTable;
|
||||||
|
|
||||||
|
if (useFixedHeader) {
|
||||||
|
headTable = (
|
||||||
|
<div
|
||||||
|
className={`${prefixCls}-header`}
|
||||||
|
ref={fixed ? null : 'headTable'}
|
||||||
|
style={headStyle}
|
||||||
|
onMouseOver={this.detectScrollTarget}
|
||||||
|
onTouchStart={this.detectScrollTarget}
|
||||||
|
onScroll={this.handleBodyScroll}
|
||||||
|
>
|
||||||
|
{renderTable(true, false)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let BodyTable = (
|
||||||
|
<div
|
||||||
|
className={`${prefixCls}-body`}
|
||||||
|
style={bodyStyle}
|
||||||
|
ref="bodyTable"
|
||||||
|
onMouseOver={this.detectScrollTarget}
|
||||||
|
onTouchStart={this.detectScrollTarget}
|
||||||
|
onScroll={this.handleBodyScroll}
|
||||||
|
>
|
||||||
|
{renderTable(!useFixedHeader)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fixed && columns.length) {
|
||||||
|
let refName;
|
||||||
|
if (columns[0].fixed === 'left' || columns[0].fixed === true) {
|
||||||
|
refName = 'fixedColumnsBodyLeft';
|
||||||
|
} else if (columns[0].fixed === 'right') {
|
||||||
|
refName = 'fixedColumnsBodyRight';
|
||||||
|
}
|
||||||
|
delete bodyStyle.overflowX;
|
||||||
|
delete bodyStyle.overflowY;
|
||||||
|
BodyTable = (
|
||||||
|
<div
|
||||||
|
className={`${prefixCls}-body-outer`}
|
||||||
|
style={{ ...bodyStyle }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`${prefixCls}-body-inner`}
|
||||||
|
ref={refName}
|
||||||
|
onMouseOver={this.detectScrollTarget}
|
||||||
|
onTouchStart={this.detectScrollTarget}
|
||||||
|
onScroll={this.handleBodyScroll}
|
||||||
|
>
|
||||||
|
{renderTable(!useFixedHeader)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <span>{headTable}{BodyTable}</span>;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTitle() {
|
||||||
|
const { title, prefixCls } = this.props;
|
||||||
|
return title ? (
|
||||||
|
<div className={`${prefixCls}-title`}>
|
||||||
|
{title(this.state.data)}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getFooter() {
|
||||||
|
const { footer, prefixCls } = this.props;
|
||||||
|
return footer ? (
|
||||||
|
<div className={`${prefixCls}-footer`}>
|
||||||
|
{footer(this.state.data)}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEmptyText() {
|
||||||
|
const { emptyText, prefixCls, data } = this.props;
|
||||||
|
return !data.length ? (
|
||||||
|
<div className={`${prefixCls}-placeholder`}>
|
||||||
|
{emptyText()}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getHeaderRowStyle(columns, rows) {
|
||||||
|
const { fixedColumnsHeadRowsHeight } = this.state;
|
||||||
|
const headerHeight = fixedColumnsHeadRowsHeight[0];
|
||||||
|
if (headerHeight && columns) {
|
||||||
|
if (headerHeight === 'auto') {
|
||||||
|
return { height: 'auto' };
|
||||||
|
}
|
||||||
|
return { height: headerHeight / rows.length };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
syncFixedTableRowHeight() {
|
||||||
|
const { prefixCls } = this.props;
|
||||||
|
const headRows = this.refs.headTable ?
|
||||||
|
this.refs.headTable.querySelectorAll('thead') :
|
||||||
|
this.refs.bodyTable.querySelectorAll('thead');
|
||||||
|
const bodyRows = this.refs.bodyTable.querySelectorAll(`.${prefixCls}-row`) || [];
|
||||||
|
const fixedColumnsHeadRowsHeight = [].map.call(
|
||||||
|
headRows, row => row.getBoundingClientRect().height || 'auto'
|
||||||
|
);
|
||||||
|
const fixedColumnsBodyRowsHeight = [].map.call(
|
||||||
|
bodyRows, row => row.getBoundingClientRect().height || 'auto'
|
||||||
|
);
|
||||||
|
if (shallowequal(this.state.fixedColumnsHeadRowsHeight, fixedColumnsHeadRowsHeight) &&
|
||||||
|
shallowequal(this.state.fixedColumnsBodyRowsHeight, fixedColumnsBodyRowsHeight)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
fixedColumnsHeadRowsHeight,
|
||||||
|
fixedColumnsBodyRowsHeight,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
resetScrollY() {
|
||||||
|
if (this.refs.headTable) {
|
||||||
|
this.refs.headTable.scrollLeft = 0;
|
||||||
|
}
|
||||||
|
if (this.refs.bodyTable) {
|
||||||
|
this.refs.bodyTable.scrollLeft = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
findExpandedRow(record, index) {
|
||||||
|
const rows = this.getExpandedRows().filter(i => i === this.getRowKey(record, index));
|
||||||
|
return rows[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
isRowExpanded(record, index) {
|
||||||
|
return typeof this.findExpandedRow(record, index) !== 'undefined';
|
||||||
|
},
|
||||||
|
|
||||||
|
detectScrollTarget(e) {
|
||||||
|
if (this.scrollTarget !== e.currentTarget) {
|
||||||
|
this.scrollTarget = e.currentTarget;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleBodyScroll(e) {
|
||||||
|
// Prevent scrollTop setter trigger onScroll event
|
||||||
|
// http://stackoverflow.com/q/1386696
|
||||||
|
if (e.target !== this.scrollTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { scroll = {} } = this.props;
|
||||||
|
const { headTable, bodyTable, fixedColumnsBodyLeft, fixedColumnsBodyRight } = this.refs;
|
||||||
|
if (scroll.x && e.target.scrollLeft !== this.lastScrollLeft) {
|
||||||
|
if (e.target === bodyTable && headTable) {
|
||||||
|
headTable.scrollLeft = e.target.scrollLeft;
|
||||||
|
} else if (e.target === headTable && bodyTable) {
|
||||||
|
bodyTable.scrollLeft = e.target.scrollLeft;
|
||||||
|
}
|
||||||
|
if (e.target.scrollLeft === 0) {
|
||||||
|
this.setState({ scrollPosition: 'left' });
|
||||||
|
} else if (e.target.scrollLeft + 1 >=
|
||||||
|
e.target.children[0].getBoundingClientRect().width -
|
||||||
|
e.target.getBoundingClientRect().width) {
|
||||||
|
this.setState({ scrollPosition: 'right' });
|
||||||
|
} else if (this.state.scrollPosition !== 'middle') {
|
||||||
|
this.setState({ scrollPosition: 'middle' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scroll.y) {
|
||||||
|
if (fixedColumnsBodyLeft && e.target !== fixedColumnsBodyLeft) {
|
||||||
|
fixedColumnsBodyLeft.scrollTop = e.target.scrollTop;
|
||||||
|
}
|
||||||
|
if (fixedColumnsBodyRight && e.target !== fixedColumnsBodyRight) {
|
||||||
|
fixedColumnsBodyRight.scrollTop = e.target.scrollTop;
|
||||||
|
}
|
||||||
|
if (bodyTable && e.target !== bodyTable) {
|
||||||
|
bodyTable.scrollTop = e.target.scrollTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remember last scrollLeft for scroll direction detecting.
|
||||||
|
this.lastScrollLeft = e.target.scrollLeft;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleRowHover(isHover, key) {
|
||||||
|
this.store.setState({
|
||||||
|
currentHoverKey: isHover ? key : null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const props = this.props;
|
||||||
|
const prefixCls = props.prefixCls;
|
||||||
|
|
||||||
|
let className = props.prefixCls;
|
||||||
|
if (props.className) {
|
||||||
|
className += ` ${props.className}`;
|
||||||
|
}
|
||||||
|
if (props.useFixedHeader || (props.scroll && props.scroll.y)) {
|
||||||
|
className += ` ${prefixCls}-fixed-header`;
|
||||||
|
}
|
||||||
|
className += ` ${prefixCls}-scroll-position-${this.state.scrollPosition}`;
|
||||||
|
|
||||||
|
const isTableScroll = this.columnManager.isAnyColumnsFixed() ||
|
||||||
|
props.scroll.x ||
|
||||||
|
props.scroll.y;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className} style={props.style}>
|
||||||
|
{this.getTitle()}
|
||||||
|
<div className={`${prefixCls}-content`}>
|
||||||
|
{this.columnManager.isAnyColumnsLeftFixed() &&
|
||||||
|
<div className={`${prefixCls}-fixed-left`}>
|
||||||
|
{this.getLeftFixedTable()}
|
||||||
|
</div>}
|
||||||
|
<div className={isTableScroll ? `${prefixCls}-scroll` : ''}>
|
||||||
|
{this.getTable({ columns: this.columnManager.groupedColumns() })}
|
||||||
|
{this.getEmptyText()}
|
||||||
|
{this.getFooter()}
|
||||||
|
</div>
|
||||||
|
{this.columnManager.isAnyColumnsRightFixed() &&
|
||||||
|
<div className={`${prefixCls}-fixed-right`}>
|
||||||
|
{this.getRightFixedTable()}
|
||||||
|
</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Table;
|
|
@ -0,0 +1,74 @@
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import objectPath from 'object-path';
|
||||||
|
|
||||||
|
const TableCell = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
record: PropTypes.object,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
index: PropTypes.number,
|
||||||
|
indent: PropTypes.number,
|
||||||
|
indentSize: PropTypes.number,
|
||||||
|
column: PropTypes.object,
|
||||||
|
expandIcon: PropTypes.node,
|
||||||
|
},
|
||||||
|
isInvalidRenderCellText(text) {
|
||||||
|
return text && !React.isValidElement(text) &&
|
||||||
|
Object.prototype.toString.call(text) === '[object Object]';
|
||||||
|
},
|
||||||
|
handleClick(e) {
|
||||||
|
const { record, column: { onCellClick } } = this.props;
|
||||||
|
if (onCellClick) {
|
||||||
|
onCellClick(record, e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
const { record, indentSize, prefixCls, indent,
|
||||||
|
index, expandIcon, column } = this.props;
|
||||||
|
const { dataIndex, render, className = '' } = column;
|
||||||
|
|
||||||
|
let text = objectPath.get(record, dataIndex);
|
||||||
|
let tdProps;
|
||||||
|
let colSpan;
|
||||||
|
let rowSpan;
|
||||||
|
|
||||||
|
if (render) {
|
||||||
|
text = render(text, record, index);
|
||||||
|
if (this.isInvalidRenderCellText(text)) {
|
||||||
|
tdProps = text.props || {};
|
||||||
|
rowSpan = tdProps.rowSpan;
|
||||||
|
colSpan = tdProps.colSpan;
|
||||||
|
text = text.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix https://github.com/ant-design/ant-design/issues/1202
|
||||||
|
if (this.isInvalidRenderCellText(text)) {
|
||||||
|
text = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const indentText = expandIcon ? (
|
||||||
|
<span
|
||||||
|
style={{ paddingLeft: `${indentSize * indent}px` }}
|
||||||
|
className={`${prefixCls}-indent indent-level-${indent}`}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
if (rowSpan === 0 || colSpan === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<td
|
||||||
|
colSpan={colSpan}
|
||||||
|
rowSpan={rowSpan}
|
||||||
|
className={className}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
>
|
||||||
|
{indentText}
|
||||||
|
{expandIcon}
|
||||||
|
{text}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default TableCell;
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import shallowequal from 'shallowequal';
|
||||||
|
|
||||||
|
export default React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
rowStyle: PropTypes.object,
|
||||||
|
rows: PropTypes.array,
|
||||||
|
},
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
return !shallowequal(nextProps, this.props);
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
const { prefixCls, rowStyle, rows } = this.props;
|
||||||
|
return (
|
||||||
|
<thead className={`${prefixCls}-thead`}>
|
||||||
|
{
|
||||||
|
rows.map((row, index) => (
|
||||||
|
<tr key={index} style={rowStyle}>
|
||||||
|
{row.map((cellProps, i) => <th {...cellProps} key={i} />)}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</thead>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,173 @@
|
||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import TableCell from './TableCell';
|
||||||
|
import ExpandIcon from './ExpandIcon';
|
||||||
|
|
||||||
|
const TableRow = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
onDestroy: PropTypes.func,
|
||||||
|
onRowClick: PropTypes.func,
|
||||||
|
onRowDoubleClick: PropTypes.func,
|
||||||
|
record: PropTypes.object,
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
expandIconColumnIndex: PropTypes.number,
|
||||||
|
onHover: PropTypes.func,
|
||||||
|
columns: PropTypes.array,
|
||||||
|
height: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.number,
|
||||||
|
]),
|
||||||
|
visible: PropTypes.bool,
|
||||||
|
index: PropTypes.number,
|
||||||
|
hoverKey: PropTypes.any,
|
||||||
|
expanded: PropTypes.bool,
|
||||||
|
expandable: PropTypes.any,
|
||||||
|
onExpand: PropTypes.func,
|
||||||
|
needIndentSpaced: PropTypes.bool,
|
||||||
|
className: PropTypes.string,
|
||||||
|
indent: PropTypes.number,
|
||||||
|
indentSize: PropTypes.number,
|
||||||
|
expandIconAsCell: PropTypes.bool,
|
||||||
|
expandRowByClick: PropTypes.bool,
|
||||||
|
store: PropTypes.object.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps() {
|
||||||
|
return {
|
||||||
|
onRowClick() {},
|
||||||
|
onRowDoubleClick() {},
|
||||||
|
onDestroy() {},
|
||||||
|
expandIconColumnIndex: 0,
|
||||||
|
expandRowByClick: false,
|
||||||
|
onHover() {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
hovered: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { store, hoverKey } = this.props;
|
||||||
|
this.unsubscribe = store.subscribe(() => {
|
||||||
|
if (store.getState().currentHoverKey === hoverKey) {
|
||||||
|
this.setState({ hovered: true });
|
||||||
|
} else if (this.state.hovered === true) {
|
||||||
|
this.setState({ hovered: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { record, onDestroy, index } = this.props;
|
||||||
|
onDestroy(record, index);
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRowClick(event) {
|
||||||
|
const {
|
||||||
|
record,
|
||||||
|
index,
|
||||||
|
onRowClick,
|
||||||
|
expandable,
|
||||||
|
expandRowByClick,
|
||||||
|
expanded,
|
||||||
|
onExpand,
|
||||||
|
} = this.props;
|
||||||
|
if (expandable && expandRowByClick) {
|
||||||
|
onExpand(!expanded, record, index);
|
||||||
|
}
|
||||||
|
onRowClick(record, index, event);
|
||||||
|
},
|
||||||
|
|
||||||
|
onRowDoubleClick(event) {
|
||||||
|
const { record, index, onRowDoubleClick } = this.props;
|
||||||
|
onRowDoubleClick(record, index, event);
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseEnter() {
|
||||||
|
const { onHover, hoverKey } = this.props;
|
||||||
|
onHover(true, hoverKey);
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseLeave() {
|
||||||
|
const { onHover, hoverKey } = this.props;
|
||||||
|
onHover(false, hoverKey);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
prefixCls, columns, record, height, visible, index,
|
||||||
|
expandIconColumnIndex, expandIconAsCell, expanded, expandRowByClick,
|
||||||
|
expandable, onExpand, needIndentSpaced, indent, indentSize,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let { className } = this.props;
|
||||||
|
|
||||||
|
if (this.state.hovered) {
|
||||||
|
className += ` ${prefixCls}-hover`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cells = [];
|
||||||
|
|
||||||
|
const expandIcon = (
|
||||||
|
<ExpandIcon
|
||||||
|
expandable={expandable}
|
||||||
|
prefixCls={prefixCls}
|
||||||
|
onExpand={onExpand}
|
||||||
|
needIndentSpaced={needIndentSpaced}
|
||||||
|
expanded={expanded}
|
||||||
|
record={record}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < columns.length; i++) {
|
||||||
|
if (expandIconAsCell && i === 0) {
|
||||||
|
cells.push(
|
||||||
|
<td
|
||||||
|
className={`${prefixCls}-expand-icon-cell`}
|
||||||
|
key="rc-table-expand-icon-cell"
|
||||||
|
>
|
||||||
|
{expandIcon}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const isColumnHaveExpandIcon = (expandIconAsCell || expandRowByClick)
|
||||||
|
? false : (i === expandIconColumnIndex);
|
||||||
|
cells.push(
|
||||||
|
<TableCell
|
||||||
|
prefixCls={prefixCls}
|
||||||
|
record={record}
|
||||||
|
indentSize={indentSize}
|
||||||
|
indent={indent}
|
||||||
|
index={index}
|
||||||
|
column={columns[i]}
|
||||||
|
key={columns[i].key}
|
||||||
|
expandIcon={isColumnHaveExpandIcon ? expandIcon : null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const style = { height };
|
||||||
|
if (!visible) {
|
||||||
|
style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
onClick={this.onRowClick}
|
||||||
|
onDoubleClick={this.onRowDoubleClick}
|
||||||
|
onMouseEnter={this.onMouseEnter}
|
||||||
|
onMouseLeave={this.onMouseLeave}
|
||||||
|
className={`${prefixCls} ${className} ${prefixCls}-level-${indent}`}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
{cells}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default TableRow;
|
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
.move-enter, .move-appear {
|
||||||
|
opacity: 0;
|
||||||
|
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||||
|
animation-duration: 2.5s;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
.move-leave {
|
||||||
|
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||||
|
animation-duration: .5s;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
.move-enter.move-enter-active, .move-appear.move-enter-active {
|
||||||
|
animation-name: moveLeftIn;
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
|
|
||||||
|
.move-leave.move-leave-active {
|
||||||
|
animation-name: moveRightOut;
|
||||||
|
animation-play-state: running;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes moveLeftIn {
|
||||||
|
0% {
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: translateX(30px);
|
||||||
|
opacity: 0;
|
||||||
|
background: #fff6de;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
80%{
|
||||||
|
background: #fff6de;
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
background: transparent;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes moveRightOut {
|
||||||
|
0% {
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: translateX(-30px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
@tablePrefixCls: rc-table;
|
||||||
|
@table-border-color: #e9e9e9;
|
||||||
|
|
||||||
|
.@{tablePrefixCls}.bordered {
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid @table-border-color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
export default function createStore(initialState) {
|
||||||
|
let state = initialState;
|
||||||
|
const listeners = [];
|
||||||
|
|
||||||
|
function setState(partial) {
|
||||||
|
state = { ...state, ...partial };
|
||||||
|
for (let i = 0; i < listeners.length; i++) {
|
||||||
|
listeners[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscribe(listener) {
|
||||||
|
listeners.push(listener);
|
||||||
|
|
||||||
|
return function unsubscribe() {
|
||||||
|
const index = listeners.indexOf(listener);
|
||||||
|
listeners.splice(index, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setState,
|
||||||
|
getState,
|
||||||
|
subscribe,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
const Table = require('./Table');
|
||||||
|
const Column = require('./Column');
|
||||||
|
const ColumnGroup = require('./ColumnGroup');
|
||||||
|
|
||||||
|
Table.Column = Column;
|
||||||
|
Table.ColumnGroup = ColumnGroup;
|
||||||
|
|
||||||
|
module.exports = Table;
|
|
@ -0,0 +1,210 @@
|
||||||
|
@tablePrefixCls: rc-table;
|
||||||
|
@text-color : #666;
|
||||||
|
@font-size-base : 12px;
|
||||||
|
@line-height: 1.5;
|
||||||
|
@table-border-color: #e9e9e9;
|
||||||
|
@table-head-background-color: #f7f7f7;
|
||||||
|
@vertical-padding: 16px;
|
||||||
|
@horizontal-padding: 8px;
|
||||||
|
|
||||||
|
.@{tablePrefixCls} {
|
||||||
|
font-size: @font-size-base;
|
||||||
|
color: @text-color;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
line-height: @line-height;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.@{tablePrefixCls}-scroll {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tablePrefixCls}-header {
|
||||||
|
overflow: hidden;
|
||||||
|
background: @table-head-background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-fixed-header &-body {
|
||||||
|
background: #fff;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-fixed-header &-body-inner {
|
||||||
|
height: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-fixed-header &-scroll &-header {
|
||||||
|
overflow-x: scroll;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
margin-bottom: -20px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tablePrefixCls}-title {
|
||||||
|
padding: @vertical-padding @horizontal-padding;
|
||||||
|
border-top: 1px solid @table-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tablePrefixCls}-content {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tablePrefixCls}-footer {
|
||||||
|
padding: @vertical-padding @horizontal-padding;
|
||||||
|
border-bottom: 1px solid @table-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tablePrefixCls}-placeholder {
|
||||||
|
padding: 16px 8px;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1px solid @table-border-color;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: separate;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: @table-head-background-color;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background .3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border-bottom: 1px solid @table-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
transition: all .3s ease;
|
||||||
|
&:hover {
|
||||||
|
background: #eaf8fe;
|
||||||
|
}
|
||||||
|
&.@{tablePrefixCls}-row-hover {
|
||||||
|
background: #eaf8fe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: @vertical-padding @horizontal-padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{tablePrefixCls} {
|
||||||
|
&-expand-icon-col {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
&-row, &-expanded-row {
|
||||||
|
&-expand-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
border: 1px solid @table-border-color;
|
||||||
|
user-select: none;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
&-spaced {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
&-spaced:after {
|
||||||
|
content: '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
&-expanded:after {
|
||||||
|
content: '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
&-collapsed:after {
|
||||||
|
content: '+'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr&-expanded-row {
|
||||||
|
background: #f7f7f7;
|
||||||
|
&:hover {
|
||||||
|
background: #f7f7f7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-column-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&-prev-columns-page,
|
||||||
|
&-next-columns-page {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #666;
|
||||||
|
z-index: 1;
|
||||||
|
&:hover {
|
||||||
|
color: #2db7f5;
|
||||||
|
}
|
||||||
|
&-disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
color: #999;
|
||||||
|
&:hover {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-prev-columns-page {
|
||||||
|
margin-right: 8px;
|
||||||
|
&:before {
|
||||||
|
content: '<';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-next-columns-page {
|
||||||
|
float: right;
|
||||||
|
&:before {
|
||||||
|
content: '>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-fixed-left,
|
||||||
|
&-fixed-right {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
table {
|
||||||
|
width: auto;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-fixed-left {
|
||||||
|
left: 0;
|
||||||
|
box-shadow: 4px 0 4px rgba(100, 100, 100, 0.1);
|
||||||
|
& .@{tablePrefixCls}-body-inner {
|
||||||
|
margin-right: -20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
.@{tablePrefixCls}-fixed-header & .@{tablePrefixCls}-body-inner {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-fixed-right {
|
||||||
|
right: 0;
|
||||||
|
box-shadow: -4px 0 4px rgba(100, 100, 100, 0.1);
|
||||||
|
|
||||||
|
// hide expand row content in right fixed Table
|
||||||
|
// https://github.com/ant-design/ant-design/issues/1898
|
||||||
|
.@{tablePrefixCls}-expanded-row {
|
||||||
|
color: transparent;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&&-scroll-position-left &-fixed-left {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&&-scroll-position-right &-fixed-right {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import warning from 'warning';
|
||||||
|
|
||||||
|
let scrollbarWidth;
|
||||||
|
|
||||||
|
// Measure scrollbar width for padding body during modal show/hide
|
||||||
|
const scrollbarMeasure = {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '-9999px',
|
||||||
|
width: '50px',
|
||||||
|
height: '50px',
|
||||||
|
overflow: 'scroll',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function measureScrollbar() {
|
||||||
|
if (typeof document === 'undefined' || typeof window === 'undefined') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (scrollbarWidth) {
|
||||||
|
return scrollbarWidth;
|
||||||
|
}
|
||||||
|
const scrollDiv = document.createElement('div');
|
||||||
|
for (const scrollProp in scrollbarMeasure) {
|
||||||
|
if (scrollbarMeasure.hasOwnProperty(scrollProp)) {
|
||||||
|
scrollDiv.style[scrollProp] = scrollbarMeasure[scrollProp];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.body.appendChild(scrollDiv);
|
||||||
|
const width = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
||||||
|
document.body.removeChild(scrollDiv);
|
||||||
|
scrollbarWidth = width;
|
||||||
|
return scrollbarWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debounce(func, wait, immediate) {
|
||||||
|
let timeout;
|
||||||
|
return function debounceFunc() {
|
||||||
|
const context = this;
|
||||||
|
const args = arguments;
|
||||||
|
// https://fb.me/react-event-pooling
|
||||||
|
if (args[0] && args[0].persist) {
|
||||||
|
args[0].persist();
|
||||||
|
}
|
||||||
|
const later = () => {
|
||||||
|
timeout = null;
|
||||||
|
if (!immediate) {
|
||||||
|
func.apply(context, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const callNow = immediate && !timeout;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
if (callNow) {
|
||||||
|
func.apply(context, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const warned = {};
|
||||||
|
export function warningOnce(condition, format, args) {
|
||||||
|
if (!warned[format]) {
|
||||||
|
warning(condition, format, args);
|
||||||
|
warned[format] = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {shallow, mount, render} from 'enzyme';
|
||||||
|
import {expect} from 'chai';
|
||||||
|
import Table from '../src/index';
|
Loading…
Reference in New Issue