mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
merge origin/master
This commit is contained in:
commit
cdf4c1e6b8
29
.jest.js
29
.jest.js
@ -7,25 +7,10 @@ const transformIgnorePatterns = [
|
||||
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
setupFiles: [
|
||||
'./tests/setup.js',
|
||||
],
|
||||
moduleFileExtensions: [
|
||||
'ts',
|
||||
'tsx',
|
||||
'js',
|
||||
'jsx',
|
||||
'json',
|
||||
'md',
|
||||
],
|
||||
modulePathIgnorePatterns: [
|
||||
'/_site/',
|
||||
],
|
||||
testPathIgnorePatterns: [
|
||||
'/node_modules/',
|
||||
'dekko',
|
||||
'node',
|
||||
],
|
||||
setupFiles: ['./tests/setup.js'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'md'],
|
||||
modulePathIgnorePatterns: ['/_site/'],
|
||||
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node'],
|
||||
transform: {
|
||||
'\\.tsx?$': './node_modules/antd-tools/lib/jest/codePreprocessor',
|
||||
'\\.js$': './node_modules/antd-tools/lib/jest/codePreprocessor',
|
||||
@ -41,12 +26,10 @@ module.exports = {
|
||||
'!components/**/*/interface.{ts,tsx}',
|
||||
],
|
||||
transformIgnorePatterns,
|
||||
snapshotSerializers: [
|
||||
'enzyme-to-json/serializer',
|
||||
],
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfigFile: './tsconfig.test.json',
|
||||
tsConfig: './tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
testURL: 'http://localhost',
|
||||
|
@ -1,14 +1,7 @@
|
||||
// jest config for server render environment
|
||||
module.exports = {
|
||||
setupFiles: [
|
||||
'./tests/setup.js',
|
||||
],
|
||||
moduleFileExtensions: [
|
||||
'ts',
|
||||
'tsx',
|
||||
'js',
|
||||
'md',
|
||||
],
|
||||
setupFiles: ['./tests/setup.js'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
|
||||
transform: {
|
||||
'\\.tsx?$': './node_modules/antd-tools/lib/jest/codePreprocessor',
|
||||
'\\.js$': './node_modules/antd-tools/lib/jest/codePreprocessor',
|
||||
@ -16,9 +9,7 @@ module.exports = {
|
||||
},
|
||||
testRegex: 'demo\\.test\\.js$',
|
||||
testEnvironment: 'node',
|
||||
snapshotSerializers: [
|
||||
'enzyme-to-json/serializer',
|
||||
],
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfigFile: './tsconfig.test.json',
|
||||
|
@ -3,22 +3,14 @@ import { mount } from 'enzyme';
|
||||
import BackTop from '..';
|
||||
|
||||
describe('BackTop', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should scroll to top after click it', () => {
|
||||
it('should scroll to top after click it', async () => {
|
||||
const wrapper = mount(<BackTop visibilityHeight={-1} />);
|
||||
document.documentElement.scrollTop = 400;
|
||||
// trigger scroll manually
|
||||
wrapper.instance().handleScroll();
|
||||
jest.runAllTimers();
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
wrapper.find('.ant-back-top').simulate('click');
|
||||
jest.runAllTimers();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
|
||||
});
|
||||
});
|
||||
|
@ -73,7 +73,6 @@
|
||||
font-size: @font-size-base;
|
||||
color: @text-color;
|
||||
font-weight: normal;
|
||||
text-align: right;
|
||||
// https://stackoverflow.com/a/22429853/3040605
|
||||
margin-left: auto;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import arrayTreeFilter from 'array-tree-filter';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import Input from '../input';
|
||||
import Icon from '../icon';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
@ -103,6 +104,7 @@ export interface CascaderState {
|
||||
value: string[];
|
||||
popupVisible: boolean | undefined;
|
||||
flattenOptions: CascaderOptionType[][] | undefined;
|
||||
prevProps: CascaderProps;
|
||||
}
|
||||
|
||||
interface CascaderLocale {
|
||||
@ -180,9 +182,29 @@ function getFilledFieldNames(props: CascaderProps) {
|
||||
return names;
|
||||
}
|
||||
|
||||
function flattenTree(
|
||||
options: CascaderOptionType[],
|
||||
props: CascaderProps,
|
||||
ancestor: CascaderOptionType[] = [],
|
||||
) {
|
||||
const names: FilledFieldNamesType = getFilledFieldNames(props);
|
||||
let flattenOptions = [] as CascaderOptionType[][];
|
||||
const childrenName = names.children;
|
||||
options.forEach(option => {
|
||||
const path = ancestor.concat(option);
|
||||
if (props.changeOnSelect || !option[childrenName] || !option[childrenName].length) {
|
||||
flattenOptions.push(path);
|
||||
}
|
||||
if (option[childrenName]) {
|
||||
flattenOptions = flattenOptions.concat(flattenTree(option[childrenName], props, path));
|
||||
}
|
||||
});
|
||||
return flattenOptions;
|
||||
}
|
||||
|
||||
const defaultDisplayRender = (label: string[]) => label.join(' / ');
|
||||
|
||||
export default class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
class Cascader extends React.Component<CascaderProps, CascaderState> {
|
||||
static defaultProps = {
|
||||
placeholder: 'Please select',
|
||||
transitionName: 'slide-up',
|
||||
@ -193,6 +215,24 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
notFoundContent: 'Not Found',
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: CascaderProps, { prevProps }: CascaderState) {
|
||||
const newState: Partial<CascaderState> = {
|
||||
prevProps: nextProps,
|
||||
};
|
||||
|
||||
if ('value' in nextProps) {
|
||||
newState.value = nextProps.value || [];
|
||||
}
|
||||
if ('popupVisible' in nextProps) {
|
||||
newState.popupVisible = nextProps.popupVisible;
|
||||
}
|
||||
if (nextProps.showSearch && prevProps.options !== nextProps.options) {
|
||||
newState.flattenOptions = flattenTree(nextProps.options, nextProps);
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
cachedOptions: CascaderOptionType[];
|
||||
|
||||
private input: Input;
|
||||
@ -204,24 +244,11 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
inputValue: '',
|
||||
inputFocused: false,
|
||||
popupVisible: props.popupVisible,
|
||||
flattenOptions: props.showSearch ? this.flattenTree(props.options, props) : undefined,
|
||||
flattenOptions: props.showSearch ? flattenTree(props.options, props) : undefined,
|
||||
prevProps: props,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: CascaderProps) {
|
||||
if ('value' in nextProps) {
|
||||
this.setState({ value: nextProps.value || [] });
|
||||
}
|
||||
if ('popupVisible' in nextProps) {
|
||||
this.setState({ popupVisible: nextProps.popupVisible });
|
||||
}
|
||||
if (nextProps.showSearch && this.props.options !== nextProps.options) {
|
||||
this.setState({
|
||||
flattenOptions: this.flattenTree(nextProps.options, nextProps),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (value: any, selectedOptions: CascaderOptionType[]) => {
|
||||
this.setState({ inputValue: '' });
|
||||
if (selectedOptions[0].__IS_FILTERED_OPTION) {
|
||||
@ -311,26 +338,6 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
}
|
||||
};
|
||||
|
||||
flattenTree(
|
||||
options: CascaderOptionType[],
|
||||
props: CascaderProps,
|
||||
ancestor: CascaderOptionType[] = [],
|
||||
) {
|
||||
const names: FilledFieldNamesType = getFilledFieldNames(props);
|
||||
let flattenOptions = [] as CascaderOptionType[][];
|
||||
const childrenName = names.children;
|
||||
options.forEach(option => {
|
||||
const path = ancestor.concat(option);
|
||||
if (props.changeOnSelect || !option[childrenName] || !option[childrenName].length) {
|
||||
flattenOptions.push(path);
|
||||
}
|
||||
if (option[childrenName]) {
|
||||
flattenOptions = flattenOptions.concat(this.flattenTree(option[childrenName], props, path));
|
||||
}
|
||||
});
|
||||
return flattenOptions;
|
||||
}
|
||||
|
||||
generateFilteredOptions(prefixCls: string | undefined) {
|
||||
const { showSearch, notFoundContent } = this.props;
|
||||
const names: FilledFieldNamesType = getFilledFieldNames(this.props);
|
||||
@ -567,3 +574,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
polyfill(Cascader);
|
||||
|
||||
export default Cascader;
|
||||
|
@ -6,7 +6,7 @@ export interface CommentProps {
|
||||
/** List of action items rendered below the comment content */
|
||||
actions?: Array<React.ReactNode>;
|
||||
/** The element to display as the comment author. */
|
||||
author?: string;
|
||||
author?: React.ReactNode;
|
||||
/** The element to display as the comment avatar - generally an antd Avatar */
|
||||
avatar?: React.ReactNode;
|
||||
/** className of comment */
|
||||
|
@ -181,24 +181,22 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
|
||||
<span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>
|
||||
))) || <Icon type="calendar" className={`${prefixCls}-picker-icon`} />;
|
||||
|
||||
const input = ({ value }: { value: moment.Moment | undefined }) => {
|
||||
return (
|
||||
<span style={{ display: 'inline-block' }}>
|
||||
<input
|
||||
ref={this.saveInput}
|
||||
disabled={disabled}
|
||||
readOnly
|
||||
value={(value && value.format(format)) || ''}
|
||||
placeholder={placeholder}
|
||||
className={pickerInputClass}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
{clearIcon}
|
||||
{inputIcon}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
const input = ({ value }: { value: moment.Moment | undefined }) => (
|
||||
<span style={{ display: 'inline-block', width: '100%' }}>
|
||||
<input
|
||||
ref={this.saveInput}
|
||||
disabled={disabled}
|
||||
readOnly
|
||||
value={(value && value.format(format)) || ''}
|
||||
placeholder={placeholder}
|
||||
className={pickerInputClass}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
{clearIcon}
|
||||
{inputIcon}
|
||||
</span>
|
||||
);
|
||||
return (
|
||||
<span className={classNames(className, pickerClass)} style={style} id={id}>
|
||||
<RcDatePicker
|
||||
|
@ -6,7 +6,7 @@ exports[`WeekPicker should support style prop 1`] = `
|
||||
style="width: 400px;"
|
||||
>
|
||||
<span
|
||||
style="display: inline-block;"
|
||||
style="display: inline-block; width: 100%;"
|
||||
>
|
||||
<input
|
||||
class="ant-calendar-picker-input ant-input"
|
||||
|
@ -112,7 +112,7 @@ exports[`renders ./components/date-picker/demo/basic.md correctly 1`] = `
|
||||
class="ant-calendar-picker"
|
||||
>
|
||||
<span
|
||||
style="display:inline-block"
|
||||
style="display:inline-block;width:100%"
|
||||
>
|
||||
<input
|
||||
class="ant-calendar-picker-input ant-input"
|
||||
@ -1146,7 +1146,7 @@ exports[`renders ./components/date-picker/demo/size.md correctly 1`] = `
|
||||
class="ant-calendar-picker ant-calendar-picker-default"
|
||||
>
|
||||
<span
|
||||
style="display:inline-block"
|
||||
style="display:inline-block;width:100%"
|
||||
>
|
||||
<input
|
||||
class="ant-calendar-picker-input ant-input"
|
||||
@ -1353,7 +1353,7 @@ exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
|
||||
class="ant-calendar-picker"
|
||||
>
|
||||
<span
|
||||
style="display:inline-block"
|
||||
style="display:inline-block;width:100%"
|
||||
>
|
||||
<input
|
||||
class="ant-calendar-picker-input ant-input"
|
||||
@ -1455,7 +1455,7 @@ exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
|
||||
class="ant-calendar-picker"
|
||||
>
|
||||
<span
|
||||
style="display:inline-block"
|
||||
style="display:inline-block;width:100%"
|
||||
>
|
||||
<input
|
||||
class="ant-calendar-picker-input ant-input"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import moment from 'moment';
|
||||
import DatePicker from '..';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
@ -38,6 +39,7 @@ describe('DatePicker with showTime', () => {
|
||||
onChange={onChangeFn}
|
||||
onOk={onOkFn}
|
||||
onOpenChange={onOpenChangeFn}
|
||||
defaultValue={moment()}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
@ -117,16 +117,16 @@ The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicke
|
||||
| -------- | ----------- | ---- | ------- |
|
||||
| defaultValue | to set default date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
|
||||
| defaultPickerValue | to set default picker date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)\] | - |
|
||||
| disabledTime | to specify the time that cannot be selected | function(dates: [moment, moment], partial: `'start'|'end'`) | - |
|
||||
| disabledTime | to specify the time that cannot be selected | function(dates: \[moment, moment], partial: `'start'|'end'`) | - |
|
||||
| format | to set the date format | string | "YYYY-MM-DD HH:mm:ss" |
|
||||
| ranges | preseted ranges for quick selection | { \[range: string]: [moment](http://momentjs.com/)\[] } \| { \[range: string]: () => [moment](http://momentjs.com/)\[] } | - |
|
||||
| renderExtraFooter | render extra footer in panel | () => React.ReactNode | - |
|
||||
| showTime | to provide an additional time selection | object\|boolean | [TimePicker Options](/components/time-picker/#API) |
|
||||
| showTime.defaultValue | to set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | [moment(), moment()] |
|
||||
| showTime.defaultValue | to set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | \[moment(), moment()] |
|
||||
| value | to set date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
|
||||
| onCalendarChange | a callback function, can be executed when the start time or the end time of the range is changing | function(dates: [moment, moment], dateStrings: [string, string]) | - |
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(dates: [moment, moment], dateStrings: [string, string]) | - |
|
||||
| onOk | callback when click ok button | function() | - |
|
||||
| onCalendarChange | a callback function, can be executed when the start time or the end time of the range is changing | function(dates: \[moment, moment], dateStrings: \[string, string]) | - |
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(dates: \[moment, moment], dateStrings: \[string, string]) | - |
|
||||
| onOk | callback when click ok button | function(dates: [moment](http://momentjs.com/)\[]) | - |
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-calendar-picker {
|
||||
|
@ -119,16 +119,16 @@ moment.locale('zh-cn');
|
||||
| --- | --- | --- | --- |
|
||||
| defaultValue | 默认日期 | [moment](http://momentjs.com/)\[] | 无 |
|
||||
| defaultPickerValue | 默认面板日期 | [moment](http://momentjs.com/)\[] | 无 |
|
||||
| disabledTime | 不可选择的时间 | function(dates: [moment, moment], partial: `'start'|'end'`) | 无 |
|
||||
| disabledTime | 不可选择的时间 | function(dates: \[moment, moment\], partial: `'start'|'end'`) | 无 |
|
||||
| format | 展示的日期格式 | string | "YYYY-MM-DD HH:mm:ss" |
|
||||
| ranges | 预设时间范围快捷选择 | { \[range: string]: [moment](http://momentjs.com/)\[] } \| { \[range: string]: () => [moment](http://momentjs.com/)\[] } | 无 |
|
||||
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - |
|
||||
| showTime | 增加时间选择功能 | Object\|boolean | [TimePicker Options](/components/time-picker/#API) |
|
||||
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | [moment(), moment()] |
|
||||
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | \[moment(), moment()] |
|
||||
| value | 日期 | [moment](http://momentjs.com/)\[] | 无 |
|
||||
| onCalendarChange | 待选日期发生变化的回调 | function(dates: [moment, moment], dateStrings: [string, string]) | 无 |
|
||||
| onChange | 日期范围发生变化的回调 | function(dates: [moment, moment], dateStrings: [string, string]) | 无 |
|
||||
| onOk | 点击确定按钮的回调 | function() | - |
|
||||
| onCalendarChange | 待选日期发生变化的回调 | function(dates: \[moment, moment\], dateStrings: \[string, string\]) | 无 |
|
||||
| onChange | 日期范围发生变化的回调 | function(dates: \[moment, moment\], dateStrings: \[string, string\]) | 无 |
|
||||
| onOk | 点击确定按钮的回调 | function(dates: [moment](http://momentjs.com/)\[]) | - |
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-calendar-picker {
|
||||
|
@ -67,7 +67,7 @@ export interface RangePickerProps extends PickerProps {
|
||||
defaultPickerValue?: RangePickerValue;
|
||||
onChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
|
||||
onCalendarChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
|
||||
onOk?: (selectedTime: moment.Moment) => void;
|
||||
onOk?: (selectedTime: moment.Moment[]) => void;
|
||||
showTime?: TimePickerProps | boolean;
|
||||
ranges?: {
|
||||
[range: string]: RangePickerPresetRange;
|
||||
|
@ -3118,6 +3118,163 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
<div
|
||||
class="ant-col-6 ant-form-item-label"
|
||||
>
|
||||
<label
|
||||
class=""
|
||||
for="checkbox-group"
|
||||
title="Checkbox.Group"
|
||||
>
|
||||
Checkbox.Group
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col-14 ant-form-item-control-wrapper"
|
||||
>
|
||||
<div
|
||||
class="ant-form-item-control has-success"
|
||||
>
|
||||
<span
|
||||
class="ant-form-item-children"
|
||||
>
|
||||
<div
|
||||
class="ant-checkbox-group"
|
||||
data-__field="[object Object]"
|
||||
data-__meta="[object Object]"
|
||||
id="checkbox-group"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-row"
|
||||
>
|
||||
<div
|
||||
class="ant-col-8"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-checkbox-checked"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
value="A"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
A
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col-8"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox ant-checkbox-checked ant-checkbox-disabled"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-checkbox-input"
|
||||
disabled=""
|
||||
type="checkbox"
|
||||
value="B"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
B
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col-8"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
value="C"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
C
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col-8"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
value="D"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
D
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="ant-col-8"
|
||||
>
|
||||
<label
|
||||
class="ant-checkbox-wrapper"
|
||||
>
|
||||
<span
|
||||
class="ant-checkbox"
|
||||
>
|
||||
<input
|
||||
class="ant-checkbox-input"
|
||||
type="checkbox"
|
||||
value="E"
|
||||
/>
|
||||
<span
|
||||
class="ant-checkbox-inner"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
E
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-row ant-form-item"
|
||||
>
|
||||
|
@ -16,7 +16,8 @@ Demostration for validataion configuration for form controls which are not show
|
||||
````jsx
|
||||
import {
|
||||
Form, Select, InputNumber, Switch, Radio,
|
||||
Slider, Button, Upload, Icon, Rate,
|
||||
Slider, Button, Upload, Icon, Rate, Checkbox,
|
||||
Row, Col,
|
||||
} from 'antd';
|
||||
|
||||
const FormItem = Form.Item;
|
||||
@ -147,6 +148,25 @@ class Demo extends React.Component {
|
||||
)}
|
||||
</FormItem>
|
||||
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
label="Checkbox.Group"
|
||||
>
|
||||
{getFieldDecorator("checkbox-group", {
|
||||
initialValue: ["A", "B"],
|
||||
})(
|
||||
<Checkbox.Group style={{ width: "100%" }}>
|
||||
<Row>
|
||||
<Col span={8}><Checkbox value="A">A</Checkbox></Col>
|
||||
<Col span={8}><Checkbox disabled value="B">B</Checkbox></Col>
|
||||
<Col span={8}><Checkbox value="C">C</Checkbox></Col>
|
||||
<Col span={8}><Checkbox value="D">D</Checkbox></Col>
|
||||
<Col span={8}><Checkbox value="E">E</Checkbox></Col>
|
||||
</Row>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Rate"
|
||||
|
@ -455,6 +455,9 @@ form {
|
||||
.@{ant-prefix}-select {
|
||||
&-selection {
|
||||
border-color: @warning-color;
|
||||
&:hover {
|
||||
border-color: @warning-color;
|
||||
}
|
||||
}
|
||||
&-open .@{ant-prefix}-select-selection,
|
||||
&-focused .@{ant-prefix}-select-selection {
|
||||
@ -501,6 +504,9 @@ form {
|
||||
.@{ant-prefix}-select {
|
||||
&-selection {
|
||||
border-color: @error-color;
|
||||
&:hover {
|
||||
border-color: @error-color;
|
||||
}
|
||||
}
|
||||
&-open .@{ant-prefix}-select-selection,
|
||||
&-focused .@{ant-prefix}-select-selection {
|
||||
|
@ -25,6 +25,24 @@
|
||||
.active(@border-color);
|
||||
}
|
||||
|
||||
// Input prefix
|
||||
.@{ant-prefix}-input-affix-wrapper {
|
||||
.@{ant-prefix}-input {
|
||||
&,
|
||||
&:hover {
|
||||
border-color: @border-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
.active(@border-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .@{ant-prefix}-input:not(.@{ant-prefix}-input-disabled) {
|
||||
border-color: @border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.@{ant-prefix}-input-prefix {
|
||||
color: @text-color;
|
||||
}
|
||||
|
@ -267,28 +267,26 @@
|
||||
&-wrap,
|
||||
> .@{inputClass} {
|
||||
&:not(:first-child):not(:last-child) {
|
||||
border-right-width: 1px;
|
||||
border-right-color: transparent;
|
||||
border-right-width: @border-width-base;
|
||||
&:hover {
|
||||
.hover();
|
||||
z-index: 1;
|
||||
}
|
||||
&:focus {
|
||||
.active();
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > * {
|
||||
border-radius: 0;
|
||||
border-right-width: 0;
|
||||
vertical-align: top; // https://github.com/ant-design/ant-design-pro/issues/139
|
||||
float: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/11863
|
||||
& > span:not(:last-child) > .@{inputClass} {
|
||||
border-right-width: 0;
|
||||
& > *:not(:last-child) {
|
||||
border-right-width: @border-width-base;
|
||||
margin-right: -@border-width-base;
|
||||
}
|
||||
|
||||
// Undo float for .ant-input-group .ant-input
|
||||
@ -305,12 +303,11 @@
|
||||
& > .@{ant-prefix}-time-picker .@{ant-prefix}-time-picker-input {
|
||||
border-radius: 0;
|
||||
border-right-width: @border-width-base;
|
||||
border-right-color: transparent;
|
||||
&:hover {
|
||||
.hover();
|
||||
z-index: 1;
|
||||
}
|
||||
&:focus {
|
||||
.active();
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,16 +333,6 @@
|
||||
border-top-right-radius: @border-radius-base;
|
||||
border-bottom-right-radius: @border-radius-base;
|
||||
border-right-width: @border-width-base;
|
||||
border-right-color: @input-border-color;
|
||||
&:hover {
|
||||
.hover();
|
||||
}
|
||||
&:focus {
|
||||
.active();
|
||||
.@{ant-prefix}-cascader-input {
|
||||
.active();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/12493
|
||||
@ -365,13 +352,14 @@
|
||||
}
|
||||
|
||||
.@{inputClass} {
|
||||
position: static;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.@{inputClass}-prefix,
|
||||
.@{inputClass}-suffix {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
z-index: 2;
|
||||
transform: translateY(-50%);
|
||||
line-height: 0;
|
||||
color: @input-color;
|
||||
|
@ -2,6 +2,7 @@ import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import shallowEqual from 'shallowequal';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import Radio from './radio';
|
||||
import {
|
||||
RadioGroupProps,
|
||||
@ -23,7 +24,7 @@ function getCheckedValue(children: React.ReactNode) {
|
||||
return matched ? { value } : undefined;
|
||||
}
|
||||
|
||||
export default class RadioGroup extends React.Component<RadioGroupProps, RadioGroupState> {
|
||||
class RadioGroup extends React.Component<RadioGroupProps, RadioGroupState> {
|
||||
static defaultProps = {
|
||||
disabled: false,
|
||||
buttonStyle: 'outline' as RadioGroupButtonStyle,
|
||||
@ -33,6 +34,22 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
|
||||
radioGroup: PropTypes.any,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(nextProps: RadioGroupProps) {
|
||||
if ('value' in nextProps) {
|
||||
return {
|
||||
value: nextProps.value,
|
||||
};
|
||||
} else {
|
||||
const checkedValue = getCheckedValue(nextProps.children);
|
||||
if (checkedValue) {
|
||||
return {
|
||||
value: checkedValue.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(props: RadioGroupProps) {
|
||||
super(props);
|
||||
let value;
|
||||
@ -60,21 +77,6 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: RadioGroupProps) {
|
||||
if ('value' in nextProps) {
|
||||
this.setState({
|
||||
value: nextProps.value,
|
||||
});
|
||||
} else {
|
||||
const checkedValue = getCheckedValue(nextProps.children);
|
||||
if (checkedValue) {
|
||||
this.setState({
|
||||
value: checkedValue.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: RadioGroupProps, nextState: RadioGroupState) {
|
||||
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
|
||||
}
|
||||
@ -162,3 +164,6 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
|
||||
return <ConfigConsumer>{this.renderGroup}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
|
||||
polyfill(RadioGroup);
|
||||
export default RadioGroup;
|
||||
|
@ -116,6 +116,7 @@
|
||||
.@{radio-inner-prefix-cls} {
|
||||
border-color: @border-color-base !important;
|
||||
background-color: @input-disabled-bg;
|
||||
cursor: not-allowed;
|
||||
&:after {
|
||||
background-color: fade(@black, 20%);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ describe('Spin', () => {
|
||||
.find('.ant-spin-nested-loading')
|
||||
.at(0)
|
||||
.prop('style'),
|
||||
).toBe(null);
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
wrapper
|
||||
.find('.ant-spin')
|
||||
|
@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import Animate from 'rc-animate';
|
||||
import omit from 'omit.js';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
|
||||
@ -93,13 +92,6 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
return !!(this.props && this.props.children);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { spinning, delay } = this.props;
|
||||
if (shouldDelay(spinning, delay)) {
|
||||
this.delayTimeout = window.setTimeout(this.delayUpdateSpinning, delay);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.debounceTimeout) {
|
||||
clearTimeout(this.debounceTimeout);
|
||||
@ -151,6 +143,7 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
size,
|
||||
tip,
|
||||
wrapperClassName,
|
||||
style,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const { spinning } = this.state;
|
||||
@ -171,33 +164,22 @@ class Spin extends React.Component<SpinProps, SpinState> {
|
||||
const divProps = omit(restProps, ['spinning', 'delay', 'indicator']);
|
||||
|
||||
const spinElement = (
|
||||
<div {...divProps} className={spinClassName}>
|
||||
<div {...divProps} style={style} className={spinClassName}>
|
||||
{renderIndicator(prefixCls, this.props)}
|
||||
{tip ? <div className={`${prefixCls}-text`}>{tip}</div> : null}
|
||||
</div>
|
||||
);
|
||||
if (this.isNestedPattern()) {
|
||||
let animateClassName = prefixCls + '-nested-loading';
|
||||
if (wrapperClassName) {
|
||||
animateClassName += ' ' + wrapperClassName;
|
||||
}
|
||||
const containerClassName = classNames({
|
||||
[`${prefixCls}-container`]: true,
|
||||
const containerClassName = classNames(`${prefixCls}-container`, {
|
||||
[`${prefixCls}-blur`]: spinning,
|
||||
});
|
||||
return (
|
||||
<Animate
|
||||
{...divProps}
|
||||
component="div"
|
||||
className={animateClassName}
|
||||
style={null}
|
||||
transitionName="fade"
|
||||
>
|
||||
<div {...divProps} className={classNames(`${prefixCls}-nested-loading`, wrapperClassName)}>
|
||||
{spinning && <div key="loading">{spinElement}</div>}
|
||||
<div className={containerClassName} key="container">
|
||||
{this.props.children}
|
||||
</div>
|
||||
</Animate>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return spinElement;
|
||||
|
@ -26,7 +26,7 @@
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
max-height: 360px;
|
||||
max-height: 400px;
|
||||
width: 100%;
|
||||
z-index: 4;
|
||||
.@{spin-prefix-cls}-dot {
|
||||
@ -75,19 +75,6 @@
|
||||
&-container {
|
||||
position: relative;
|
||||
transition: opacity 0.3s;
|
||||
.clearfix;
|
||||
}
|
||||
|
||||
&-blur {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
opacity: 0.5;
|
||||
-webkit-filter: blur(0.5px);
|
||||
filter: blur(0.5px);
|
||||
|
||||
/* autoprefixer: off */
|
||||
filter: ~'progid\:DXImageTransform\.Microsoft\.Blur(PixelRadius\=1, MakeShadow\=false)';
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
@ -97,12 +84,27 @@
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: @component-background;
|
||||
opacity: 0.3;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: all 0.3s;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
&-blur {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
opacity: 0.5;
|
||||
|
||||
&:after {
|
||||
opacity: 0.4;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// tip
|
||||
// ------------------------------
|
||||
&-tip {
|
||||
|
@ -58,8 +58,13 @@ export default class SelectionCheckboxAll<T> extends React.Component<
|
||||
});
|
||||
}
|
||||
|
||||
checkSelection(data: T[], type: string, byDefaultChecked: boolean) {
|
||||
const { store, getCheckboxPropsByItem, getRecordKey } = this.props;
|
||||
checkSelection(
|
||||
props: SelectionCheckboxAllProps<T>,
|
||||
data: T[],
|
||||
type: string,
|
||||
byDefaultChecked: boolean,
|
||||
) {
|
||||
const { store, getCheckboxPropsByItem, getRecordKey } = props || this.props;
|
||||
// type should be 'every' | 'some'
|
||||
if (type === 'every' || type === 'some') {
|
||||
return byDefaultChecked
|
||||
@ -93,8 +98,9 @@ export default class SelectionCheckboxAll<T> extends React.Component<
|
||||
checked = false;
|
||||
} else {
|
||||
checked = store.getState().selectionDirty
|
||||
? this.checkSelection(data, 'every', false)
|
||||
: this.checkSelection(data, 'every', false) || this.checkSelection(data, 'every', true);
|
||||
? this.checkSelection(props, data, 'every', false)
|
||||
: this.checkSelection(props, data, 'every', false) ||
|
||||
this.checkSelection(props, data, 'every', true);
|
||||
}
|
||||
return checked;
|
||||
}
|
||||
@ -106,15 +112,17 @@ export default class SelectionCheckboxAll<T> extends React.Component<
|
||||
indeterminate = false;
|
||||
} else {
|
||||
indeterminate = store.getState().selectionDirty
|
||||
? this.checkSelection(data, 'some', false) && !this.checkSelection(data, 'every', false)
|
||||
: (this.checkSelection(data, 'some', false) &&
|
||||
!this.checkSelection(data, 'every', false)) ||
|
||||
(this.checkSelection(data, 'some', true) && !this.checkSelection(data, 'every', true));
|
||||
? this.checkSelection(props, data, 'some', false) &&
|
||||
!this.checkSelection(props, data, 'every', false)
|
||||
: (this.checkSelection(props, data, 'some', false) &&
|
||||
!this.checkSelection(props, data, 'every', false)) ||
|
||||
(this.checkSelection(props, data, 'some', true) &&
|
||||
!this.checkSelection(props, data, 'every', true));
|
||||
}
|
||||
return indeterminate;
|
||||
}
|
||||
|
||||
handleSelectAllChagne = (e: CheckboxChangeEvent) => {
|
||||
handleSelectAllChange = (e: CheckboxChangeEvent) => {
|
||||
const checked = e.target.checked;
|
||||
this.props.onSelect(checked ? 'all' : 'removeAll', 0, null);
|
||||
};
|
||||
@ -171,7 +179,7 @@ export default class SelectionCheckboxAll<T> extends React.Component<
|
||||
checked={checked}
|
||||
indeterminate={indeterminate}
|
||||
disabled={disabled}
|
||||
onChange={this.handleSelectAllChagne}
|
||||
onChange={this.handleSelectAllChange}
|
||||
/>
|
||||
{customSelections}
|
||||
</div>
|
||||
|
@ -25,6 +25,7 @@ import {
|
||||
TableComponents,
|
||||
RowSelectionType,
|
||||
TableLocale,
|
||||
AdditionalCellProps,
|
||||
ColumnProps,
|
||||
CompareFn,
|
||||
TableStateFilters,
|
||||
@ -814,6 +815,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
|
||||
const key = this.getColumnKey(column, i) as string;
|
||||
let filterDropdown;
|
||||
let sortButton;
|
||||
let onHeaderCell = column.onHeaderCell;
|
||||
const sortTitle = this.getColumnTitle(column.title, {}) || locale.sortTitle;
|
||||
const isSortColumn = this.isSortColumn(column);
|
||||
if ((column.filters && column.filters.length > 0) || column.filterDropdown) {
|
||||
@ -848,8 +850,27 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
onHeaderCell = (col: Column<T>) => {
|
||||
let colProps: AdditionalCellProps = {};
|
||||
// Get original first
|
||||
if (column.onHeaderCell) {
|
||||
colProps = {
|
||||
...column.onHeaderCell(col),
|
||||
};
|
||||
}
|
||||
// Add sorter logic
|
||||
const onHeaderCellClick = colProps.onClick;
|
||||
colProps.onClick = (...args) => {
|
||||
this.toggleSortOrder(column);
|
||||
if (onHeaderCellClick) {
|
||||
onHeaderCellClick(...args);
|
||||
}
|
||||
};
|
||||
return colProps;
|
||||
};
|
||||
}
|
||||
const sortTitleString = (sortButton && typeof sortTitle === 'string') ? sortTitle : undefined;
|
||||
const sortTitleString = sortButton && typeof sortTitle === 'string' ? sortTitle : undefined;
|
||||
return {
|
||||
...column,
|
||||
className: classNames(column.className, {
|
||||
@ -863,13 +884,13 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
|
||||
key="title"
|
||||
title={sortTitleString}
|
||||
className={sortButton ? `${prefixCls}-column-sorters` : undefined}
|
||||
onClick={() => this.toggleSortOrder(column)}
|
||||
>
|
||||
{this.renderColumnTitle(column.title)}
|
||||
{sortButton}
|
||||
</div>,
|
||||
filterDropdown,
|
||||
],
|
||||
onHeaderCell,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -1038,8 +1038,12 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
|
||||
class=""
|
||||
>
|
||||
<colgroup>
|
||||
<col />
|
||||
<col />
|
||||
<col
|
||||
style="width:30%;min-width:30%"
|
||||
/>
|
||||
<col
|
||||
style="width:20%;min-width:20%"
|
||||
/>
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead
|
||||
@ -1053,31 +1057,48 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
|
||||
Name
|
||||
</div>
|
||||
<i
|
||||
class="anticon anticon-smile-o ant-table-filter-icon ant-dropdown-trigger"
|
||||
style="color:#aaa"
|
||||
class="anticon anticon-search ant-table-filter-icon ant-dropdown-trigger"
|
||||
title="Filter menu"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="smile"
|
||||
data-icon="search"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
|
||||
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
|
||||
/>
|
||||
</svg>
|
||||
</i>
|
||||
</th>
|
||||
<th
|
||||
class=""
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
<div>
|
||||
Age
|
||||
</div>
|
||||
<i
|
||||
class="anticon anticon-search ant-table-filter-icon ant-dropdown-trigger"
|
||||
title="Filter menu"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="search"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
|
||||
/>
|
||||
</svg>
|
||||
</i>
|
||||
</th>
|
||||
<th
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
@ -1086,20 +1107,20 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
|
||||
Address
|
||||
</div>
|
||||
<i
|
||||
class="anticon anticon-filter ant-dropdown-trigger"
|
||||
class="anticon anticon-search ant-table-filter-icon ant-dropdown-trigger"
|
||||
title="Filter menu"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class=""
|
||||
data-icon="filter"
|
||||
data-icon="search"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M349 838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V642H349v196zm531.1-684H143.9c-24.5 0-39.8 26.7-27.5 48l221.3 376h348.8l221.3-376c12.1-21.3-3.2-48-27.7-48z"
|
||||
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
|
||||
/>
|
||||
</svg>
|
||||
</i>
|
||||
@ -1120,17 +1141,35 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
|
||||
class="ant-table-row-indent indent-level-0"
|
||||
style="padding-left:0px"
|
||||
/>
|
||||
John Brown
|
||||
</td>
|
||||
<td
|
||||
class=""
|
||||
>
|
||||
32
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
John Brown
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
New York No. 1 Lake Park
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
32
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
New York No. 1 Lake Park
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
@ -1144,17 +1183,35 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
|
||||
class="ant-table-row-indent indent-level-0"
|
||||
style="padding-left:0px"
|
||||
/>
|
||||
Joe Black
|
||||
</td>
|
||||
<td
|
||||
class=""
|
||||
>
|
||||
42
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
Joe Black
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
London No. 1 Lake Park
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
42
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
London No. 1 Lake Park
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
@ -1168,17 +1225,35 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
|
||||
class="ant-table-row-indent indent-level-0"
|
||||
style="padding-left:0px"
|
||||
/>
|
||||
Jim Green
|
||||
</td>
|
||||
<td
|
||||
class=""
|
||||
>
|
||||
32
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
Jim Green
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
Sidney No. 1 Lake Park
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
32
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
Sidney No. 1 Lake Park
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
@ -1192,17 +1267,35 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
|
||||
class="ant-table-row-indent indent-level-0"
|
||||
style="padding-left:0px"
|
||||
/>
|
||||
Jim Red
|
||||
</td>
|
||||
<td
|
||||
class=""
|
||||
>
|
||||
32
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
Jim Red
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
London No. 2 Lake Park
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
32
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td
|
||||
class="ant-table-column-has-actions ant-table-column-has-filters"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class=""
|
||||
>
|
||||
London No. 2 Lake Park
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -17,6 +17,7 @@ Implement a customized column search example via `filterDropdown`.
|
||||
import {
|
||||
Table, Input, Button, Icon,
|
||||
} from 'antd';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
|
||||
const data = [{
|
||||
key: '1',
|
||||
@ -45,12 +46,60 @@ class App extends React.Component {
|
||||
searchText: '',
|
||||
};
|
||||
|
||||
handleSearch = (selectedKeys, confirm) => () => {
|
||||
getColumnSearchProps = (dataIndex) => ({
|
||||
filterDropdown: ({
|
||||
setSelectedKeys, selectedKeys, confirm, clearFilters,
|
||||
}) => (
|
||||
<div className="custom-filter-dropdown">
|
||||
<Input
|
||||
ref={node => { this.searchInput = node; }}
|
||||
placeholder={`Search ${dataIndex}`}
|
||||
value={selectedKeys[0]}
|
||||
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
|
||||
onPressEnter={() => this.handleSearch(selectedKeys, confirm)}
|
||||
style={{ width: 188, marginBottom: 8, display: 'block' }}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.handleSearch(selectedKeys, confirm)}
|
||||
icon="search"
|
||||
size="small"
|
||||
style={{ width: 90, marginRight: 8 }}
|
||||
>
|
||||
Search
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => this.handleReset(clearFilters)}
|
||||
size="small"
|
||||
style={{ width: 90 }}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
filterIcon: filtered => <Icon type="search" style={{ color: filtered ? '#1890ff' : undefined }} />,
|
||||
onFilter: (value, record) => record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: (visible) => {
|
||||
if (visible) {
|
||||
setTimeout(() => this.searchInput.select());
|
||||
}
|
||||
},
|
||||
render: (text) => (
|
||||
<Highlighter
|
||||
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
|
||||
searchWords={[this.state.searchText]}
|
||||
autoEscape
|
||||
textToHighlight={text.toString()}
|
||||
/>
|
||||
),
|
||||
})
|
||||
|
||||
handleSearch = (selectedKeys, confirm) => {
|
||||
confirm();
|
||||
this.setState({ searchText: selectedKeys[0] });
|
||||
}
|
||||
|
||||
handleReset = clearFilters => () => {
|
||||
handleReset = (clearFilters) => {
|
||||
clearFilters();
|
||||
this.setState({ searchText: '' });
|
||||
}
|
||||
@ -60,57 +109,19 @@ class App extends React.Component {
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
filterDropdown: ({
|
||||
setSelectedKeys, selectedKeys, confirm, clearFilters,
|
||||
}) => (
|
||||
<div className="custom-filter-dropdown">
|
||||
<Input
|
||||
ref={ele => this.searchInput = ele}
|
||||
placeholder="Search name"
|
||||
value={selectedKeys[0]}
|
||||
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
|
||||
onPressEnter={this.handleSearch(selectedKeys, confirm)}
|
||||
/>
|
||||
<Button type="primary" onClick={this.handleSearch(selectedKeys, confirm)}>Search</Button>
|
||||
<Button onClick={this.handleReset(clearFilters)}>Reset</Button>
|
||||
</div>
|
||||
),
|
||||
filterIcon: filtered => <Icon type="smile-o" style={{ color: filtered ? '#108ee9' : '#aaa' }} />,
|
||||
onFilter: (value, record) => record.name.toLowerCase().includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: (visible) => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
this.searchInput.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
render: (text) => {
|
||||
const { searchText } = this.state;
|
||||
return searchText ? (
|
||||
<span>
|
||||
{text.split(new RegExp(`(${searchText})`, 'gi')).map((fragment, i) => (
|
||||
fragment.toLowerCase() === searchText.toLowerCase()
|
||||
? <span key={i} className="highlight">{fragment}</span> : fragment // eslint-disable-line
|
||||
))}
|
||||
</span>
|
||||
) : text;
|
||||
},
|
||||
width: '30%',
|
||||
...this.getColumnSearchProps('name'),
|
||||
}, {
|
||||
title: 'Age',
|
||||
dataIndex: 'age',
|
||||
key: 'age',
|
||||
width: '20%',
|
||||
...this.getColumnSearchProps('age'),
|
||||
}, {
|
||||
title: 'Address',
|
||||
dataIndex: 'address',
|
||||
key: 'address',
|
||||
filters: [{
|
||||
text: 'London',
|
||||
value: 'London',
|
||||
}, {
|
||||
text: 'New York',
|
||||
value: 'New York',
|
||||
}],
|
||||
onFilter: (value, record) => record.address.indexOf(value) === 0,
|
||||
...this.getColumnSearchProps('address'),
|
||||
}];
|
||||
return <Table columns={columns} dataSource={data} />;
|
||||
}
|
||||
@ -122,21 +133,8 @@ ReactDOM.render(<App />, mountNode);
|
||||
````css
|
||||
.custom-filter-dropdown {
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
|
||||
}
|
||||
|
||||
.custom-filter-dropdown input {
|
||||
width: 130px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.custom-filter-dropdown button {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #f50;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
|
||||
}
|
||||
````
|
||||
|
@ -43,6 +43,11 @@ export interface ColumnProps<T> {
|
||||
onHeaderCell?: (props: ColumnProps<T>) => any;
|
||||
}
|
||||
|
||||
export interface AdditionalCellProps {
|
||||
onClick?: React.MouseEventHandler<HTMLElement>;
|
||||
[name: string]: any;
|
||||
}
|
||||
|
||||
export interface TableComponents {
|
||||
table?: React.ReactType;
|
||||
header?: {
|
||||
|
@ -280,8 +280,6 @@
|
||||
&-thead > tr > th,
|
||||
&-tbody > tr > td {
|
||||
padding: @table-padding-vertical @table-padding-horizontal;
|
||||
word-break: break-word;
|
||||
-ms-word-break: break-all;
|
||||
}
|
||||
|
||||
&-thead > tr > th.@{table-prefix-cls}-selection-column-custom {
|
||||
|
@ -41,13 +41,13 @@ class Demo extends React.Component {
|
||||
<div>
|
||||
<Select style={{ width: 200 }} onChange={(val) => { this.setState({ parentPos: val }); }}>
|
||||
{positionList.map(pos => (
|
||||
<Option value={pos}>Parent - {pos}</Option>
|
||||
<Option key={pos} value={pos}>Parent - {pos}</Option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
<Select style={{ width: 200 }} onChange={(val) => { this.setState({ childPos: val }); }}>
|
||||
{positionList.map(pos => (
|
||||
<Option value={pos}>Child - {pos}</Option>
|
||||
<Option key={pos} value={pos}>Child - {pos}</Option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
|
@ -36,9 +36,9 @@ Working on your first Pull Request? You can learn how from this free video serie
|
||||
|
||||
To help you get your feet wet and get you familiar with our contribution process, we have a list of [good first issues](https://github.com/ant-design/ant-design/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) that contain bugs or small features that have a relatively limited scope. This is a great place to get started.
|
||||
|
||||
If you decide to fix an issue, please be sure to check the comment thread in case somebody is already working on a fix. If nobody is working on it at the moment, please leave a comment stating that you intend to work on it so other people don’t accidentally duplicate your effort.
|
||||
If you decide to fix an issue, please be sure to check the comment thread in case somebody is already working on a fix. If nobody is working on it at the moment, please leave a comment stating that you intend to work on it so other people don't accidentally duplicate your effort.
|
||||
|
||||
If somebody claims an issue but doesn’t follow up for more than two weeks, it’s fine to take over it but you should still leave a comment.
|
||||
If somebody claims an issue but doesn't follow up for more than two weeks, it's fine to take over it but you should still leave a comment.
|
||||
|
||||
## Sending a Pull Request
|
||||
|
||||
@ -48,7 +48,7 @@ The core team is monitoring for pull requests. We will review your pull request
|
||||
|
||||
1. Fork the repository and create your branch from [proper branch](#Branch-Organization).
|
||||
1. Run `npm install` in the repository root.
|
||||
1. If you’ve fixed a bug or added code that should be tested, add tests!
|
||||
1. If you've fixed a bug or added code that should be tested, add tests!
|
||||
1. Ensure the test suite passes (npm run test). Tip: `npm test -- --watch TestName` is helpful in development.
|
||||
1. Run `npm test -- -u` to update [jest snapshot](http://facebook.github.io/jest/docs/en/snapshot-testing.html#snapshot-testing-with-jest) and commit these changes as well (if has).
|
||||
1. Make sure your code lints (npm run lint). Tip: Lint runs automatically when you `git commit`.
|
||||
|
@ -32,6 +32,7 @@ Split View | [react-split-pane](https://github.com/tomkp/react-split-pane)
|
||||
Image Crop | [react-image-crop](https://github.com/DominicTobias/react-image-crop)
|
||||
Trend Lines | [react-sparklines](https://github.com/borisyankov/react-sparklines)
|
||||
Formatted Input | [text-mask](https://github.com/text-mask/text-mask)
|
||||
Keywords highlight | [react-highlight-words](https://github.com/bvaughn/react-highlight-words)
|
||||
Animation | [react-move](https://github.com/react-tools/react-move) [Ant Motion](https://motion.ant.design/components/tween-one)
|
||||
|
||||
<style>
|
||||
|
@ -32,6 +32,7 @@ Emoji | [emoji-mart](https://github.com/missive/emoji-mart)
|
||||
图片裁切 | [react-image-crop](https://github.com/DominicTobias/react-image-crop)
|
||||
趋势线 | [react-sparklines](https://github.com/borisyankov/react-sparklines)
|
||||
格式化输入 | [text-mask](https://github.com/text-mask/text-mask)
|
||||
关键字高亮 | [react-highlight-words](https://github.com/bvaughn/react-highlight-words)
|
||||
动画 | [react-move](https://github.com/react-tools/react-move) [Ant Motion](https://motion.ant.design/components/tween-one)
|
||||
|
||||
<style>
|
||||
|
@ -20,7 +20,7 @@ title: 设计价值观
|
||||
|
||||
作为一份子,自然界的方方面面都会对用户行为产生深远影响,设计者应该从其中汲取灵感,并运用到当下的设计工作中。我们已做了部分探索,并将追求『自然』作为我们未来持之以恒的方向。
|
||||
|
||||
- 在行为的执行中,利用[行为分析](https://zhuanlan.zhihu.com/p/41952711)、人工智能、传感器、[元数据](https://zhuanlan.zhihu.com/p/43613398)等一系列方式,辅助用户有效决策、减少用户额外操作,从而节省用户脑力和体力,让人机交互行为更自然。
|
||||
- 在行为的执行中,利用[行为分析](https://zhuanlan.zhihu.com/p/41952711)、人工智能、[传感器](https://zhuanlan.zhihu.com/p/52648777)、[元数据](https://zhuanlan.zhihu.com/p/43613398)等一系列方式,辅助用户有效决策、减少用户额外操作,从而节省用户脑力和体力,让人机交互行为更自然。
|
||||
- 在感知和认知中,视觉系统扮演着最重要的角色,通过提炼自然界中的客观规律并运用到界面设计中,从而创建更有层次产品体验;同时,适时加入听觉系统、触觉系统等,创建更多维、更真实的产品体验。详见视觉语言
|
||||
|
||||
> 想了解自然价值观的前世今生,[请移步至专栏](https://zhuanlan.zhihu.com/p/44809866)。
|
||||
|
@ -156,6 +156,7 @@
|
||||
"react-document-title": "^2.0.3",
|
||||
"react-dom": "^16.5.2",
|
||||
"react-github-button": "^0.1.11",
|
||||
"react-highlight-words": "^0.14.0",
|
||||
"react-infinite-scroller": "^1.2.1",
|
||||
"react-intl": "^2.7.0",
|
||||
"react-resizable": "^1.7.5",
|
||||
|
Loading…
Reference in New Issue
Block a user