diff --git a/examples/components/Page/Form.jsx b/examples/components/Page/Form.jsx index ed3a757a..f2f880f4 100644 --- a/examples/components/Page/Form.jsx +++ b/examples/components/Page/Form.jsx @@ -1,3 +1,6 @@ +import React from 'react'; +import ResultBox from '../../../src/components/ResultBox'; + export default { type: 'page', title: '表单页面', @@ -17,6 +20,38 @@ export default { label: 'Email', type: 'email', name: 'email' + }, + + { + name: 'checkboxes', + type: 'checkboxes', + joinValues: false, + options: [ + { + label: '张学友', + value: 'a' + }, + { + label: '刘德华', + value: 'b' + }, + { + label: '黎明', + value: 'c' + }, + { + label: '郭富城', + value: 'd' + } + ] + }, + + { + label: 'Result', + name: 'checkboxes', + component: ({value, onChange}) => { + return ; + } } ] } diff --git a/scss/_mixins.scss b/scss/_mixins.scss index da6f8713..232a42fc 100644 --- a/scss/_mixins.scss +++ b/scss/_mixins.scss @@ -279,3 +279,106 @@ border-color: $active-border; } } + +@mixin input-clear { + padding: px2rem(3px); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + + svg { + fill: $Form-input-iconColor; + top: 0; + width: px2rem(10px); + height: px2rem(10px); + } + + &:hover svg { + fill: darken($color: $Form-input-iconColor, $amount: 20%); + } +} + +@mixin input-input { + display: flex; + background-color: $Form-input-bg; + border: $Form-input-borderWidth solid $Form-input-borderColor; + border-radius: $Form-input-borderRadius; + // height: $Form-input-height; + line-height: $Form-input-lineHeight; + padding: $Form-input-paddingY $Form-input-paddingX; + font-size: $Form-input-fontSize; + flex-wrap: wrap; + + input { + flex-basis: px2rem(80px); + flex-grow: 1; + outline: none; + background: transparent; + border: none; + color: $Form-input-color; + width: 100%; + height: $Form-input-lineHeight * $Form-input-fontSize; + + &::placeholder { + color: $Form-input-placeholderColor; + user-select: none; + } + } +} + +@mixin input-text { + position: relative; + min-width: $Form-control-widthBase; + + &.is-inline { + display: inline-block; + } + + &-input { + @include input-input(); + } + + &.is-error > &-input { + border-color: $Form-input-onError-borderColor; + background-color: $Form-input-onError-bg; + } + + &.is-focused > &-input { + border-color: $Form-input-onFocused-borderColor; + box-shadow: $Form-input-boxShadow; + + @if $Form-input-onFocused-bg !=$Form-input-bg { + background-color: $Form-input-onFocused-bg; + } + } + + &.is-error.is-focused > &-input { + border-color: $Form-input-onError-borderColor; + } + + &.is-disabled > &-input { + color: $text--muted-color; + background: $Form-input-onDisabled-bg; + border-color: $Form-input-onDisabled-borderColor; + } + + &-spinner { + line-height: $Form-input-lineHeight * $Form-input-fontSize; + } + + &-clear { + @include input-clear(); + } + + // 需要能撑开 + @include media-breakpoint-up(sm) { + &.#{$ns}Form-control--sizeXs > &-input, + &.#{$ns}Form-control--sizeSm > &-input, + &.#{$ns}Form-control--sizeMd > &-input, + &.#{$ns}Form-control--sizeLg > &-input { + min-width: 100%; + display: inline-flex; + } + } +} diff --git a/scss/_variables.scss b/scss/_variables.scss index 47077e1b..240011d2 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -589,6 +589,14 @@ $Remark-borderColor: $borderColor !default; $Remark-onHover-borderColor: $borderColor !default; $Remark-marginLeft: $gap-sm !default; +$ResultBox-value-bg: #f5f5f5 !default; +$ResultBox-value--onHover-bg: #ebebeb !default; +$ResultBox-value-color: #000 !default; +$ResultBox-value--onDisabled-color: #cccccc !default; +$ResultBox-icon-color: #999 !default; +$ResultBox-icon--onHover-color: #666666 !default; +$ResultBox-icon--onDisabled-color: #ebebeb !default; + // Form $Form-fontSize: $fontSizeBase !default; $Form-description-color: lighten($text-color, 10%) !default; diff --git a/scss/components/form/_input-box.scss b/scss/components/_input-box.scss similarity index 100% rename from scss/components/form/_input-box.scss rename to scss/components/_input-box.scss diff --git a/scss/components/_result-box.scss b/scss/components/_result-box.scss new file mode 100644 index 00000000..754e43d4 --- /dev/null +++ b/scss/components/_result-box.scss @@ -0,0 +1,95 @@ +.#{$ns}ResultBox { + @include input-input(); + flex-wrap: wrap; + padding: 0 3px; + min-height: $Form-input-height; + align-items: center; + + &.is-error { + border-color: $Form-input-onError-borderColor; + background-color: $Form-input-onError-bg; + } + + &.is-focused { + border-color: $Form-input-onFocused-borderColor; + box-shadow: $Form-input-boxShadow; + + @if $Form-input-onFocused-bg !=$Form-input-bg { + background-color: $Form-input-onFocused-bg; + } + } + + &.is-error.is-focused { + border-color: $Form-input-onError-borderColor; + } + + &.is-disabled { + color: $text--muted-color; + background: $Form-input-onDisabled-bg; + border-color: $Form-input-onDisabled-borderColor; + } + + &-clear { + @include input-clear(); + } + + > svg { + display: inline-block; + width: 14px; + color: $icon-color; + } + + > a { + cursor: pointer; + } + + &-mid { + flex-grow: 1; + } + + &-value { + background: $ResultBox-value-bg; + color: $ResultBox-value-color; + font-size: $Form-input-fontSize; + padding: 0 5px; + min-height: 24px; + flex-wrap: nowrap; + display: inline-flex; + align-items: center; + margin: 2px 3px; + user-select: none; + + > a { + cursor: pointer; + margin-left: 5px; + color: $ResultBox-icon-color; + &:hover { + color: $ResultBox-icon--onHover-color; + } + } + + &:hover { + background: $ResultBox-value--onHover-bg; + } + + &.is-disabled { + pointer-events: none; + color: $ResultBox-value--onDisabled-color; + + > a { + color: $ResultBox-icon--onDisabled-color; + } + } + } + + &-placeholder { + color: $Form-input-placeholderColor; + user-select: none; + margin-left: 8px; + } + + > input { + padding-left: 8px; + min-height: 24px; + } +} diff --git a/scss/components/form/_text.scss b/scss/components/form/_text.scss index 36d0d002..9510116b 100644 --- a/scss/components/form/_text.scss +++ b/scss/components/form/_text.scss @@ -1,106 +1,3 @@ -@mixin input-clear { - padding: px2rem(3px); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - - svg { - fill: $Form-input-iconColor; - top: 0; - width: px2rem(10px); - height: px2rem(10px); - } - - &:hover svg { - fill: darken($color: $Form-input-iconColor, $amount: 20%); - } -} - -@mixin input-input { - display: flex; - background-color: $Form-input-bg; - border: $Form-input-borderWidth solid $Form-input-borderColor; - border-radius: $Form-input-borderRadius; - // height: $Form-input-height; - line-height: $Form-input-lineHeight; - padding: $Form-input-paddingY $Form-input-paddingX; - font-size: $Form-input-fontSize; - flex-wrap: wrap; - - input { - flex-basis: px2rem(80px); - flex-grow: 1; - outline: none; - background: transparent; - border: none; - color: $Form-input-color; - width: 100%; - height: $Form-input-lineHeight * $Form-input-fontSize; - - &::placeholder { - color: $Form-input-placeholderColor; - user-select: none; - } - } -} - -@mixin input-text { - position: relative; - min-width: $Form-control-widthBase; - - &.is-inline { - display: inline-block; - } - - &-input { - @include input-input(); - } - - &.is-error > &-input { - border-color: $Form-input-onError-borderColor; - background-color: $Form-input-onError-bg; - } - - &.is-focused > &-input { - border-color: $Form-input-onFocused-borderColor; - box-shadow: $Form-input-boxShadow; - - @if $Form-input-onFocused-bg !=$Form-input-bg { - background-color: $Form-input-onFocused-bg; - } - } - - &.is-error.is-focused > &-input { - border-color: $Form-input-onError-borderColor; - } - - &.is-disabled > &-input { - color: $text--muted-color; - background: $Form-input-onDisabled-bg; - border-color: $Form-input-onDisabled-borderColor; - } - - &-spinner { - line-height: $Form-input-lineHeight * $Form-input-fontSize; - } - - &-clear { - @include input-clear(); - } - - // 需要能撑开 - @include media-breakpoint-up(sm) { - &.#{$ns}Form-control--sizeXs > &-input, - &.#{$ns}Form-control--sizeSm > &-input, - &.#{$ns}Form-control--sizeMd > &-input, - &.#{$ns}Form-control--sizeLg > &-input { - min-width: 100%; - display: inline-flex; - } - } -} - .#{$ns}TextControl { @include input-text(); diff --git a/scss/themes/cxd.scss b/scss/themes/cxd.scss index b2c46294..feb65471 100644 --- a/scss/themes/cxd.scss +++ b/scss/themes/cxd.scss @@ -539,6 +539,8 @@ $Combo--horizontal-dragger-top: px2rem(5px); @import '../components/carousel'; @import '../components/image-gallery'; @import '../components/images'; +@import '../components/input-box'; +@import '../components/result-box'; @import '../components/form/fieldset'; @import '../components/form/group'; @@ -558,7 +560,7 @@ $Combo--horizontal-dragger-top: px2rem(5px); @import '../components/form/date'; @import '../components/form/date-range'; @import '../components/form/image'; -@import '../components/form/input-box'; + @import '../components/form/file'; @import '../components/form/editor'; @import '../components/form/rich-text'; diff --git a/scss/themes/dark.scss b/scss/themes/dark.scss index 8daefc3b..125289d9 100644 --- a/scss/themes/dark.scss +++ b/scss/themes/dark.scss @@ -202,6 +202,8 @@ pre { @import '../components/carousel'; @import '../components/image-gallery'; @import '../components/images'; +@import '../components/input-box'; +@import '../components/result-box'; @import '../components/form/fieldset'; @import '../components/form/group'; @@ -221,7 +223,7 @@ pre { @import '../components/form/date'; @import '../components/form/date-range'; @import '../components/form/image'; -@import '../components/form/input-box'; + @import '../components/form/file'; @import '../components/form/editor'; @import '../components/form/rich-text'; diff --git a/scss/themes/default.scss b/scss/themes/default.scss index 4dd8dda8..8f474714 100644 --- a/scss/themes/default.scss +++ b/scss/themes/default.scss @@ -67,6 +67,8 @@ $Form-input-borderColor: #cfdadd; @import '../components/carousel'; @import '../components/image-gallery'; @import '../components/images'; +@import '../components/input-box'; +@import '../components/result-box'; @import '../components/form/fieldset'; @import '../components/form/group'; @@ -86,7 +88,7 @@ $Form-input-borderColor: #cfdadd; @import '../components/form/date'; @import '../components/form/date-range'; @import '../components/form/image'; -@import '../components/form/input-box'; + @import '../components/form/file'; @import '../components/form/editor'; @import '../components/form/rich-text'; diff --git a/src/components/ResultBox.tsx b/src/components/ResultBox.tsx new file mode 100644 index 00000000..32034e33 --- /dev/null +++ b/src/components/ResultBox.tsx @@ -0,0 +1,146 @@ +import {ThemeProps, themeable} from '../theme'; +import React from 'react'; +import {InputBoxProps} from './InputBox'; +import uncontrollable from 'uncontrollable'; +import {Icon} from './icons'; +import Input from './Input'; +import {autobind} from '../utils/helper'; + +export interface ResultBoxProps + extends ThemeProps, + Omit { + value?: Array; + itemRender: (value: any) => JSX.Element; + onChange?: (value: Array) => void; + + allowInput?: boolean; + inputValue?: string; + onInputChange?: (value: string) => void; +} + +export class ResultBox extends React.Component { + static defaultProps: Pick< + ResultBoxProps, + 'clearable' | 'placeholder' | 'itemRender' + > = { + clearable: false, + placeholder: '暂无结果', + itemRender: (option: any) => {String(option.label)} + }; + + state = { + isFocused: false + }; + + @autobind + clearValue() { + const onChange = this.props.onChange; + onChange && onChange([]); + } + + @autobind + handleInputChange(e: React.ChangeEvent) { + const onInputChange = this.props.onInputChange; + onInputChange && onInputChange(e.currentTarget.value); + } + + @autobind + handleFocus(e: any) { + const onFocus = this.props.onFocus; + onFocus && onFocus(e); + this.setState({ + isFocused: true + }); + } + + @autobind + handleBlur(e: any) { + const onBlur = this.props.onBlur; + onBlur && onBlur(e); + this.setState({ + isFocused: false + }); + } + + render() { + const { + className, + classnames: cx, + classPrefix, + clearable, + disabled, + hasError, + value, + placeholder, + result, + children, + itemRender, + onInputChange, + inputValue, + allowInput, + ...rest + } = this.props; + const isFocused = this.state.isFocused; + + return ( +
+ {Array.isArray(value) && value.length ? ( + value.map((item, index) => ( +
+ + {itemRender(item)} + + + + +
+ )) + ) : allowInput ? null : ( + + {placeholder || '无'} + + )} + + {allowInput ? ( + + ) : ( + + )} + + {clearable && !disabled && Array.isArray(value) && value.length ? ( + + + + ) : null} + + {children} +
+ ); + } +} + +export default themeable( + uncontrollable(ResultBox, { + value: 'onChange', + inputValue: 'onInputChange' + }) +);