diff --git a/examples/components/Form/Combo.jsx b/examples/components/Form/Combo.jsx index 72e61a04..193733c0 100644 --- a/examples/components/Form/Combo.jsx +++ b/examples/components/Form/Combo.jsx @@ -565,6 +565,52 @@ export default { ] }, + { + title: "Tabs", + hash: "tabs", + body: [ + { + type: "form", + api: "/api/mock2/saveForm?waitSeconds=2", + title: "", + mode: "horizontal", + wrapWithPanel: false, + className: "m-t", + // debug: true, + controls: [ + { + type: "combo", + name: "combo101", + label: "组合多条多行", + multiple: true, + multiLine: true, + value: [{}], + tabsMode: true, + tabsStyle: 'card', + maxLength: 3, + controls: [ + { + name: "a", + label: "文本", + type: "text", + placeholder: "文本", + value: '', + size: 'full' + }, + { + name: "b", + label: "选项", + type: "select", + options: ["a", "b", "c"], + size: 'full' + } + ] + }, + ] + } + ] + }, + { title: "其他", hash: 'others', diff --git a/scss/components/_tabs.scss b/scss/components/_tabs.scss index 251cd56c..a544fe31 100644 --- a/scss/components/_tabs.scss +++ b/scss/components/_tabs.scss @@ -12,8 +12,9 @@ > .#{$ns}Tabs-link { margin-bottom: -$Tabs-borderWidth; display: inline-block; + position: relative; - > a { + > a:first-child { font-size: $Tabs-linkFontSize; outline: none; border: $Tabs-borderWidth solid transparent; @@ -27,22 +28,33 @@ display: block; } - &:hover > a, - > a:focus { + > .#{$ns}Combo-toolbarBtn { + position: absolute; + right: -10px; + top: -10px; + z-index: 10; + display: none; + } + &:hover > .#{$ns}Combo-toolbarBtn { + display: block; + } + + &:hover > a:first-child, + > a:first-child:focus { border-color: $Tabs-onHover-borderColor; text-decoration: none; } - &.disabled > a, - &.is-disabled > a { + &.disabled > a:first-child, + &.is-disabled > a:first-child { color: $Tabs-onDisabled-color; background-color: transparent; border-color: transparent; pointer-events: none; } - &.active > a, - &.is-active > a { + &.active > a:first-child, + &.is-active > a:first-child { color: $Tabs-onActive-color; background-color: $Tabs-onActive-bg; border-color: $Tabs-onActive-borderColor; @@ -77,9 +89,9 @@ > li { &.is-active { - > a, - > a:hover, - > a:focus { + > a:first-child, + > a:first-child:hover, + > a:first-child:focus { border-bottom: px2rem(2px) solid $primary; color: $primary; background-color: transparent; @@ -88,9 +100,9 @@ } } - > a, - > a:hover, - > a:focus { + > a:first-child, + > a:first-child:hover, + > a:first-child:focus { color: #666; background-color: transparent; border-color: transparent; @@ -106,18 +118,18 @@ > li { &.is-active { - > a, - > a:hover, - > a:focus { + > a:first-child, + > a:first-child:hover, + > a:first-child:focus { color: $primary; background-color: #fff; margin-left: px2rem(1px); } } - > a, - > a:hover, - > a:focus { + > a:first-child, + > a:first-child:hover, + > a:first-child:focus { color: #666; background-color: transparent; } @@ -131,7 +143,7 @@ margin-bottom: px2rem(10px); > li { - > a { + > a:first-child { font-size: $fontSizeSm; text-align: center; margin-right: 0; @@ -141,18 +153,18 @@ } &.is-active { - > a, - > a:hover, - > a:focus { + > a:first-child, + > a:first-child:hover, + > a:first-child:focus { color: #fff; background-color: $primary; margin-left: px2rem(1px); } } - > a, - > a:hover, - > a:focus { + > a:first-child, + > a:first-child:hover, + > a:first-child:focus { color: $primary; background-color: #eaf6fe; } diff --git a/src/components/Tabs.tsx b/src/components/Tabs.tsx index 115a3140..875bacba 100644 --- a/src/components/Tabs.tsx +++ b/src/components/Tabs.tsx @@ -16,7 +16,7 @@ const transitionStyles: { [ENTERED]: 'in' }; -export interface TabProps extends Schema { +export interface TabProps { title?: string; // 标题 icon?: string; eventKey: string | number; @@ -27,11 +27,13 @@ export interface TabProps extends Schema { reload?: boolean; mountOnEnter?: boolean; unmountOnExit?: boolean; + toolbar?: React.ReactNode; }; export interface TabsProps { mode?: '' | 'line' | 'card' | 'radio'; tabsMode?: '' | 'line' | 'card' | 'radio'; + additionBtns?: React.ReactNode; handleSelect?: Function; classPrefix: string; classnames: ClassNamesFn; @@ -43,8 +45,9 @@ export interface TabsProps { } export class Tabs extends React.Component { - static defaultProps: Pick = { - mode: '' + static defaultProps:Pick = { + mode: '', + contentClassName: '' }; handleSelect(key: any) { @@ -58,7 +61,7 @@ export class Tabs extends React.Component { } const { classnames: cx, activeKey } = this.props; - const { eventKey, disabled, icon, title } = child.props; + const { eventKey, disabled, icon, title, toolbar } = child.props; return (
  • { onClick={() => disabled ? '' : this.handleSelect(eventKey)} > {icon ? : null} {title} + {toolbar}
  • ); } @@ -97,7 +101,8 @@ export class Tabs extends React.Component { className, mode: dMode, tabsMode, - children + children, + additionBtns } = this.props; if (!Array.isArray(children)) { @@ -120,6 +125,7 @@ export class Tabs extends React.Component { {children.map((tab, index) => ( this.renderNav(tab, index) ))} + {additionBtns}
    { canAccessSuperData: false, addIcon: 'fa fa-plus', dragIcon: 'glyphicon glyphicon-sort', - deleteIcon: 'glyphicon glyphicon-remove' + deleteIcon: 'glyphicon glyphicon-remove', + tabsMode: false, + tabsStyle: '' }; static propsList: Array = [ "minLength", @@ -98,7 +103,9 @@ export default class ComboControl extends React.Component { "dragIcon", "deleteIcon", "noBorder", - "conditions" + "conditions", + "tabsMode", + "tabsStyle" ]; subForms:Array = []; @@ -134,7 +141,7 @@ export default class ComboControl extends React.Component { store.config({ minLength, maxLength, - length: this.getValueAsArray().length + length: this.getValueAsArray().length, }); formItem && formItem.setSubStore(store); @@ -149,12 +156,17 @@ export default class ComboControl extends React.Component { minLength, maxLength } = nextProps; + const values = this.getValueAsArray(nextProps); store.config({ minLength, maxLength, - length: this.getValueAsArray(nextProps).length + length: values.length }); + + if (store.activeKey >= values.length) { + store.setActiveKey(values.length - 1); + } } } @@ -474,7 +486,182 @@ export default class ComboControl extends React.Component { } } + @autobind + handleTabSelect(key:number) { + const { + store + } = this.props; + + store.setActiveKey(key); + } + + renderPlaceholder() { + return ( + {this.props.placeholder || '没有数据'} + ); + } + + renderTabsMode() { + const { + classPrefix: ns, + classnames: cx, + tabsStyle, + formClassName, + render, + disabled, + store, + flat, + subFormMode, + addButtonText, + addable, + removable, + typeSwitchable, + itemRemovableOn, + delimiter, + canAccessSuperData, + addIcon, + deleteIcon, + tabsLabelTpl, + conditions + } = this.props; + + let controls = this.props.controls; + let value = this.props.value; + + if (flat && typeof value === 'string') { + value = value.split(delimiter || ','); + } + + const finnalRemovable = store.removable !== false // minLength ? + && !disabled // 控件自身是否禁用 + && removable !== false; // 是否可以删除 + + if (!Array.isArray(value)) { + return this.renderPlaceholder(); + } + + // todo 支持拖拽排序。 + + return ( + + {store.addable && addable !== false ? Array.isArray(conditions) && conditions.length ? ( + render('add-button', { + type: 'dropdown-button', + icon: addIcon, + label: addButtonText || '新增', + level: 'info', + size: 'sm', + closeOnClick: true + }, { + buttons: conditions.map(item => ({ + label: item.label, + onClick: (e:any) => { + this.addItemWith(item) + return false; + } + })) + }) + ) : ( + + {addIcon ? () : null} + {addButtonText || '新增'} + + ) : null} + + ) : null} + > + {value.map((value, index) => { + const data = this.formatValue(value, index); + let condition:Condition | null = null; + + if (Array.isArray(conditions) && conditions.length) { + condition = this.pickCondition(data); + controls = condition.controls; + } + + let finnalControls = flat ? [{ + ...controls && controls[0], + name: 'flat' + }] : controls; + + let toolbar = undefined; + if ( + finnalRemovable + && ( // 表达式判断单条是否可删除 + !itemRemovableOn + || evalExpression(itemRemovableOn, value) !== false + ) + ) { + toolbar = ( + + + + ); + } + + return ( + + {condition && typeSwitchable !== false ? ( +
    + +