forked from p96170835/amis
parent
f8f299f1e2
commit
3c613d9646
|
@ -6,6 +6,9 @@
|
||||||
- `min` 最小值
|
- `min` 最小值
|
||||||
- `max` 最大值
|
- `max` 最大值
|
||||||
- `step` 步长
|
- `step` 步长
|
||||||
|
- `multiple` 支持选择范围,默认为`false`
|
||||||
|
- `joinValuse` 默认为 `true`,选择的 `value` 会通过 `delimiter` 连接起来,否则直接将以`{min: 1, max: 100}`的形式提交,开启`multiple`时有效
|
||||||
|
- `delimiter` 默认为 `,`
|
||||||
|
|
||||||
```schema:height="400" scope="form-item"
|
```schema:height="400" scope="form-item"
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
- `delimiter` 默认为 `,`
|
- `delimiter` 默认为 `,`
|
||||||
- `clearable` 默认为 `false`, 当设置为 `true` 时,已选中的选项右侧会有个小 `X` 用来取消设置。
|
- `clearable` 默认为 `false`, 当设置为 `true` 时,已选中的选项右侧会有个小 `X` 用来取消设置。
|
||||||
- `searchable` 默认为 `false`,表示可以通过输入部分内容检索出选项。
|
- `searchable` 默认为 `false`,表示可以通过输入部分内容检索出选项。
|
||||||
|
- `checkall` 默认为 `false` 开启后支持全选
|
||||||
|
- `checkAllLabel` 默认为 `全选`, 全选的文字
|
||||||
|
- `defaultCheckAll` 是否默认全选,默认为`false`
|
||||||
- 更多配置请参考 [FormItem](./FormItem.md)
|
- 更多配置请参考 [FormItem](./FormItem.md)
|
||||||
|
|
||||||
单选
|
单选
|
||||||
|
|
|
@ -675,6 +675,7 @@ $Form-select-menu-onActive-color: $info !default;
|
||||||
$Form-select-menu-onActive-bg: transparent !default;
|
$Form-select-menu-onActive-bg: transparent !default;
|
||||||
$Form-select-menu-onDisabled-color: $text--muted-color !default;
|
$Form-select-menu-onDisabled-color: $text--muted-color !default;
|
||||||
$Form-select-menu-onDisabled-bg: transparent !default;
|
$Form-select-menu-onDisabled-bg: transparent !default;
|
||||||
|
$Form-select-checkall-bottomBorder: #eceff8 !default;
|
||||||
|
|
||||||
// InputGroup
|
// InputGroup
|
||||||
$InputGroup-height: $Form-input-height !default;
|
$InputGroup-height: $Form-input-height !default;
|
||||||
|
|
|
@ -13,12 +13,17 @@
|
||||||
.#{$ns}InputRange-label--mid {
|
.#{$ns}InputRange-label--mid {
|
||||||
left: calc(50% - 60px);
|
left: calc(50% - 60px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-multiple {
|
||||||
|
.#{$ns}InputRange {
|
||||||
|
width: calc(100% - 210px);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}InputRange {
|
.#{$ns}InputRange {
|
||||||
&-input {
|
&-input {
|
||||||
font-size: $fontSizeSm;
|
font-size: $fontSizeSm;
|
||||||
width: px2rem(74px);
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: px2rem(26px);
|
right: px2rem(26px);
|
||||||
top: px2rem(12px);
|
top: px2rem(12px);
|
||||||
|
@ -26,7 +31,7 @@
|
||||||
|
|
||||||
input {
|
input {
|
||||||
padding: px2rem(10px);
|
padding: px2rem(10px);
|
||||||
width: 100%;
|
width: px2rem(74px);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
|
@ -34,6 +39,11 @@
|
||||||
border: $borderWidth solid $info;
|
border: $borderWidth solid $info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-separator {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-unit {
|
&-unit {
|
||||||
|
@ -167,6 +177,10 @@
|
||||||
.#{$ns}InputRange.is-disabled & {
|
.#{$ns}InputRange.is-disabled & {
|
||||||
background: $InputRange-track-onDisabled-bg;
|
background: $InputRange-track-onDisabled-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background: $InputRange-track-onActive-bg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-track--background {
|
&-track--background {
|
||||||
|
|
|
@ -156,7 +156,17 @@
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-checkall {
|
||||||
|
padding: (
|
||||||
|
$Form-select-menu-height - $Form-input-lineHeight *
|
||||||
|
$Form-input-fontSize - px2rem(2px)
|
||||||
|
)/2 $Form-select-paddingX;
|
||||||
|
border-bottom: px2rem(1px) solid $Form-select-checkall-bottomBorder;
|
||||||
|
min-width: px2rem(100px);
|
||||||
|
}
|
||||||
|
|
||||||
&-option {
|
&-option {
|
||||||
|
min-width: px2rem(100px);
|
||||||
padding: (
|
padding: (
|
||||||
$Form-select-menu-height - $Form-input-lineHeight *
|
$Form-select-menu-height - $Form-input-lineHeight *
|
||||||
$Form-input-fontSize - px2rem(2px)
|
$Form-input-fontSize - px2rem(2px)
|
||||||
|
@ -177,6 +187,10 @@
|
||||||
background-color: $Form-select-menu-onDisabled-bg;
|
background-color: $Form-select-menu-onDisabled-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-checkAll {
|
||||||
|
border-bottom: px2rem(1px) solid $Form-select-checkall-bottomBorder;
|
||||||
|
}
|
||||||
|
|
||||||
&--placeholder {
|
&--placeholder {
|
||||||
color: $Form-input-placeholderColor;
|
color: $Form-input-placeholderColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file Checkbox
|
* @file Checkbox
|
||||||
* @author fex
|
* @author fex
|
||||||
|
@ -6,6 +7,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import {ClassNamesFn, themeable} from '../theme';
|
import {ClassNamesFn, themeable} from '../theme';
|
||||||
|
import { autobind } from '../utils/helper';
|
||||||
|
|
||||||
const sizeMap = {
|
const sizeMap = {
|
||||||
sm: 'i-checks-sm',
|
sm: 'i-checks-sm',
|
||||||
|
@ -23,6 +25,7 @@ interface CheckboxProps {
|
||||||
label?: string;
|
label?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
onChange?: (value: any) => void;
|
onChange?: (value: any) => void;
|
||||||
|
onClick?: (e:any, checked:boolean) => void;
|
||||||
value?: any;
|
value?: any;
|
||||||
containerClass?: string;
|
containerClass?: string;
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
|
@ -44,13 +47,8 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: CheckboxProps) {
|
@autobind
|
||||||
super(props);
|
handleCheck(e: React.ChangeEvent<any>) {
|
||||||
|
|
||||||
this.hanldeCheck = this.hanldeCheck.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
hanldeCheck(e: React.ChangeEvent<any>) {
|
|
||||||
const {trueValue, falseValue, onChange} = this.props;
|
const {trueValue, falseValue, onChange} = this.props;
|
||||||
|
|
||||||
if (!onChange) {
|
if (!onChange) {
|
||||||
|
@ -60,6 +58,25 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||||
onChange(e.currentTarget.checked ? trueValue : falseValue);
|
onChange(e.currentTarget.checked ? trueValue : falseValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleClick(e:any) {
|
||||||
|
const {
|
||||||
|
checked,
|
||||||
|
value,
|
||||||
|
trueValue,
|
||||||
|
onClick,
|
||||||
|
disabled
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const isChecked:boolean = !!(typeof checked !== 'undefined'
|
||||||
|
? checked
|
||||||
|
: typeof value === 'undefined'
|
||||||
|
? value
|
||||||
|
: value == trueValue);
|
||||||
|
|
||||||
|
disabled ? null : onClick && onClick(e, isChecked);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {
|
let {
|
||||||
size,
|
size,
|
||||||
|
@ -88,6 +105,7 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
onClick={this.handleClick}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
|
@ -98,7 +116,7 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||||
? value
|
? value
|
||||||
: value == trueValue
|
: value == trueValue
|
||||||
}
|
}
|
||||||
onChange={this.hanldeCheck}
|
onChange={this.handleCheck}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
name={name}
|
name={name}
|
||||||
|
@ -110,4 +128,4 @@ export class Checkbox extends React.Component<CheckboxProps, any> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default themeable(Checkbox);
|
export default themeable(Checkbox);
|
|
@ -16,7 +16,10 @@ interface RangeProps extends RendererProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
value?: number;
|
value: {
|
||||||
|
min: number,
|
||||||
|
max: number
|
||||||
|
} | number;
|
||||||
classPrefix: string;
|
classPrefix: string;
|
||||||
classnames: ClassNamesFn;
|
classnames: ClassNamesFn;
|
||||||
}
|
}
|
||||||
|
@ -28,10 +31,10 @@ export class Range extends React.Component<RangeProps, any> {
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {min, max, value, className, classPrefix: ns} = this.props;
|
const {min, max, value, className, classPrefix: ns, multiple} = this.props;
|
||||||
|
|
||||||
const classNames = {
|
const classNames = {
|
||||||
activeTrack: `${ns}InputRange-track is-active`,
|
activeTrack: multiple ? `${ns}InputRange-track is-active` : `${ns}InputRange-track`,
|
||||||
disabledInputRange: `${ns}InputRange is-disabled`,
|
disabledInputRange: `${ns}InputRange is-disabled`,
|
||||||
inputRange: `${ns}InputRange`,
|
inputRange: `${ns}InputRange`,
|
||||||
labelContainer: `${ns}InputRange-labelContainer`,
|
labelContainer: `${ns}InputRange-labelContainer`,
|
||||||
|
@ -50,7 +53,8 @@ export class Range extends React.Component<RangeProps, any> {
|
||||||
classNames={classNames}
|
classNames={classNames}
|
||||||
minValue={min}
|
minValue={min}
|
||||||
maxValue={max}
|
maxValue={max}
|
||||||
value={typeof value === 'number' ? value : min}
|
value={value}
|
||||||
|
multiple={multiple}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,14 @@ import Downshift, {ControllerStateAndHelpers} from 'downshift';
|
||||||
import * as cx from 'classnames';
|
import * as cx from 'classnames';
|
||||||
import {closeIcon} from './icons';
|
import {closeIcon} from './icons';
|
||||||
import * as matchSorter from 'match-sorter';
|
import * as matchSorter from 'match-sorter';
|
||||||
import {noop, anyChanged} from '../utils/helper';
|
import {noop} from '../utils/helper';
|
||||||
import find = require('lodash/find');
|
import find = require('lodash/find');
|
||||||
import isPlainObject = require('lodash/isPlainObject');
|
import isPlainObject = require('lodash/isPlainObject');
|
||||||
|
import union = require('lodash/union');
|
||||||
import {highlight} from '../renderers/Form/Options';
|
import {highlight} from '../renderers/Form/Options';
|
||||||
import {findDOMNode} from 'react-dom';
|
import {findDOMNode} from 'react-dom';
|
||||||
import {ClassNamesFn, themeable} from '../theme';
|
import {ClassNamesFn, themeable} from '../theme';
|
||||||
|
import Checkbox from './Checkbox';
|
||||||
|
|
||||||
export interface Option {
|
export interface Option {
|
||||||
label?: string;
|
label?: string;
|
||||||
|
@ -64,7 +66,7 @@ export function value2array(value: OptionValue | Array<OptionValue>, props: Part
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
value = value[0];
|
value = value[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
let expandedValue = expandValue(value as OptionValue, props);
|
let expandedValue = expandValue(value as OptionValue, props);
|
||||||
return expandedValue ? [expandedValue] : [];
|
return expandedValue ? [expandedValue] : [];
|
||||||
}
|
}
|
||||||
|
@ -148,6 +150,9 @@ interface SelectProps {
|
||||||
onNewOptionClick: (value: Option) => void;
|
onNewOptionClick: (value: Option) => void;
|
||||||
onFocus?: Function;
|
onFocus?: Function;
|
||||||
onBlur?: Function;
|
onBlur?: Function;
|
||||||
|
checkAll?: boolean;
|
||||||
|
checkAllLabel?: string;
|
||||||
|
defaultCheckAll?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SelectState {
|
interface SelectState {
|
||||||
|
@ -156,6 +161,8 @@ interface SelectState {
|
||||||
inputValue: string;
|
inputValue: string;
|
||||||
highlightedIndex: number;
|
highlightedIndex: number;
|
||||||
selection: Array<Option>;
|
selection: Array<Option>;
|
||||||
|
checkedAll: boolean;
|
||||||
|
checkedPartial: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Select extends React.Component<SelectProps, SelectState> {
|
export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
|
@ -176,6 +183,9 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
onNewOptionClick: noop,
|
onNewOptionClick: noop,
|
||||||
inline: false,
|
inline: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
checkAll: false,
|
||||||
|
checkAllLabel: '全选',
|
||||||
|
defaultCheckAll: false
|
||||||
};
|
};
|
||||||
|
|
||||||
input: HTMLInputElement;
|
input: HTMLInputElement;
|
||||||
|
@ -196,6 +206,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
this.handleStateChange = this.handleStateChange.bind(this);
|
this.handleStateChange = this.handleStateChange.bind(this);
|
||||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||||
this.getTarget = this.getTarget.bind(this);
|
this.getTarget = this.getTarget.bind(this);
|
||||||
|
this.toggleCheckAll = this.toggleCheckAll.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
|
@ -203,11 +214,34 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
highlightedIndex: -1,
|
highlightedIndex: -1,
|
||||||
selection: value2array(props.value, props),
|
selection: value2array(props.value, props),
|
||||||
|
checkedAll: false,
|
||||||
|
checkedPartial: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {loadOptions} = this.props;
|
const {loadOptions, options, checkAll, defaultCheckAll} = this.props;
|
||||||
|
let {selection} = this.state;
|
||||||
|
|
||||||
|
if (checkAll && options.length) {
|
||||||
|
let checkedAll = false;
|
||||||
|
if (selection.length) {
|
||||||
|
const optionsValues = options.map(option => option.value);
|
||||||
|
const selectionValues = selection.map(select => select.value);
|
||||||
|
checkedAll = optionsValues.every(option => selectionValues.indexOf(option) > -1);
|
||||||
|
}
|
||||||
|
if (defaultCheckAll) {
|
||||||
|
checkedAll = true;
|
||||||
|
selection = union(options, selection);
|
||||||
|
}
|
||||||
|
if (checkedAll) {
|
||||||
|
this.setState({
|
||||||
|
checkedAll: true,
|
||||||
|
checkedPartial: true,
|
||||||
|
selection: selection
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loadOptions && loadOptions('');
|
loadOptions && loadOptions('');
|
||||||
}
|
}
|
||||||
|
@ -282,13 +316,44 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
this.input = ref;
|
this.input = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleCheckAll() {
|
||||||
|
let {options, onChange} = this.props;
|
||||||
|
const {checkedAll} = this.state;
|
||||||
|
|
||||||
|
if (!checkedAll) {
|
||||||
|
this.setState({
|
||||||
|
selection: options,
|
||||||
|
checkedAll: true,
|
||||||
|
checkedPartial: true,
|
||||||
|
isOpen: true
|
||||||
|
});
|
||||||
|
onChange(options);
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
selection: [],
|
||||||
|
checkedAll: false,
|
||||||
|
checkedPartial: false,
|
||||||
|
isOpen: true
|
||||||
|
});
|
||||||
|
onChange([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
removeItem(index: number, e?: React.MouseEvent<HTMLElement>) {
|
removeItem(index: number, e?: React.MouseEvent<HTMLElement>) {
|
||||||
let value = this.props.value;
|
const {onChange, options, checkAll} = this.props;
|
||||||
const onChange = this.props.onChange;
|
let {selection: value} = this.state;
|
||||||
|
|
||||||
e && e.stopPropagation();
|
e && e.stopPropagation();
|
||||||
value = Array.isArray(value) ? value.concat() : [value];
|
value = Array.isArray(value) ? value.concat() : [value];
|
||||||
value.splice(index, 1);
|
value.splice(index, 1);
|
||||||
|
|
||||||
|
if (checkAll && (!value.length || value.length < options.length)) {
|
||||||
|
const checkedPartial = value.some((item:Option) => options.indexOf(item) > -1);
|
||||||
|
this.setState({
|
||||||
|
checkedAll: false,
|
||||||
|
checkedPartial: checkedPartial
|
||||||
|
});
|
||||||
|
}
|
||||||
onChange(value);
|
onChange(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,9 +369,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(selectItem: any) {
|
handleChange(selectItem: any) {
|
||||||
const {onChange, multiple, onNewOptionClick} = this.props;
|
const {onChange, multiple, onNewOptionClick, options, checkAll} = this.props;
|
||||||
|
let {checkedAll, selection, checkedPartial} = this.state;
|
||||||
let selection = this.state.selection;
|
|
||||||
|
|
||||||
if (selectItem.isNew) {
|
if (selectItem.isNew) {
|
||||||
delete selectItem.isNew;
|
delete selectItem.isNew;
|
||||||
|
@ -314,20 +378,46 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multiple) {
|
if (multiple) {
|
||||||
selection = selection.concat();
|
if (checkAll) {
|
||||||
const idx = selection.indexOf(selectItem);
|
if (selectItem.__all) {
|
||||||
if (~idx) {
|
this.toggleCheckAll();
|
||||||
selection.splice(idx, 1);
|
} else {
|
||||||
|
selection = selection.concat();
|
||||||
|
const idx = selection.indexOf(selectItem);
|
||||||
|
if (~idx) {
|
||||||
|
selection.splice(idx, 1);
|
||||||
|
} else {
|
||||||
|
selection.push(selectItem);
|
||||||
|
}
|
||||||
|
onChange(selection);
|
||||||
|
|
||||||
|
const optionsValues = options.map(option => option.value);
|
||||||
|
const selectionValues = selection.map(select => select.value);
|
||||||
|
checkedAll = optionsValues.every(option => selectionValues.indexOf(option) > -1);
|
||||||
|
checkedPartial = optionsValues.some(option => selectionValues.indexOf(option) > -1);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
checkedAll,
|
||||||
|
checkedPartial
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
selection.push(selectItem);
|
selection = selection.concat();
|
||||||
|
const idx = selection.indexOf(selectItem);
|
||||||
|
if (~idx) {
|
||||||
|
selection.splice(idx, 1);
|
||||||
|
} else {
|
||||||
|
selection.push(selectItem);
|
||||||
|
}
|
||||||
|
onChange(selection);
|
||||||
}
|
}
|
||||||
onChange(selection);
|
|
||||||
} else {
|
} else {
|
||||||
onChange(selectItem);
|
onChange(selectItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStateChange(changes: any) {
|
handleStateChange(changes: any) {
|
||||||
|
const {multiple, checkAll} = this.props;
|
||||||
let update: any = {};
|
let update: any = {};
|
||||||
const loadOptions = this.props.loadOptions;
|
const loadOptions = this.props.loadOptions;
|
||||||
let doLoad = false;
|
let doLoad = false;
|
||||||
|
@ -346,8 +436,8 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
update = {
|
update = {
|
||||||
...update,
|
...update,
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
isOpen: false,
|
isOpen: multiple && checkAll ? true : false,
|
||||||
isFocused: false,
|
isFocused: multiple && checkAll ? true : false
|
||||||
};
|
};
|
||||||
doLoad = true;
|
doLoad = true;
|
||||||
break;
|
break;
|
||||||
|
@ -419,7 +509,10 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
promptTextCreator,
|
promptTextCreator,
|
||||||
multiple,
|
multiple,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
|
checkAll,
|
||||||
|
checkAllLabel,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const {selection, checkedAll, checkedPartial} = this.state;
|
||||||
|
|
||||||
let filtedOptions: Array<Option> =
|
let filtedOptions: Array<Option> =
|
||||||
inputValue && isOpen && !loadOptions
|
inputValue && isOpen && !loadOptions
|
||||||
|
@ -429,7 +522,11 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
: options.concat();
|
: options.concat();
|
||||||
|
|
||||||
if (multiple) {
|
if (multiple) {
|
||||||
filtedOptions = filtedOptions.filter((option: any) => !~selectedItem.indexOf(option));
|
if (checkAll) {
|
||||||
|
filtedOptions.unshift({label: checkAllLabel, value: 'all', __all: true});
|
||||||
|
} else {
|
||||||
|
filtedOptions = filtedOptions.filter((option: any) => !~selectedItem.indexOf(option));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -452,29 +549,54 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
const menu = (
|
const menu = (
|
||||||
<div className={cx('Select-menu')}>
|
<div className={cx('Select-menu')}>
|
||||||
{filtedOptions.length ? (
|
{filtedOptions.length ? (
|
||||||
filtedOptions.map((item, index) => (
|
filtedOptions.map((item, index) => {
|
||||||
<div
|
const checked = checkAll ? selection.some((o:Option) => o.value == item.value) : false;
|
||||||
{...getItemProps({
|
|
||||||
key: index,
|
return (
|
||||||
index,
|
<div
|
||||||
item,
|
{...getItemProps({
|
||||||
disabled: item.disabled,
|
key: index,
|
||||||
})}
|
index,
|
||||||
className={cx(`Select-option`, {
|
item,
|
||||||
'is-disabled': item.disabled,
|
disabled: item.disabled,
|
||||||
'is-highlight': highlightedIndex === index,
|
})}
|
||||||
'is-active':
|
className={cx(`Select-option`, {
|
||||||
selectedItem === item ||
|
'is-disabled': item.disabled,
|
||||||
(Array.isArray(selectedItem) && ~selectedItem.indexOf(item)),
|
'is-highlight': highlightedIndex === index,
|
||||||
})}
|
'is-checkAll': checkAll && index === 0,
|
||||||
>
|
'is-active':
|
||||||
{item.isNew
|
selectedItem === item ||
|
||||||
? promptTextCreator(item.label as string)
|
(Array.isArray(selectedItem) && ~selectedItem.indexOf(item)),
|
||||||
: item.disabled
|
})}
|
||||||
? item[labelField]
|
>
|
||||||
: highlight(item[labelField], inputValue as string, cx('Select-option-hl'))}
|
{checkAll ?
|
||||||
</div>
|
index === 0 ? (
|
||||||
))
|
<Checkbox
|
||||||
|
checked={checkedPartial}
|
||||||
|
partial={checkedPartial && !checkedAll}
|
||||||
|
>
|
||||||
|
{checkAllLabel}
|
||||||
|
</Checkbox>
|
||||||
|
) : (
|
||||||
|
<Checkbox
|
||||||
|
checked={checked}
|
||||||
|
trueValue={item.value}
|
||||||
|
>
|
||||||
|
{item.isNew
|
||||||
|
? promptTextCreator(item.label as string)
|
||||||
|
: item.disabled
|
||||||
|
? item[labelField]
|
||||||
|
: highlight(item[labelField], inputValue as string, cx('Select-option-hl'))}
|
||||||
|
</Checkbox>
|
||||||
|
) : (
|
||||||
|
item.isNew
|
||||||
|
? promptTextCreator(item.label as string)
|
||||||
|
: item.disabled
|
||||||
|
? item[labelField]
|
||||||
|
: highlight(item[labelField], inputValue as string, cx('Select-option-hl'))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)})
|
||||||
) : (
|
) : (
|
||||||
<div className={cx('Select-option Select-option--placeholder')}>{noResultsText}</div>
|
<div className={cx('Select-option Select-option--placeholder')}>{noResultsText}</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import isNumber = require('lodash/isNumber');
|
||||||
|
import isObject = require('lodash/isObject');
|
||||||
|
import isEqual = require('lodash/isEqual');
|
||||||
import {
|
import {
|
||||||
FormItem,
|
FormItem,
|
||||||
FormControlProps
|
FormControlProps
|
||||||
|
@ -17,7 +20,11 @@ export interface RangeProps extends FormControlProps {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
showInput?: boolean;
|
showInput?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
onChange: (value: number) => void;
|
value: any;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
multiple?: boolean;
|
||||||
|
joinValues?: boolean;
|
||||||
|
delimiter?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface DefaultProps {
|
export interface DefaultProps {
|
||||||
|
@ -28,12 +35,38 @@ export interface DefaultProps {
|
||||||
clearable: boolean;
|
clearable: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
showInput: boolean;
|
showInput: boolean;
|
||||||
|
multiple: boolean;
|
||||||
|
joinValues: boolean;
|
||||||
|
delimiter: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function formatValue(value: string | number | {min: number, max: number}, props: Partial<RangeProps>) {
|
||||||
|
if (props.multiple) {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const [minValue, maxValue] = value.split(props.delimiter || ',').map(v => Number(v));
|
||||||
|
return {
|
||||||
|
min: props.min && minValue < props.min && props.min || minValue || props.min,
|
||||||
|
max: props.max && maxValue > props.max && props.max || maxValue || props.max
|
||||||
|
}
|
||||||
|
} else if (typeof value === 'object') {
|
||||||
|
return {
|
||||||
|
min: props.min && value.min < props.min && props.min || value.min || props.min,
|
||||||
|
max: props.max && value.max > props.max && props.max || value.max || props.max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value || props.min;
|
||||||
|
}
|
||||||
|
|
||||||
type PropsWithDefaults = RangeProps & DefaultProps;
|
type PropsWithDefaults = RangeProps & DefaultProps;
|
||||||
|
|
||||||
export interface RangeState {
|
export interface RangeState {
|
||||||
value: any;
|
value: {
|
||||||
|
min?: number,
|
||||||
|
max?: number
|
||||||
|
} | number | string | undefined;
|
||||||
|
minValue?: any;
|
||||||
|
maxValue?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class RangeControl extends React.PureComponent<RangeProps, RangeState> {
|
export default class RangeControl extends React.PureComponent<RangeProps, RangeState> {
|
||||||
|
@ -46,27 +79,53 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
|
||||||
unit: '',
|
unit: '',
|
||||||
clearable: true,
|
clearable: true,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
showInput: false
|
showInput: false,
|
||||||
|
multiple: false,
|
||||||
|
joinValues: true,
|
||||||
|
delimiter: ','
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props: RangeProps) {
|
constructor(props: RangeProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
const {value: propsValue, multiple, delimiter, min, max} = this.props;
|
||||||
value: parseFloat(props.value) || 0
|
const value = formatValue(propsValue, {
|
||||||
};
|
multiple,
|
||||||
|
delimiter,
|
||||||
|
min,
|
||||||
|
max
|
||||||
|
});
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
value: value,
|
||||||
|
minValue: isObject(value) ? value.min : min,
|
||||||
|
maxValue: isObject(value) ? value.max : max
|
||||||
|
};
|
||||||
this.handleChange = this.handleChange.bind(this);
|
this.handleChange = this.handleChange.bind(this);
|
||||||
this.handleEnd = this.handleEnd.bind(this);
|
this.handleEnd = this.handleEnd.bind(this);
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
this.handleInputChange = this.handleInputChange.bind(this);
|
||||||
this.rangeValue = this.rangeValue.bind(this);
|
|
||||||
this.midLabelRef = this.midLabelRef.bind(this);
|
this.midLabelRef = this.midLabelRef.bind(this);
|
||||||
|
this.clearValue = this.clearValue.bind(this);
|
||||||
|
this.handleMinInputBlur = this.handleMinInputBlur.bind(this);
|
||||||
|
this.handleMaxInputBlur = this.handleMaxInputBlur.bind(this);
|
||||||
|
this.handleMinInputChange = this.handleMinInputChange.bind(this);
|
||||||
|
this.handleMaxInputChange = this.handleMaxInputChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: RangeProps) {
|
componentWillReceiveProps(nextProps: RangeProps) {
|
||||||
const { value } = this.props;
|
const {value} = this.props;
|
||||||
if (value !== nextProps.value) {
|
const {value: nextPropsValue, multiple, delimiter, min, max} = nextProps;
|
||||||
|
if (value !== nextPropsValue) {
|
||||||
|
const value = formatValue(nextPropsValue, {
|
||||||
|
multiple,
|
||||||
|
delimiter,
|
||||||
|
min,
|
||||||
|
max
|
||||||
|
});
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
value: parseFloat(nextProps.value) || 0
|
value: value,
|
||||||
|
minValue: isObject(value) ? value.min : min,
|
||||||
|
maxValue: isObject(value) ? value.max : max
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,17 +161,42 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
|
||||||
|
|
||||||
handleChange(value: any) {
|
handleChange(value: any) {
|
||||||
this.setState({
|
this.setState({
|
||||||
value: this.getValue(value)
|
value: value,
|
||||||
|
minValue: value.min,
|
||||||
|
maxValue: value.max
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearValue() {
|
||||||
|
const {multiple, min, max} = this.props;
|
||||||
|
if (multiple) {
|
||||||
|
this.setState({
|
||||||
|
value: {
|
||||||
|
min: min,
|
||||||
|
max: max
|
||||||
|
},
|
||||||
|
minValue: min,
|
||||||
|
maxValue: max
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
value: min
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleEnd(value: any) {
|
handleEnd(value: any) {
|
||||||
|
const {multiple, joinValues, delimiter} = this.props;
|
||||||
|
let endValue = value;
|
||||||
|
if (multiple && joinValues) {
|
||||||
|
endValue = [value.min, value.max].join(delimiter || ',');
|
||||||
|
}
|
||||||
const {
|
const {
|
||||||
onChange
|
onChange
|
||||||
} = this.props;
|
} = this.props;
|
||||||
this.setState({
|
this.setState({
|
||||||
value
|
value
|
||||||
}, () => onChange(value));
|
}, () => onChange(endValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
getStepPrecision() {
|
getStepPrecision() {
|
||||||
|
@ -125,25 +209,39 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
|
||||||
: step.toString().split(".")[1].length;
|
: step.toString().split(".")[1].length;
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue(value: any) {
|
getValue(value: any, type?: string) {
|
||||||
const {
|
const {
|
||||||
max,
|
max,
|
||||||
min,
|
min,
|
||||||
step
|
step
|
||||||
} = this.props as PropsWithDefaults;
|
} = this.props as PropsWithDefaults;
|
||||||
|
const {value: stateValue} = this.state;
|
||||||
|
|
||||||
if (value === '' || value === '-' || new RegExp('^[-]?\\d+[.]{1}[0]{0,' + this.getStepPrecision() + '}$').test(value)) {
|
if (value === '' || value === '-' || new RegExp('^[-]?\\d+[.]{1}[0]{0,' + this.getStepPrecision() + '}$').test(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!value || value === '00') {
|
|
||||||
// return min;
|
|
||||||
// }
|
|
||||||
|
|
||||||
value = Math.round(parseFloat(value) / step) * step;
|
value = Math.round(parseFloat(value) / step) * step;
|
||||||
value = (step < 1) ? parseFloat(value.toFixed(this.getStepPrecision())) : ~~value;
|
value = (step < 1) ? parseFloat(value.toFixed(this.getStepPrecision())) : ~~value;
|
||||||
|
|
||||||
return (value < min && min) || (value > max && max) || value;
|
switch(type) {
|
||||||
|
case 'min':
|
||||||
|
{
|
||||||
|
if (isObject(stateValue) && isNumber(stateValue.max)) {
|
||||||
|
if (value >= stateValue.max && (min <= stateValue.max - step)) {
|
||||||
|
return stateValue.max - step;
|
||||||
|
}
|
||||||
|
if (value < stateValue.max - step) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
case 'max':
|
||||||
|
return isObject(stateValue) && isNumber(stateValue.min) ? (value > max && max) || (value <= stateValue.min && (stateValue.min + step)) || value : max;
|
||||||
|
default:
|
||||||
|
return (value < min && min) || (value > max && max) || value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInputChange(evt: React.ChangeEvent<HTMLInputElement>) {
|
handleInputChange(evt: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
@ -152,12 +250,44 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
rangeValue() {
|
handleMinInputBlur(evt: React.ChangeEvent<HTMLInputElement>) {
|
||||||
let { value } = this.state;
|
const minValue = this.getValue(evt.target.value, 'min');
|
||||||
// if (/^\d+[.]{1}$/.test(value)) {
|
const {value} = this.state;
|
||||||
// return this.props.min;
|
isObject(value) ?
|
||||||
// }
|
this.setState({
|
||||||
return parseFloat(value);
|
value: {
|
||||||
|
min: minValue,
|
||||||
|
max: value.max
|
||||||
|
},
|
||||||
|
minValue: minValue
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMaxInputBlur(evt: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
const maxValue = this.getValue(evt.target.value, 'max');
|
||||||
|
const {value} = this.state;
|
||||||
|
isObject(value) ?
|
||||||
|
this.setState({
|
||||||
|
value: {
|
||||||
|
min: value.min,
|
||||||
|
max: maxValue
|
||||||
|
},
|
||||||
|
maxValue: maxValue
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMinInputChange(evt: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
this.setState({
|
||||||
|
minValue: evt.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMaxInputChange(evt: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
this.setState({
|
||||||
|
maxValue: evt.target.value
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -170,8 +300,8 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
|
||||||
name,
|
name,
|
||||||
disabled,
|
disabled,
|
||||||
className,
|
className,
|
||||||
onChange,
|
|
||||||
showInput,
|
showInput,
|
||||||
|
multiple,
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
classPrefix: ns,
|
classPrefix: ns,
|
||||||
} = this.props as PropsWithDefaults;
|
} = this.props as PropsWithDefaults;
|
||||||
|
@ -180,10 +310,11 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
|
||||||
<div className={cx("RangeControl", {
|
<div className={cx("RangeControl", {
|
||||||
'RangeControl--withInput': showInput,
|
'RangeControl--withInput': showInput,
|
||||||
'RangeControl--clearable': clearable,
|
'RangeControl--clearable': clearable,
|
||||||
|
'is-multiple': multiple
|
||||||
}, className)}>
|
}, className)}>
|
||||||
<InputRange
|
<InputRange
|
||||||
classPrefix={ns}
|
classPrefix={ns}
|
||||||
value={this.rangeValue()}
|
value={this.state.value}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
onChangeComplete={this.handleEnd}
|
onChangeComplete={this.handleEnd}
|
||||||
|
@ -191,6 +322,7 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
|
||||||
min={min}
|
min={min}
|
||||||
step={step}
|
step={step}
|
||||||
formatLabel={(value: any) => value + unit}
|
formatLabel={(value: any) => value + unit}
|
||||||
|
multiple={multiple}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<span className={cx("InputRange-label InputRange-label--mid")} ref={this.midLabelRef}>
|
<span className={cx("InputRange-label InputRange-label--mid")} ref={this.midLabelRef}>
|
||||||
|
@ -199,38 +331,46 @@ export default class RangeControl extends React.PureComponent<RangeProps, RangeS
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{showInput ? (
|
{showInput ?
|
||||||
<div className={cx("InputRange-input")}>
|
multiple && isObject(this.state.value) ? (
|
||||||
<input
|
<div className={cx("InputRange-input is-multiple")}>
|
||||||
className={this.state.value !== min ? 'is-active' : ''}
|
<input
|
||||||
type="text"
|
className={this.state.value.min !== min ? 'is-active' : ''}
|
||||||
name={name}
|
type="text"
|
||||||
value={this.state.value}
|
name={name}
|
||||||
disabled={disabled}
|
value={this.state.minValue}
|
||||||
onChange={this.handleInputChange}
|
disabled={disabled}
|
||||||
/>
|
onChange={this.handleMinInputChange}
|
||||||
{/* <span
|
onBlur={this.handleMinInputBlur}
|
||||||
className={cx("InputRange-unit", this.state.value !== min ? 'is-active' : '')}
|
/>
|
||||||
>
|
<span className={cx("InputRange-input-separator")}> - </span>
|
||||||
{unit}
|
<input
|
||||||
</span> */}
|
className={this.state.value.max !== max ? 'is-active' : ''}
|
||||||
</div>
|
type="text"
|
||||||
) : null}
|
name={name}
|
||||||
|
value={this.state.maxValue}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={this.handleMaxInputChange}
|
||||||
|
onBlur={this.handleMaxInputBlur}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={cx("InputRange-input")}>
|
||||||
|
<input
|
||||||
|
className={this.state.value !== min ? 'is-active' : ''}
|
||||||
|
type="text"
|
||||||
|
name={name}
|
||||||
|
value={!isObject(this.state.value) ? this.state.value : 0}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={this.handleInputChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
|
||||||
{/* {clearable && this.sliderValue() ? (
|
{clearable && showInput ? (
|
||||||
<span
|
<a onClick={() => this.clearValue()} className={cx("InputRange-clear", {
|
||||||
className={cx('icon icon-clear', {
|
'is-active': (multiple ? isEqual(this.state.value, {min: min, max: max}) : this.state.value !== min)
|
||||||
'active': (this.state.value !== min)
|
|
||||||
})}
|
|
||||||
onClick={() => this.handleChange(0)}
|
|
||||||
>
|
|
||||||
<i className="iconfont icon-x"></i>
|
|
||||||
</span>
|
|
||||||
) : null} */}
|
|
||||||
|
|
||||||
{clearable && this.rangeValue() !== min && showInput ? (
|
|
||||||
<a onClick={() => this.handleChange(min)} className={cx("InputRange-clear", {
|
|
||||||
'is-active': (this.state.value !== min)
|
|
||||||
})}>{closeIcon}</a>
|
})}>{closeIcon}</a>
|
||||||
) : null}
|
) : null}
|
||||||
</div >
|
</div >
|
||||||
|
|
Loading…
Reference in New Issue