Compare commits

...

11 Commits

Author SHA1 Message Date
偏右 c6f4e1ad0f Update CHANGELOG.md 8 years ago
Benjy Cui 4178b0afd8 bump 1.11.6, close: #4580 8 years ago
Benjy Cui 640522cb73 bump 1.11.5 8 years ago
Justin 16ce494f92 fix: controlled filterDropdown props in antd@1.x (#4470) 8 years ago
afc163 8fdace7173 update 1.x changelog for 1.11.3 and 1.11.4 8 years ago
yiminghe f7933cd2b6 fix pub 8 years ago
Benjy Cui 44fd5990f6 bump 1.11.4 8 years ago
Benjy Cui d009cf0b76 fix: Upload should work without children 8 years ago
Benjy Cui 51b74b7daa bump 1.11.3 8 years ago
Justin 635bc8cedc fix: getComputedStyle for IE8 (#3965) 8 years ago
Midqiu ff59663898 docs: missing comma (#3172) 8 years ago
  1. 20
      CHANGELOG.md
  2. 2
      components/form/index.md
  3. 33
      components/input/calculateNodeHeight.js
  4. 86
      components/table/Table.jsx
  5. 123
      components/table/demo/custom-filter-panel.md
  6. 74
      components/table/filterDropdown.jsx
  7. 2
      components/table/index.md
  8. 6
      components/table/style/index.less
  9. 8
      components/upload/index.jsx
  10. 4
      package.json

20
CHANGELOG.md

@ -5,10 +5,28 @@ toc: false
timeline: true
---
如果需要查看 `0.12.x` 及之前的更新日志,请移步 [GitHub](https://github.com/ant-design/ant-design/releases?after=1.0.0)。
如果需要查看 `0.12.x` 及之前的更新日志,请移步 [GitHub](https://github.com/ant-design/ant-design/blob/0.12-stable/CHANGELOG.md)。
---
## 1.11.5
`2017-01-09`
* 修复 `Table[filterDropdown]` 显示不受控的问题。
## 1.11.4
`2016-11-24`
* 修复 Upload 的 children 不能为 `null` 的问题。
## 1.11.3
`2016-11-23`
* 修复 `Input[type=textarea]` 在 IE8 下面的兼容性问题。[#3946](https://github.com/ant-design/ant-design/issues/3946) [@codering](https://github.com/codering)
## 1.11.2
`2016-09-26`

2
components/form/index.md

@ -90,7 +90,7 @@ CustomizedForm = Form.create({})(CustomizedForm);
| 参数 | 说明 | 类型 | 默认值 |
|-----------|-----------------------------------------|-----|--------|
| options.id | 必填输入控件唯一标志 | string | |
| options.id | 必填输入控件唯一标志 | string | |
| options.valuePropName | 子节点的值的属性,如 Switch 的是 'checked' | string | 'value' |
| options.initialValue | 子节点的初始值,类型、可选值均由子节点决定 | | |
| options.trigger | 收集子节点的值的时机 | string | 'onChange' |

33
components/input/calculateNodeHeight.js

@ -37,6 +37,21 @@ const SIZING_STYLE = [
let computedStyleCache = {};
let hiddenTextarea;
function computedStyle(node) {
return window.getComputedStyle ? getComputedStyle(node) : node.currentStyle;
}
function getStylePropertyValue(style, prop) {
let value;
if (style) {
value = window.getComputedStyle ?
style.getPropertyValue(prop)
:
style[prop.replace(/-(\w)/gi, (word, letter) => letter.toUpperCase())];
}
return value;
}
function calculateNodeStyling(node, useCache = false) {
const nodeRef = (
node.getAttribute('id') ||
@ -48,26 +63,26 @@ function calculateNodeStyling(node, useCache = false) {
return computedStyleCache[nodeRef];
}
const style = window.getComputedStyle(node);
const style = computedStyle(node);
const boxSizing = (
style.getPropertyValue('box-sizing') ||
style.getPropertyValue('-moz-box-sizing') ||
style.getPropertyValue('-webkit-box-sizing')
getStylePropertyValue(style, 'box-sizing') ||
getStylePropertyValue(style, '-moz-box-sizing') ||
getStylePropertyValue(style, '-webkit-box-sizing')
);
const paddingSize = (
parseFloat(style.getPropertyValue('padding-bottom')) +
parseFloat(style.getPropertyValue('padding-top'))
parseFloat(getStylePropertyValue(style, 'padding-bottom')) +
parseFloat(getStylePropertyValue(style, 'padding-top'))
);
const borderSize = (
parseFloat(style.getPropertyValue('border-bottom-width')) +
parseFloat(style.getPropertyValue('border-top-width'))
parseFloat(getStylePropertyValue(style, 'border-bottom-width')) +
parseFloat(getStylePropertyValue(style, 'border-top-width'))
);
const sizingStyle = SIZING_STYLE
.map(name => `${name}:${style.getPropertyValue(name)}`)
.map(name => `${name}:${getStylePropertyValue(style, name)}`)
.join(';');
const nodeInfo = {

86
components/table/Table.jsx

@ -43,6 +43,7 @@ export default class Table extends React.Component {
bordered: React.PropTypes.bool,
onChange: React.PropTypes.func,
locale: React.PropTypes.object,
dropdownPrefixCls: React.PropTypes.string,
}
static defaultProps = {
@ -77,12 +78,12 @@ export default class Table extends React.Component {
selectionDirty: false,
...this.getSortStateFromColumns(),
pagination: this.hasPagination() ?
{
...defaultPagination,
...pagination,
current: pagination.defaultCurrent || pagination.current || 1,
pageSize: pagination.defaultPageSize || pagination.pageSize || 10,
} : {},
{
...defaultPagination,
...pagination,
current: pagination.defaultCurrent || pagination.current || 1,
pageSize: pagination.defaultPageSize || pagination.pageSize || 10,
} : {},
};
this.CheckboxPropsCache = {};
@ -498,10 +499,11 @@ export default class Table extends React.Component {
}
renderRowSelection() {
const { prefixCls, rowSelection } = this.props;
const columns = this.props.columns.concat();
if (this.props.rowSelection) {
if (rowSelection) {
const data = this.getFlatCurrentPageData().filter((item) => {
if (this.props.rowSelection.getCheckboxProps) {
if (rowSelection.getCheckboxProps) {
return !this.getCheckboxPropsByItem(item).disabled;
}
return true;
@ -519,27 +521,20 @@ export default class Table extends React.Component {
data.every(item => this.getCheckboxPropsByItem(item).defaultChecked)
);
}
let selectionColumn;
if (this.props.rowSelection.type === 'radio') {
selectionColumn = {
key: 'selection-column',
render: this.renderSelectionRadio,
className: 'ant-table-selection-column',
};
} else {
let selectionColumn = {
key: 'selection-column',
render: this.renderSelectionRadio,
className: `${prefixCls}-selection-column`,
};
if (rowSelection.type !== 'radio') {
const checkboxAllDisabled = data.every(item => this.getCheckboxPropsByItem(item).disabled);
const checkboxAll = (
selectionColumn.render = this.renderSelectionCheckBox;
selectionColumn.title = (
<Checkbox checked={checked}
disabled={checkboxAllDisabled}
onChange={this.handleSelectAllRow}
/>
);
selectionColumn = {
key: 'selection-column',
title: checkboxAll,
render: this.renderSelectionCheckBox,
className: 'ant-table-selection-column',
};
}
if (columns.some(column => column.fixed === 'left' || column.fixed === true)) {
selectionColumn.fixed = 'left';
@ -574,6 +569,7 @@ export default class Table extends React.Component {
}
renderColumnsDropdown(columns) {
const { prefixCls, dropdownPrefixCls } = this.props;
const { sortOrder } = this.state;
const locale = this.getLocale();
return columns.map((originColumn, i) => {
@ -589,6 +585,8 @@ export default class Table extends React.Component {
column={column}
selectedKeys={colFilters}
confirmFilter={this.handleFilter}
prefixCls={`${prefixCls}-filter`}
dropdownPrefixCls={dropdownPrefixCls || 'ant-dropdown'}
/>
);
}
@ -597,20 +595,20 @@ export default class Table extends React.Component {
if (isSortColumn) {
column.className = column.className || '';
if (sortOrder) {
column.className += ' ant-table-column-sort';
column.className += ` ${prefixCls}-column-sort`;
}
}
const isAscend = isSortColumn && sortOrder === 'ascend';
const isDescend = isSortColumn && sortOrder === 'descend';
sortButton = (
<div className="ant-table-column-sorter">
<span className={`ant-table-column-sorter-up ${isAscend ? 'on' : 'off'}`}
<div className={`${prefixCls}-column-sorter`}>
<span className={`${prefixCls}-column-sorter-up ${isAscend ? 'on' : 'off'}`}
title="↑"
onClick={() => this.toggleSortOrder('ascend', column)}
>
<Icon type="caret-up" />
</span>
<span className={`ant-table-column-sorter-down ${isDescend ? 'on' : 'off'}`}
<span className={`${prefixCls}-column-sorter-down ${isDescend ? 'on' : 'off'}`}
title="↓"
onClick={() => this.toggleSortOrder('descend', column)}
>
@ -756,16 +754,17 @@ export default class Table extends React.Component {
}
render() {
const { style, className, ...restProps } = this.props;
const { style, className, prefixCls, showHeader, ...restProps } = this.props;
const data = this.getCurrentPageData();
let columns = this.renderRowSelection();
const expandIconAsCell = this.props.expandedRowRender && this.props.expandIconAsCell !== false;
const locale = this.getLocale();
const classString = classNames({
[`ant-table-${this.props.size}`]: true,
'ant-table-bordered': this.props.bordered,
'ant-table-empty': !data.length,
[`${prefixCls}-${this.props.size}`]: true,
[`${prefixCls}-bordered`]: this.props.bordered,
[`${prefixCls}-empty`]: !data.length,
[`${prefixCls}-without-column-header`]: !showHeader,
});
columns = this.renderColumnsDropdown(columns);
@ -775,12 +774,20 @@ export default class Table extends React.Component {
return newColumn;
});
let expandIconColumnIndex = (columns[0] && columns[0].key === 'selection-column') ? 1 : 0;
if ('expandIconColumnIndex' in restProps) {
expandIconColumnIndex = restProps.expandIconColumnIndex;
}
let table = (
<RcTable {...restProps}
<RcTable
{...restProps}
prefixCls={prefixCls}
data={data}
columns={columns}
showHeader={showHeader}
className={classString}
expandIconColumnIndex={(columns[0] && columns[0].key === 'selection-column') ? 1 : 0}
expandIconColumnIndex={expandIconColumnIndex}
expandIconAsCell={expandIconAsCell}
emptyText={() => locale.emptyText}
/>
@ -788,14 +795,15 @@ export default class Table extends React.Component {
// if there is no pagination or no data,
// the height of spin should decrease by half of pagination
const paginationPatchClass = (this.hasPagination() && data && data.length !== 0)
? 'ant-table-with-pagination'
: 'ant-table-without-pagination';
const spinClassName = this.props.loading ? `${paginationPatchClass} ant-table-spin-holder` : '';
table = <Spin className={spinClassName} spinning={this.props.loading}>{table}</Spin>;
? `${prefixCls}-with-pagination`
: `${prefixCls}-without-pagination`;
const spinClassName = this.props.loading ? `${paginationPatchClass} ${prefixCls}-spin-holder` : '';
return (
<div className={`${className} clearfix`} style={style}>
{table}
{this.renderPagination()}
<Spin className={spinClassName} spinning={this.props.loading}>
{table}
{this.renderPagination()}
</Spin>
</div>
);
}

123
components/table/demo/custom-filter-panel.md

@ -0,0 +1,123 @@
---
order: 19
title: 自定义筛选菜单
---
## zh-CN
通过 `filterDropdown`、`filterDropdownVisible` 和 `filterDropdownVisibleChange` 定义自定义的列筛选功能,并实现一个搜索列的示例。
## en-US
Implement a customized column search example via `filterDropdown`, `filterDropdownVisible` and `filterDropdownVisibleChange`.
````jsx
import { Table, Input, Button } from 'antd';
const data = [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}, {
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
}];
const App = React.createClass({
getInitialState() {
return {
filterDropdownVisible: false,
data,
searchText: '',
};
},
onInputChange(e) {
this.setState({ searchText: e.target.value });
},
onSearch() {
const { searchText } = this.state;
const reg = new RegExp(searchText, 'gi');
this.setState({
filterDropdownVisible: false,
data: data.map((record) => {
const match = record.name.match(reg);
if (!match) {
return null;
}
return {
...record,
name: (
<span>
{record.name.split(reg).map((text, i) => (
i > 0 ? [<span className="highlight">{match[0]}</span>, text] : text
))}
</span>
),
};
}).filter(record => !!record),
});
},
render() {
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
filterDropdown: (
<div className="custom-filter-dropdown">
<Input
placeholder="Search name"
value={this.state.searchText}
onChange={this.onInputChange}
onPressEnter={this.onSearch}
/>
<Button type="primary" onClick={this.onSearch}>Search</Button>
</div>
),
filterDropdownVisible: this.state.filterDropdownVisible,
onFilterDropdownVisibleChange: visible => this.setState({ filterDropdownVisible: visible }),
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}];
return <Table columns={columns} dataSource={this.state.data} />;
},
});
ReactDOM.render(<App />, mountNode);
````
````css
.custom-filter-dropdown {
padding: 8px;
border-radius: 6px;
background: #fff;
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
}
.custom-filter-dropdown input {
width: 130px;
margin-right: 8px;
}
.highlight {
color: #f50;
}
````

74
components/table/filterDropdown.jsx

@ -5,36 +5,56 @@ import Icon from '../icon';
import Checkbox from '../checkbox';
import Radio from '../radio';
const FilterDropdownMenuWrapper = ({ onClick, children }) => (
<div className="ant-table-filter-dropdown" onClick={onClick}>{children}</div>
const FilterDropdownMenuWrapper = ({ onClick, children, className }) => (
<div className={className} onClick={onClick}>{children}</div>
);
export default class FilterMenu extends React.Component {
static defaultProps = {
handleFilter() {},
column: null,
column: {},
}
constructor(props) {
super(props);
const visible = ('filterDropdownVisible' in props.column) ?
props.column.filterDropdownVisible : false;
this.state = {
selectedKeys: props.selectedKeys,
keyPathOfSelectedItem: {}, //
visible: false,
visible,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
selectedKeys: nextProps.selectedKeys,
});
const { column } = nextProps;
let newState = {};
if ('selectedKeys' in nextProps) {
newState.selectedKeys = nextProps.selectedKeys;
}
if ('filterDropdownVisible' in column) {
newState.visible = column.filterDropdownVisible;
}
if (Object.keys(newState).length > 0) {
this.setState(newState);
}
}
setSelectedKeys = ({ selectedKeys }) => {
this.setState({ selectedKeys });
}
setVisible(visible) {
const { column } = this.props;
if (!('filterDropdownVisible' in column)) {
this.setState({ visible });
}
if (column.onFilterDropdownVisibleChange) {
column.onFilterDropdownVisibleChange(visible);
}
}
handleClearFilters = () => {
this.setState({
selectedKeys: [],
@ -42,16 +62,12 @@ export default class FilterMenu extends React.Component {
}
handleConfirm = () => {
this.setState({
visible: false,
});
this.setVisible(false);
this.confirmFilter();
}
onVisibleChange = (visible) => {
this.setState({
visible,
});
this.setVisible(visible);
if (!visible) {
this.confirmFilter();
}
@ -66,13 +82,14 @@ export default class FilterMenu extends React.Component {
renderMenuItem(item) {
const { column } = this.props;
const multiple = ('filterMultiple' in column) ? column.filterMultiple : true;
const input = multiple ? (
<Checkbox checked={this.state.selectedKeys.indexOf(item.value.toString()) >= 0} />
) : (
<Radio checked={this.state.selectedKeys.indexOf(item.value.toString()) >= 0} />
);
return (
<MenuItem key={item.value}>
{
multiple
? <Checkbox checked={this.state.selectedKeys.indexOf(item.value.toString()) >= 0} />
: <Radio checked={this.state.selectedKeys.indexOf(item.value.toString()) >= 0} />
}
{input}
<span>{item.text}</span>
</MenuItem>
);
@ -85,7 +102,7 @@ export default class FilterMenu extends React.Component {
const containSelected = Object.keys(keyPathOfSelectedItem).some(
key => keyPathOfSelectedItem[key].indexOf(item.value) >= 0
);
const subMenuCls = containSelected ? 'ant-dropdown-submenu-contain-selected' : '';
const subMenuCls = containSelected ? `${this.props.dropdownPrefixCls}-submenu-contain-selected` : '';
return (
<SubMenu title={item.text} className={subMenuCls} key={item.value.toString()}>
{item.children.map(child => this.renderMenuItem(child))}
@ -112,31 +129,34 @@ export default class FilterMenu extends React.Component {
}
render() {
const { column, locale } = this.props;
const { column, locale, prefixCls, dropdownPrefixCls } = this.props;
// default multiple selection in filter dropdown
const multiple = ('filterMultiple' in column) ? column.filterMultiple : true;
const menus = column.filterDropdown ? column.filterDropdown : (
const menus = column.filterDropdown ? (
<FilterDropdownMenuWrapper>
{column.filterDropdown}
</FilterDropdownMenuWrapper>
) : (
<FilterDropdownMenuWrapper className={`${prefixCls}-dropdown`}>
<Menu
multiple={multiple}
onClick={this.handleMenuItemClick}
prefixCls="ant-dropdown-menu"
prefixCls={`${dropdownPrefixCls}-menu`}
onSelect={this.setSelectedKeys}
onDeselect={this.setSelectedKeys}
selectedKeys={this.state.selectedKeys}
>
{this.renderMenus(column.filters)}
</Menu>
<div className="ant-table-filter-dropdown-btns">
<div className={`${prefixCls}-dropdown-btns`}>
<a
className="ant-table-filter-dropdown-link confirm"
className={`${prefixCls}-dropdown-link confirm`}
onClick={this.handleConfirm}
>
{locale.filterConfirm}
</a>
<a
className="ant-table-filter-dropdown-link clear"
className={`${prefixCls}-dropdown-link clear`}
onClick={this.handleClearFilters}
>
{locale.filterReset}
@ -146,7 +166,7 @@ export default class FilterMenu extends React.Component {
);
const dropdownSelectedClass = (this.props.selectedKeys.length > 0)
? 'ant-table-filter-selected' : '';
? `${prefixCls}-selected` : '';
return (
<Dropdown

2
components/table/index.md

@ -89,6 +89,8 @@ const columns = [{
| onFilter | 本地模式下,确定筛选的运行函数 | Function | - |
| filterMultiple | 是否多选 | Boolean | true |
| filterDropdown | 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 | React.Element | - |
| filterDropdownVisible | 用于控制自定义筛选菜单是否可见 | Boolean | - |
| onFilterDropdownVisibleChange | 自定义筛选菜单可见变化时调用 | function(visible) {} | - |
| sorter | 排序函数,本地排序使用一个函数,需要服务端排序可设为 true | Function or Boolean | - |
| colSpan | 表头列合并,设置为 0 时,不渲染 | Number | |
| width | 列宽度 | String or Number | - |

6
components/table/style/index.less

@ -97,6 +97,12 @@
}
}
// https://github.com/ant-design/ant-design/issues/4373
&-without-column-header &-title + &-content,
&-without-column-header table {
border-radius: 0;
}
&-tbody > tr.@{table-prefix-cls}-row-selected {
background: #fafafa;
}

8
components/upload/index.jsx

@ -280,9 +280,11 @@ export default class Upload extends React.Component {
[`${prefixCls}-disabled`]: this.props.disabled,
});
const uploadButton = this.props.children
? <div className={uploadButtonCls}><RcUpload {...props} ref="upload" /></div>
: null;
const uploadButton = (
<div className={uploadButtonCls} style={{ display: this.props.children ? '' : 'none' }}>
<RcUpload {...props} ref="upload" />
</div>
);
if (this.props.listType === 'picture-card') {
return (

4
package.json

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "1.11.2",
"version": "1.11.6",
"title": "Ant Design",
"description": "An enterprise-class UI design language and React-based implementation",
"homepage": "http://ant.design/",
@ -140,7 +140,7 @@
"jest": "jest",
"pre-publish": "node ./scripts/prepub",
"prepublish": "antd-tools run guard",
"pub": "antd-tools run update-self && antd-tools run pub",
"pub": "antd-tools run update-self && antd-tools run pub --npm-tag=1.x",
"authors": "git log --format='%aN <%aE>' | sort -u | grep -v 'users.noreply.github.com' | grep -v 'gitter.im' | grep -v '.local>' | grep -v 'alibaba-inc.com' | grep -v 'alipay.com' | grep -v 'taobao.com' > AUTHORS.txt"
},
"jest": {

Loading…
Cancel
Save