mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(NavBar): add placeholder prop (#5938)
This commit is contained in:
parent
5bbe5608ce
commit
ed28f808b2
@ -39,7 +39,7 @@ export default {
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Usage
|
||||
### Use Slot
|
||||
|
||||
```html
|
||||
<van-nav-bar title="Title" left-text="Back" left-arrow>
|
||||
@ -59,8 +59,9 @@ export default {
|
||||
| left-text | Left Text | *string* | `''` |
|
||||
| right-text | Right Text | *string* | `''` |
|
||||
| left-arrow | Whether to show left arrow | *boolean* | `false` |
|
||||
| fixed | Whether to fixed top | *boolean* | `false` |
|
||||
| border | Whether to show bottom border | *boolean* | `true` |
|
||||
| fixed | Whether to fixed top | *boolean* | `false` |
|
||||
| placeholder `v2.6.0` | Whether to generage a placeholder element when fixed | *boolean* | `false` |
|
||||
| z-index | Z-index | *number \| string* | `1` |
|
||||
|
||||
### Slots
|
||||
|
@ -39,14 +39,14 @@ export default {
|
||||
}
|
||||
```
|
||||
|
||||
### 高级用法
|
||||
### 使用插槽
|
||||
|
||||
通过插槽定制内容
|
||||
通过插槽自定义导航栏两侧的内容
|
||||
|
||||
```html
|
||||
<van-nav-bar title="标题" left-text="返回" left-arrow>
|
||||
<template #right>
|
||||
<van-icon name="search"/>
|
||||
<van-icon name="search" size="18" />
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
```
|
||||
@ -61,8 +61,9 @@ export default {
|
||||
| left-text | 左侧文案 | *string* | `''` |
|
||||
| right-text | 右侧文案 | *string* | `''` |
|
||||
| left-arrow | 是否显示左侧箭头 | *boolean* | `false` |
|
||||
| fixed | 是否固定在顶部 | *boolean* | `false` |
|
||||
| border | 是否显示下边框 | *boolean* | `true` |
|
||||
| fixed | 是否固定在顶部 | *boolean* | `false` |
|
||||
| placeholder `v2.6.0` | 固定在顶部时,是否在标签位置生成一个等高的占位元素 | *boolean* | `false` |
|
||||
| z-index | 元素 z-index | *number \| string* | `1` |
|
||||
|
||||
### Slots
|
||||
|
@ -11,10 +11,10 @@
|
||||
/>
|
||||
</demo-block>
|
||||
|
||||
<demo-block :title="t('advancedUsage')">
|
||||
<demo-block :title="t('useSlot')">
|
||||
<van-nav-bar :title="t('title')" :left-text="t('back')" left-arrow>
|
||||
<template #right>
|
||||
<van-icon name="search" />
|
||||
<van-icon name="search" size="18" />
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
</demo-block>
|
||||
@ -23,6 +23,15 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
i18n: {
|
||||
'zh-CN': {
|
||||
useSlot: '使用插槽',
|
||||
},
|
||||
'en-US': {
|
||||
useSlot: 'Use Slot',
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onClickLeft() {
|
||||
this.$toast(this.t('back'));
|
||||
|
103
src/nav-bar/index.js
Normal file
103
src/nav-bar/index.js
Normal file
@ -0,0 +1,103 @@
|
||||
// Utils
|
||||
import { createNamespace } from '../utils';
|
||||
import { BORDER_BOTTOM } from '../utils/constant';
|
||||
|
||||
// Components
|
||||
import Icon from '../icon';
|
||||
|
||||
const [createComponent, bem] = createNamespace('nav-bar');
|
||||
|
||||
export default createComponent({
|
||||
props: {
|
||||
title: String,
|
||||
fixed: Boolean,
|
||||
zIndex: [Number, String],
|
||||
leftText: String,
|
||||
rightText: String,
|
||||
leftArrow: Boolean,
|
||||
placeholder: Boolean,
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
height: null,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.placeholder && this.fixed) {
|
||||
this.height = this.$refs.navBar.getBoundingClientRect().height;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
genLeft() {
|
||||
const leftSlot = this.slots('left');
|
||||
|
||||
if (leftSlot) {
|
||||
return leftSlot;
|
||||
}
|
||||
|
||||
return [
|
||||
this.leftArrow && <Icon class={bem('arrow')} name="arrow-left" />,
|
||||
this.leftText && <span class={bem('text')}>{this.leftText}</span>,
|
||||
];
|
||||
},
|
||||
|
||||
genRight() {
|
||||
const rightSlot = this.slots('right');
|
||||
|
||||
if (rightSlot) {
|
||||
return rightSlot;
|
||||
}
|
||||
|
||||
if (this.rightText) {
|
||||
return <span class={bem('text')}>{this.rightText}</span>;
|
||||
}
|
||||
},
|
||||
|
||||
genNavBar() {
|
||||
return (
|
||||
<div
|
||||
ref="navBar"
|
||||
style={{ zIndex: this.zIndex }}
|
||||
class={[bem({ fixed: this.fixed }), { [BORDER_BOTTOM]: this.border }]}
|
||||
>
|
||||
<div class={bem('left')} onClick={this.onClickLeft}>
|
||||
{this.genLeft()}
|
||||
</div>
|
||||
<div class={[bem('title'), 'van-ellipsis']}>
|
||||
{this.slots('title') || this.title}
|
||||
</div>
|
||||
<div class={bem('right')} onClick={this.onClickRight}>
|
||||
{this.genRight()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
onClickLeft(event) {
|
||||
this.$emit('click-left', event);
|
||||
},
|
||||
|
||||
onClickRight(event) {
|
||||
this.$emit('click-right', event);
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.placeholder && this.fixed) {
|
||||
return (
|
||||
<div class={bem('placeholder')} style={{ height: `${this.height}px` }}>
|
||||
{this.genNavBar()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.genNavBar();
|
||||
},
|
||||
});
|
@ -1,95 +0,0 @@
|
||||
// Utils
|
||||
import { createNamespace, noop } from '../utils';
|
||||
import { inherit } from '../utils/functional';
|
||||
import { BORDER_BOTTOM } from '../utils/constant';
|
||||
|
||||
// Components
|
||||
import Icon from '../icon';
|
||||
|
||||
// Types
|
||||
import { CreateElement, RenderContext } from 'vue/types';
|
||||
import { ScopedSlot, DefaultSlots } from '../utils/types';
|
||||
|
||||
export type NavBarProps = {
|
||||
title?: string;
|
||||
fixed?: boolean;
|
||||
zIndex: number;
|
||||
border: boolean;
|
||||
leftText?: string;
|
||||
rightText?: string;
|
||||
leftArrow?: boolean;
|
||||
};
|
||||
|
||||
export type NavBarSlots = DefaultSlots & {
|
||||
left?: ScopedSlot;
|
||||
title?: ScopedSlot;
|
||||
right?: ScopedSlot;
|
||||
};
|
||||
|
||||
export type NavBarEvents = {
|
||||
'click-left'?(event: Event): void;
|
||||
'click-right'?(event: Event): void;
|
||||
};
|
||||
|
||||
const [createComponent, bem] = createNamespace('nav-bar');
|
||||
|
||||
function NavBar(
|
||||
h: CreateElement,
|
||||
props: NavBarProps,
|
||||
slots: NavBarSlots,
|
||||
ctx: RenderContext<NavBarProps>
|
||||
) {
|
||||
function LeftPart() {
|
||||
if (slots.left) {
|
||||
return slots.left();
|
||||
}
|
||||
|
||||
return [
|
||||
props.leftArrow && <Icon class={bem('arrow')} name="arrow-left" />,
|
||||
props.leftText && <span class={bem('text')}>{props.leftText}</span>,
|
||||
];
|
||||
}
|
||||
|
||||
function RightPart() {
|
||||
if (slots.right) {
|
||||
return slots.right();
|
||||
}
|
||||
|
||||
if (props.rightText) {
|
||||
return <span class={bem('text')}>{props.rightText}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ zIndex: props.zIndex }}
|
||||
class={[bem({ fixed: props.fixed }), { [BORDER_BOTTOM]: props.border }]}
|
||||
{...inherit(ctx)}
|
||||
>
|
||||
<div class={bem('left')} onClick={ctx.listeners['click-left'] || noop}>
|
||||
{LeftPart()}
|
||||
</div>
|
||||
<div class={[bem('title'), 'van-ellipsis']}>
|
||||
{slots.title ? slots.title() : props.title}
|
||||
</div>
|
||||
<div class={bem('right')} onClick={ctx.listeners['click-right'] || noop}>
|
||||
{RightPart()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
NavBar.props = {
|
||||
title: String,
|
||||
fixed: Boolean,
|
||||
zIndex: [Number, String],
|
||||
leftText: String,
|
||||
rightText: String,
|
||||
leftArrow: Boolean,
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default createComponent<NavBarProps, NavBarEvents>(NavBar);
|
@ -15,7 +15,7 @@ exports[`renders demo correctly 1`] = `
|
||||
<div class="van-nav-bar__left"><i class="van-icon van-icon-arrow-left van-nav-bar__arrow">
|
||||
<!----></i><span class="van-nav-bar__text">返回</span></div>
|
||||
<div class="van-nav-bar__title van-ellipsis">标题</div>
|
||||
<div class="van-nav-bar__right"><i class="van-icon van-icon-search">
|
||||
<div class="van-nav-bar__right"><i class="van-icon van-icon-search" style="font-size: 18px;">
|
||||
<!----></i></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,15 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`placeholder prop 1`] = `
|
||||
<div class="van-nav-bar__placeholder" style="height: 0px;">
|
||||
<div class="van-nav-bar van-nav-bar--fixed van-hairline--bottom">
|
||||
<div class="van-nav-bar__left"></div>
|
||||
<div class="van-nav-bar__title van-ellipsis"></div>
|
||||
<div class="van-nav-bar__right"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`render left & right slot 1`] = `
|
||||
<div class="van-nav-bar van-hairline--bottom">
|
||||
<div class="van-nav-bar__left">Custom Left</div>
|
||||
|
@ -21,3 +21,36 @@ test('render title slot', () => {
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('placeholder prop', () => {
|
||||
const wrapper = mount(NavBar, {
|
||||
propsData: {
|
||||
fixed: true,
|
||||
placeholder: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('click-left event', () => {
|
||||
const wrapper = mount(NavBar, {
|
||||
propsData: {
|
||||
leftText: 'left',
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.find('.van-nav-bar__left').trigger('click');
|
||||
expect(wrapper.emitted('click-left')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('click-right event', () => {
|
||||
const wrapper = mount(NavBar, {
|
||||
propsData: {
|
||||
rightText: 'right',
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.find('.van-nav-bar__right').trigger('click');
|
||||
expect(wrapper.emitted('click-right')).toBeTruthy();
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user