From d2d2f5b173c4ece7a09a3eeaef1737cd7726c238 Mon Sep 17 00:00:00 2001 From: tangjinzhou <tangjinzhou@yidian-inc.com> Date: Fri, 1 Dec 2017 18:48:16 +0800 Subject: [PATCH] add demo --- components/tabs/InkTabBar.vue | 2 +- components/tabs/ScrollableInkTabBar.vue | 2 +- components/tabs/ScrollableTabBar.vue | 2 +- components/tabs/ScrollableTabBarMixin.js | 26 ++-- components/tabs/TabBarMixin.js | 82 ++++++++---- components/tabs/TabPane.vue | 6 +- components/tabs/Tabs.vue | 31 ++--- components/tabs/demo/card-top.vue | 69 ++++++++++ components/tabs/demo/card.vue | 25 ++++ components/tabs/demo/custom-add-trigger.vue | 69 ++++++++++ components/tabs/demo/editable-card.vue | 61 +++++++++ components/tabs/demo/extra.vue | 21 +++ components/tabs/demo/index.vue | 54 ++++++++ components/tabs/demo/position.vue | 38 ++++++ components/tabs/demo/size.vue | 16 +++ components/tabs/demo/slide.vue | 7 +- components/tabs/index.vue | 141 ++++++++------------ components/tabs/index.zh-CN | 52 ++++++++ 18 files changed, 555 insertions(+), 149 deletions(-) create mode 100644 components/tabs/demo/card-top.vue create mode 100644 components/tabs/demo/card.vue create mode 100644 components/tabs/demo/custom-add-trigger.vue create mode 100644 components/tabs/demo/editable-card.vue create mode 100644 components/tabs/demo/extra.vue create mode 100644 components/tabs/demo/index.vue create mode 100644 components/tabs/demo/position.vue create mode 100644 components/tabs/demo/size.vue create mode 100644 components/tabs/index.zh-CN diff --git a/components/tabs/InkTabBar.vue b/components/tabs/InkTabBar.vue index 48dafa1d2..bab261e8e 100644 --- a/components/tabs/InkTabBar.vue +++ b/components/tabs/InkTabBar.vue @@ -8,7 +8,7 @@ export default { render (h) { const inkBarNode = this.getInkBarNode() const tabs = this.getTabs(h) - return this.getRootNode([inkBarNode, tabs]) + return this.getRootNode([inkBarNode, tabs], h) }, } </script> diff --git a/components/tabs/ScrollableInkTabBar.vue b/components/tabs/ScrollableInkTabBar.vue index 292633176..f04450b9f 100644 --- a/components/tabs/ScrollableInkTabBar.vue +++ b/components/tabs/ScrollableInkTabBar.vue @@ -10,7 +10,7 @@ export default { const inkBarNode = this.getInkBarNode() const tabs = this.getTabs(h) const scrollbarNode = this.getScrollBarNode([inkBarNode, tabs]) - return this.getRootNode(scrollbarNode) + return this.getRootNode(scrollbarNode, h) }, } </script> diff --git a/components/tabs/ScrollableTabBar.vue b/components/tabs/ScrollableTabBar.vue index 3a1e83071..7abc2d748 100644 --- a/components/tabs/ScrollableTabBar.vue +++ b/components/tabs/ScrollableTabBar.vue @@ -9,7 +9,7 @@ export default { const inkBarNode = this.getInkBarNode() const tabs = this.getTabs(h) const scrollbarNode = this.getScrollBarNode([inkBarNode, tabs]) - return this.getRootNode(scrollbarNode) + return this.getRootNode(scrollbarNode, h) }, } </script> diff --git a/components/tabs/ScrollableTabBarMixin.js b/components/tabs/ScrollableTabBarMixin.js index 3f91dd48a..6df86318b 100644 --- a/components/tabs/ScrollableTabBarMixin.js +++ b/components/tabs/ScrollableTabBarMixin.js @@ -27,20 +27,8 @@ export default { this.resizeEvent = addDOMEventListener(window, 'resize', debouncedResize) }, - updated (prevProps) { - const props = this.$props - if (prevProps && prevProps.tabBarPosition !== props.tabBarPosition) { - this.setOffset(0) - return - } - const nextPrev = this.setNextPrev() - // wait next, prev show hide - if (this.isNextPrevShown(this) !== this.isNextPrevShown(nextPrev)) { - Object.assign(this, this.scrollToActiveTab) - } else if (!prevProps || props.activeKey !== prevProps.activeKey) { - // can not use props.activeKey - this.scrollToActiveTab() - } + updated () { + this.updatedCal() }, beforeDestroy () { @@ -48,9 +36,17 @@ export default { this.resizeEvent.remove() } }, + watch: { + tabBarPosition (val) { + this.setOffset(0) + }, + }, methods: { updatedCal () { - + this.setNextPrev() + this.$nextTick(() => { + this.scrollToActiveTab() + }) }, setNextPrev () { const navNode = this.$refs.nav diff --git a/components/tabs/TabBarMixin.js b/components/tabs/TabBarMixin.js index f70010914..7f148de68 100644 --- a/components/tabs/TabBarMixin.js +++ b/components/tabs/TabBarMixin.js @@ -1,3 +1,4 @@ +import Icon from '../icon' export default { props: { prefixCls: { @@ -19,6 +20,16 @@ export default { }, activeKey: String, panels: Array, + extraContent: [String, Number, Function], + hideAdd: Boolean, + removeTab: { + default: () => {}, + type: Function, + }, + createNewTab: { + default: () => {}, + type: Function, + }, }, methods: { getTabs (h) { @@ -28,33 +39,46 @@ export default { if (!child) { return } + let { disabled, closable } = child + const { tabKey, tab } = child // componentOptions.propsData中获取的值disabled没有根据类型初始化, 会出现空字符串 - child.disabled = child.disabled === '' || child.disabled - const key = child.tabKey - let cls = activeKey === key ? `${prefixCls}-tab-active` : '' + disabled = disabled === '' || disabled + let cls = activeKey === tabKey ? `${prefixCls}-tab-active` : '' cls += ` ${prefixCls}-tab` - if (child.disabled) { + if (disabled) { cls += ` ${prefixCls}-tab-disabled` } else { } const onClick = () => { - !child.disabled && this.onTabClick(key) + !disabled && this.onTabClick(tabKey) } - // const ref = {} - // if (activeKey === key) { - // ref.ref = this.saveRef('activeTab') - // } + + let tabC = typeof tab === 'function' ? child.tab(h, tabKey) : tab + if (this.$parent.type === 'editable-card') { + closable = closable === undefined ? true : closable === '' || closable + const closeIcon = closable ? ( + <Icon + type='close' + onClick={e => this.removeTab(tabKey, e)} + /> + ) : null + tabC = <div class={closable ? undefined : `${prefixCls}-tab-unclosable`}> + {tabC} + {closeIcon} + </div> + } + rst.push( <div role='tab' - aria-disabled={child.disabled ? 'true' : 'false'} - aria-selected={activeKey === key ? 'true' : 'false'} + aria-disabled={disabled ? 'true' : 'false'} + aria-selected={activeKey === tabKey ? 'true' : 'false'} class={cls} - key={key} + key={tabKey} onClick={onClick} - ref={activeKey === key ? 'activeTab' : undefined} + ref={activeKey === tabKey ? 'activeTab' : undefined} > - {typeof child.tab === 'function' ? child.tab(h, key) : child.tab} + {tabC} </div> ) }) @@ -63,23 +87,35 @@ export default { }, getRootNode (contents, createElement) { const { - prefixCls, onKeyDown, tabBarPosition, $slots, + prefixCls, onKeyDown, tabBarPosition, hideAdd, } = this + let extraContent = this.extraContent + const tabsType = this.$parent.type const cls = { [`${prefixCls}-bar`]: true, } const topOrBottom = (tabBarPosition === 'top' || tabBarPosition === 'bottom') const tabBarExtraContentStyle = topOrBottom ? { float: 'right' } : {} let children = contents - if ($slots.default) { - children = [ - <div key='extra' class={`${prefixCls}-extra-content`} style={tabBarExtraContentStyle}> - {$slots.default} - </div>, - contents, - ] - children = topOrBottom ? children : children.reverse() + extraContent = typeof extraContent === 'function' ? extraContent(createElement) : extraContent + + if (tabsType === 'editable-card' && !hideAdd) { + extraContent = ( + <span> + <Icon type='plus' class={`${prefixCls}-new-tab`} onClick={this.createNewTab} /> + {extraContent} + </span> + ) } + + children = [ + <div key='extra' class={`${prefixCls}-extra-content`} style={tabBarExtraContentStyle}> + {extraContent} + </div>, + contents, + ] + children = topOrBottom ? children : children.reverse() + return ( <div role='tablist' diff --git a/components/tabs/TabPane.vue b/components/tabs/TabPane.vue index 9375f89a6..5a66ec07a 100644 --- a/components/tabs/TabPane.vue +++ b/components/tabs/TabPane.vue @@ -4,7 +4,7 @@ :aria-hidden="active ? 'false' : 'true'" :class="classes" > - <slot v-if="isRender || forceRender"> + <slot v-if="isRender"> </slot> </div> </template> @@ -14,12 +14,10 @@ export default { props: { tabKey: [String, Number], tab: [String, Number, Function], - forceRender: Boolean, disabled: Boolean, - // placeholder: [Function, String, Number], + closable: Boolean, }, data () { - console.log(this.disabled) return { } }, diff --git a/components/tabs/Tabs.vue b/components/tabs/Tabs.vue index 94b61f883..e99854e27 100644 --- a/components/tabs/Tabs.vue +++ b/components/tabs/Tabs.vue @@ -45,6 +45,13 @@ export default { destroyInactiveTabPane: Boolean, activeKey: String, defaultActiveKey: String, + type: { + validator (value) { + return ['line', 'card', 'editable-card'].includes(value) + }, + }, + onChange: { type: Function, default: () => {} }, + onTabClick: { type: Function, default: () => {} }, }, data () { return { @@ -79,11 +86,8 @@ export default { } return activeKey }, - onTabClick (activeKey) { - console.log('onTabClick', activeKey) - // if (this.tabBar.props.onTabClick) { - // this.tabBar.props.onTabClick(activeKey) - // } + handleTabClick (activeKey) { + this.onTabClick(activeKey) this.setActiveKey(activeKey) }, @@ -92,11 +96,11 @@ export default { if (eventKeyCode === KeyCode.RIGHT || eventKeyCode === KeyCode.DOWN) { e.preventDefault() const nextKey = this.getNextActiveKey(true) - this.onTabClick(nextKey) + this.handleTabClick(nextKey) } else if (eventKeyCode === KeyCode.LEFT || eventKeyCode === KeyCode.UP) { e.preventDefault() const previousKey = this.getNextActiveKey(false) - this.onTabClick(previousKey) + this.handleTabClick(previousKey) } }, @@ -105,8 +109,7 @@ export default { if (!this.activeKey) { this.stateActiveKey = activeKey } - // this.stateActiveKey = activeKey - this.$emit('change', activeKey) + this.onChange(activeKey) } }, @@ -115,7 +118,7 @@ export default { const children = [] this.$slots.default.forEach(({ componentOptions = {}}) => { const c = componentOptions.propsData - if (c && !c.disabled) { + if (c && !c.disabled && c.disabled !== '') { if (next) { children.push(c) } else { @@ -145,7 +148,7 @@ export default { tabBarPosition, destroyInactiveTabPane, onNavKeyDown, - onTabClick, + handleTabClick, stateActiveKey, classes, setActiveKey, @@ -176,16 +179,14 @@ export default { prefixCls: prefixCls, onKeyDown: onNavKeyDown, tabBarPosition: tabBarPosition, - onTabClick: onTabClick, + onTabClick: handleTabClick, activeKey: stateActiveKey, key: 'tabBar', }, style: this.tabBarProps.style || {}, } const contents = [ - <ScrollableInkTabBar {...tabBarProps}> - {this.$slots.tabBarExtraContent} - </ScrollableInkTabBar>, + <ScrollableInkTabBar {...tabBarProps} />, <TabContent {...tabContentProps}> {$slots.default} </TabContent>, diff --git a/components/tabs/demo/card-top.vue b/components/tabs/demo/card-top.vue new file mode 100644 index 000000000..444ab42f8 --- /dev/null +++ b/components/tabs/demo/card-top.vue @@ -0,0 +1,69 @@ +<template> + <div class="card-container"> + <Tabs type="card"> + <TabPane tab="Tab Title 1" tabKey="1"> + <p>Content of Tab Pane 1</p> + <p>Content of Tab Pane 1</p> + <p>Content of Tab Pane 1</p> + </TabPane> + <TabPane tab="Tab Title 2" tabKey="2"> + <p>Content of Tab Pane 2</p> + <p>Content of Tab Pane 2</p> + <p>Content of Tab Pane 2</p> + </TabPane> + <TabPane tab="Tab Title 3" tabKey="3"> + <p>Content of Tab Pane 3</p> + <p>Content of Tab Pane 3</p> + <p>Content of Tab Pane 3</p> + </TabPane> + </Tabs> + </div> +</template> +<script> +import { Tabs } from 'antd' +export default { + data () { + return { + } + }, + methods: { + callback (key) { + console.log(key) + }, + }, + components: { + Tabs, + TabPane: Tabs.TabPane, + }, +} +</script> +<style> +.card-container { + background: #F5F5F5; + overflow: hidden; + padding: 24px; +} +.card-container > .ant-tabs-card > .ant-tabs-content { + height: 120px; + margin-top: -16px; +} + +.card-container > .ant-tabs-card > .ant-tabs-content > .ant-tabs-tabpane { + background: #fff; + padding: 16px; +} + +.card-container > .ant-tabs-card > .ant-tabs-bar { + border-color: #fff; +} + +.card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab { + border-color: transparent; + background: transparent; +} + +.card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active { + border-color: #fff; + background: #fff; +} +</style> diff --git a/components/tabs/demo/card.vue b/components/tabs/demo/card.vue new file mode 100644 index 000000000..916b8acdc --- /dev/null +++ b/components/tabs/demo/card.vue @@ -0,0 +1,25 @@ +<template> + <Tabs @change="callback" type="card"> + <TabPane tab="Tab 1" tabKey="1">Content of Tab Pane 1</TabPane> + <TabPane tab="Tab 2" tabKey="2">Content of Tab Pane 2</TabPane> + <TabPane tab="Tab 3" tabKey="3">Content of Tab Pane 3</TabPane> + </Tabs> +</template> +<script> +import { Tabs } from 'antd' +export default { + data () { + return { + } + }, + methods: { + callback (key) { + console.log(key) + }, + }, + components: { + Tabs, + TabPane: Tabs.TabPane, + }, +} +</script> diff --git a/components/tabs/demo/custom-add-trigger.vue b/components/tabs/demo/custom-add-trigger.vue new file mode 100644 index 000000000..bfb7317aa --- /dev/null +++ b/components/tabs/demo/custom-add-trigger.vue @@ -0,0 +1,69 @@ +<template> +<div> + <div :style="{ marginBottom: '16px' }"> + <AntButton @click="add">ADD</AntButton> + </div> + <Tabs + hideAdd + v-model="activeKey" + type="editable-card" + @edit="onEdit" + > + <TabPane v-for="pane in panes" :tab="pane.title" :key="pane.key" :tabKey="pane.key" :closable="pane.closable"> + {{pane.content}} + </TabPane> + </Tabs> + </div> + +</template> +<script> +import { Tabs, Button } from 'antd' +export default { + data () { + const panes = [ + { title: 'Tab 1', content: 'Content of Tab 1', key: '1' }, + { title: 'Tab 2', content: 'Content of Tab 2', key: '2' }, + ] + return { + activeKey: panes[0].key, + panes, + newTabIndex: 0, + } + }, + methods: { + callback (key) { + console.log(key) + }, + onEdit (targetKey, action) { + this[action](targetKey) + }, + add () { + const panes = this.panes + const activeKey = `newTab${this.newTabIndex++}` + panes.push({ title: 'New Tab', content: 'Content of new Tab', key: activeKey }) + this.panes = panes + this.activeKey = activeKey + }, + remove (targetKey) { + let activeKey = this.activeKey + let lastIndex + this.panes.forEach((pane, i) => { + if (pane.key === targetKey) { + lastIndex = i - 1 + } + }) + const panes = this.panes.filter(pane => pane.key !== targetKey) + if (lastIndex >= 0 && activeKey === targetKey) { + activeKey = panes[lastIndex].key + } + this.panes = panes + this.activeKey = activeKey + }, + }, + components: { + Tabs, + TabPane: Tabs.TabPane, + AntButton: Button, + }, +} +</script> diff --git a/components/tabs/demo/editable-card.vue b/components/tabs/demo/editable-card.vue new file mode 100644 index 000000000..ecf56dfb9 --- /dev/null +++ b/components/tabs/demo/editable-card.vue @@ -0,0 +1,61 @@ +<template> + <Tabs + v-model="activeKey" + type="editable-card" + @edit="onEdit" + > + <TabPane v-for="pane in panes" :tab="pane.title" :key="pane.key" :tabKey="pane.key" :closable="pane.closable"> + {{pane.content}} + </TabPane> + </Tabs> +</template> +<script> +import { Tabs } from 'antd' +export default { + data () { + const panes = [ + { title: 'Tab 1', content: 'Content of Tab 1', key: '1', closable: false }, + { title: 'Tab 2', content: 'Content of Tab 2', key: '2' }, + ] + return { + activeKey: panes[0].key, + panes, + newTabIndex: 0, + } + }, + methods: { + callback (key) { + console.log(key) + }, + onEdit (targetKey, action) { + this[action](targetKey) + }, + add () { + const panes = this.panes + const activeKey = `newTab${this.newTabIndex++}` + panes.push({ title: 'New Tab', content: 'Content of new Tab', key: activeKey }) + this.panes = panes + this.activeKey = activeKey + }, + remove (targetKey) { + let activeKey = this.activeKey + let lastIndex + this.panes.forEach((pane, i) => { + if (pane.key === targetKey) { + lastIndex = i - 1 + } + }) + const panes = this.panes.filter(pane => pane.key !== targetKey) + if (lastIndex >= 0 && activeKey === targetKey) { + activeKey = panes[lastIndex].key + } + this.panes = panes + this.activeKey = activeKey + }, + }, + components: { + Tabs, + TabPane: Tabs.TabPane, + }, +} +</script> diff --git a/components/tabs/demo/extra.vue b/components/tabs/demo/extra.vue new file mode 100644 index 000000000..a0f206665 --- /dev/null +++ b/components/tabs/demo/extra.vue @@ -0,0 +1,21 @@ +<template> + <Tabs :tabBarExtraContent="operations"> + <TabPane tab="Tab 1" tabKey="1">Content of tab 1</TabPane> + <TabPane tab="Tab 2" tabKey="2">Content of tab 2</TabPane> + <TabPane tab="Tab 3" tabKey="3">Content of tab 3</TabPane> + </Tabs> +</template> +<script> +import { Tabs, Button } from 'antd' +export default { + methods: { + operations (h) { + return h('span', [<Button>Extra Action</Button>]) + }, + }, + components: { + Tabs, + TabPane: Tabs.TabPane, + }, +} +</script> diff --git a/components/tabs/demo/index.vue b/components/tabs/demo/index.vue new file mode 100644 index 000000000..192a15f49 --- /dev/null +++ b/components/tabs/demo/index.vue @@ -0,0 +1,54 @@ +<template> + <div> + <h1>Basic</h1> + <Basic /> + <h1>CardTop</h1> + <CardTop /> + <h1>Card</h1> + <Card /> + <h1>CustomAddTrigger</h1> + <CustomAddTrigger /> + <h1>Disabled</h1> + <Disabled /> + <h1>EditableCard</h1> + <EditableCard /> + <h1>Extra</h1> + <Extra /> + <h1>Icon</h1> + <Icon /> + <h1>Position</h1> + <Position /> + <h1>Size</h1> + <Size /> + <h1>Slide</h1> + <Slide/> + </div> +</template> +<script> +import Basic from './basic' +import CardTop from './card-top' +import Card from './card' +import CustomAddTrigger from './custom-add-trigger' +import Disabled from './disabled' +import EditableCard from './editable-card' +import Extra from './extra' +import Icon from './icon' +import Position from './position' +import Size from './size' +import Slide from './slide' +export default { + components: { + Basic, + CardTop, + Card, + CustomAddTrigger, + Disabled, + EditableCard, + Extra, + Icon, + Position, + Size, + Slide, + }, +} +</script> diff --git a/components/tabs/demo/position.vue b/components/tabs/demo/position.vue new file mode 100644 index 000000000..e79faf24c --- /dev/null +++ b/components/tabs/demo/position.vue @@ -0,0 +1,38 @@ +<template> + <div style="width: 500px"> + <RadioGroup v-model="tabPosition" style="margin:8px"> + <RadioButton value="top">top</RadioButton> + <RadioButton value="bottom">bottom</RadioButton> + <RadioButton value="left">left</RadioButton> + <RadioButton value="right">right</RadioButton> + </RadioGroup> + <Tabs defaultActiveKey="1" :tabPosition="tabPosition"> + <TabPane tab="Tab 1" tabKey="1">Content of Tab 1</TabPane> + <TabPane tab="Tab 2" tabKey="2">Content of Tab 2</TabPane> + <TabPane tab="Tab 3" tabKey="3">Content of Tab 3</TabPane> + </Tabs> + </div> + +</template> +<script> +import { Tabs, Radio } from 'antd' +export default { + data () { + return { + tabPosition: 'top', + } + }, + methods: { + callback (val) { + console.log(val) + }, + }, + components: { + Tabs, + TabPane: Tabs.TabPane, + Radio, + RadioGroup: Radio.Group, + RadioButton: Radio.Button, + }, +} +</script> diff --git a/components/tabs/demo/size.vue b/components/tabs/demo/size.vue new file mode 100644 index 000000000..7ccecdee5 --- /dev/null +++ b/components/tabs/demo/size.vue @@ -0,0 +1,16 @@ +<template> + <Tabs defaultActiveKey="2" size="small"> + <TabPane tab="Tab 1" tabKey="1">Content of tab 1</TabPane> + <TabPane tab="Tab 2" tabKey="2">Content of tab 2</TabPane> + <TabPane tab="Tab 3" tabKey="3">Content of tab 3</TabPane> + </Tabs> +</template> +<script> +import { Tabs } from 'antd' +export default { + components: { + Tabs, + TabPane: Tabs.TabPane, + }, +} +</script> diff --git a/components/tabs/demo/slide.vue b/components/tabs/demo/slide.vue index 514e7b1af..8b9158181 100644 --- a/components/tabs/demo/slide.vue +++ b/components/tabs/demo/slide.vue @@ -1,10 +1,10 @@ <template> <div style="width: 500px"> - <RadioGroup v-model="mode" :style="{ marginBottom: 8 }"> + <RadioGroup v-model="mode" :style="{ marginBottom: '8px' }"> <RadioButton value="top">Horizontal</RadioButton> <RadioButton value="left">Vertical</RadioButton> </RadioGroup> - <Tabs defaultActiveKey="1" :tabPosition="mode" :style="{ height: '200px'}"> + <Tabs defaultActiveKey="1" :tabPosition="mode" :style="{ height: '200px'}" @prevClick="callback" @nextClick="callback"> <TabPane tab="Tab 1" tabKey="1">Content of tab 1</TabPane> <TabPane tab="Tab 2" tabKey="2">Content of tab 2</TabPane> <TabPane tab="Tab 3" tabKey="3">Content of tab 3</TabPane> @@ -29,6 +29,9 @@ export default { } }, methods: { + callback (val) { + console.log(val) + }, }, components: { Tabs, diff --git a/components/tabs/index.vue b/components/tabs/index.vue index e4875a79c..48ca53ccd 100644 --- a/components/tabs/index.vue +++ b/components/tabs/index.vue @@ -1,10 +1,5 @@ <script> import Tabs from './Tabs' -// import TabPane from './TabPane' -import ScrollableInkTabBar from './ScrollableInkTabBar' -import TabBar from './TabBar' -import TabContent from './TabContent' -import Icon from '../icon' import isFlexSupported from '../_util/isFlexSupported' export default { props: { @@ -12,11 +7,9 @@ export default { activeKey: String, defaultActiveKey: String, hideAdd: { type: Boolean, default: false }, - onChange: { type: Function, default: () => {} }, - onTabClick: { type: Function, default: () => {} }, - onPrevClick: { type: Function, default: () => {} }, - onNextClick: { type: Function, default: () => {} }, tabBarStyle: Object, + tabBarExtraContent: [String, Number, Function], + destroyInactiveTabPane: { type: Boolean, default: false }, type: { validator (value) { return ['line', 'card', 'editable-card'].includes(value) @@ -27,20 +20,20 @@ export default { return ['top', 'right', 'bottom', 'left'].includes(value) }, }, - onEdit: { type: Function, default: () => {} }, size: { validator (value) { return ['default', 'small'].includes(value) }, }, - animated: Boolean | Object, + animated: { type: [Boolean, Object], default: undefined }, + }, + model: { + prop: 'activeKey', + event: 'change', }, methods: { createNewTab (targetKey) { - const onEdit = this.$props.onEdit - if (onEdit) { - onEdit(targetKey, 'add') - } + this.$emit('edit', targetKey, 'add') }, removeTab (targetKey, e) { @@ -48,20 +41,21 @@ export default { if (!targetKey) { return } - - const onEdit = this.$props.onEdit - if (onEdit) { - onEdit(targetKey, 'remove') - } + this.$emit('edit', targetKey, 'remove') }, handleChange (activeKey) { - // const onChange = this.$props.onChange - // if (onChange) { - // onChange(activeKey) - // } this.$emit('change', activeKey) }, + onTabClick (val) { + this.$emit('tabClick', val) + }, + onPrevClick (val) { + this.$emit('prevClick', val) + }, + onNextClick (val) { + this.$emit('nextClick', val) + }, }, mounted () { @@ -72,29 +66,32 @@ export default { } }, - render () { + render (createElement) { const { prefixCls, size, type = 'line', tabPosition, tabBarStyle, - // hideAdd, + hideAdd, onTabClick, onPrevClick, onNextClick, - animated = true, - } = this.$props - let { tabBarExtraContent } = this.$props + animated, + destroyInactiveTabPane = false, + activeKey, + defaultActiveKey, + } = this + const { tabBarExtraContent } = this.$props let { inkBarAnimated, tabPaneAnimated } = typeof animated === 'object' ? { // eslint-disable-line - inkBarAnimated: animated.inkBar, tabPaneAnimated: animated.tabPane, + inkBarAnimated: !!animated.inkBar, tabPaneAnimated: !!animated.tabPane, } : { - inkBarAnimated: animated, tabPaneAnimated: animated, + inkBarAnimated: animated === undefined || animated, tabPaneAnimated: animated === undefined || animated, } // card tabs should not have animation if (type !== 'line') { - tabPaneAnimated = 'animated' in this.$props ? tabPaneAnimated : false + tabPaneAnimated = animated === undefined ? false : tabPaneAnimated } const cls = { [`${prefixCls}-mini`]: size === 'small' || size, @@ -103,56 +100,7 @@ export default { [`${prefixCls}-${type}`]: true, [`${prefixCls}-no-animation`]: !tabPaneAnimated, } - // only card type tabs can be added and closed - let childrenWithClose - // if (type === 'editable-card') { - // childrenWithClose = [] - // React.Children.forEach(children, (child, index) => { - // let closable = child.props.closable - // closable = typeof closable === 'undefined' ? true : closable - // const closeIcon = closable ? ( - // <Icon - // type='close' - // onClick={e => this.removeTab(child.key, e)} - // /> - // ) : null - // childrenWithClose.push(cloneElement(child, { - // tab: ( - // <div className={closable ? undefined : `${prefixCls}-tab-unclosable`}> - // {child.props.tab} - // {closeIcon} - // </div> - // ), - // key: child.key || index, - // })) - // }) - // // Add new tab handler - // if (!hideAdd) { - // tabBarExtraContent = ( - // <span> - // <Icon type='plus' className={`${prefixCls}-new-tab`} onClick={this.createNewTab} /> - // {tabBarExtraContent} - // </span> - // ) - // } - // } - tabBarExtraContent = tabBarExtraContent ? ( - <div class={`${prefixCls}-extra-content`}> - {tabBarExtraContent} - </div> - ) : null - - // const renderTabBar = () => ( - // <ScrollableInkTabBar - // inkBarAnimated={inkBarAnimated} - // extraContent={tabBarExtraContent} - // onTabClick={onTabClick} - // onPrevClick={onPrevClick} - // onNextClick={onNextClick} - // style={tabBarStyle} - // /> - // ) const tabBarProps = { inkBarAnimated, extraContent: tabBarExtraContent, @@ -160,21 +108,40 @@ export default { onPrevClick, onNextClick, style: tabBarStyle, + hideAdd, + removeTab: this.removeTab, + createNewTab: this.createNewTab, } const tabContentProps = { animated: tabPaneAnimated, animatedWithMargin: true, } + const self = this + const tabsProps = { + props: { + prefixCls, + tabBarPosition: tabPosition, + onChange: this.handleChange, + tabBarProps: tabBarProps, + tabContentProps: tabContentProps, + destroyInactiveTabPane, + activeKey, + defaultActiveKey, + type, + onTabClick: this.onTabClick, + }, + on: { + change (val) { + self.handleChange(val) + }, + }, + } return ( <Tabs - {...this.$props} class={cls} - tabBarPosition={tabPosition} - onChange={this.handleChange} - tabBarProps={tabBarProps} - tabContentProps={tabContentProps} + {...tabsProps} > - {childrenWithClose || this.$slots.default} + {this.$slots.default} </Tabs> ) }, diff --git a/components/tabs/index.zh-CN b/components/tabs/index.zh-CN new file mode 100644 index 000000000..fb06b446c --- /dev/null +++ b/components/tabs/index.zh-CN @@ -0,0 +1,52 @@ +--- +category: Components +subtitle: 标签页 +type: Data Display +title: Tabs +cols: 1 +--- + +选项卡切换组件。 + +## 何时使用 + +提供平级的区域将大块内容进行收纳和展现,保持界面整洁。 + +Ant Design 依次提供了三级选项卡,分别用于不同的场景。 + +- 卡片式的页签,提供可关闭的样式,常用于容器顶部。 +- 标准线条式页签,用于容器内部的主功能切换,这是最常用的 Tabs。 +- [RadioButton](/components/radio/#components-radio-demo-radiobutton) 可作为更次级的页签来使用。 + +## API + +### Tabs + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| activeKey | 当前激活 tab 面板的 key | string | 无 | +| animated | 是否使用动画切换 Tabs,在 `tabPosition=top|bottom` 时有效 | boolean \| {inkBar:boolean, tabPane:boolean} | true, 当 type="card" 时为 false | +| defaultActiveKey | 初始化选中面板的 key,如果没有设置 activeKey | string | 第一个面板 | +| hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | boolean | false | +| size | 大小,提供 `default` 和 `small` 两种大小,仅当 `type="line"` 时生效。 | string | 'default' | +| tabBarExtraContent | tab bar 上额外的元素 | string\|number\|Function | 无 | +| tabBarStyle | tab bar 的样式对象 | object | - | +| tabPosition | 页签位置,可选值有 `top` `right` `bottom` `left` | string | 'top' | +| type | 页签的基本样式,可选 `line`、`card` `editable-card` 类型 | string | 'line' | + + +### 事件 +| 参数 | 说明 | 类型 | 默认值 | +| change | 切换面板的回调 | Function | 无 | +| edit | 新增和删除页签的回调,在 `type="editable-card"` 时有效 | (targetKey, action): void | 无 | +| nextClick | next 按钮被点击的回调 | Function | 无 | +| prevClick | prev 按钮被点击的回调 | Function | 无 | +| tabClick | tab 被点击的回调 | Function | 无 | + +### Tabs.TabPane + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false | +| tabKey | 对应 activeKey | string | 无 | +| tab | 选项卡头显示文字 | string\|Function | 无 |