mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
[improvement] Actionsheet: tsx (#2807)
This commit is contained in:
parent
9236132dbf
commit
52e6fd2f0b
@ -1,98 +0,0 @@
|
|||||||
import { use } from '../utils';
|
|
||||||
import { PopupMixin } from '../mixins/popup';
|
|
||||||
import Icon from '../icon';
|
|
||||||
import Loading from '../loading';
|
|
||||||
import Popup from '../popup';
|
|
||||||
|
|
||||||
const [sfc, bem] = use('actionsheet');
|
|
||||||
|
|
||||||
export default sfc({
|
|
||||||
props: {
|
|
||||||
...PopupMixin.props,
|
|
||||||
title: String,
|
|
||||||
actions: Array,
|
|
||||||
cancelText: String,
|
|
||||||
overlay: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
closeOnClickOverlay: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onSelect(event, item) {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
if (!item.disabled && !item.loading) {
|
|
||||||
if (item.callback) {
|
|
||||||
item.callback(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('select', item);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onCancel() {
|
|
||||||
this.$emit('input', false);
|
|
||||||
this.$emit('cancel');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render(h) {
|
|
||||||
const { title, cancelText, onCancel } = this;
|
|
||||||
|
|
||||||
const Header = () => (
|
|
||||||
<div class={[bem('header'), 'van-hairline--top-bottom']}>
|
|
||||||
{title}
|
|
||||||
<Icon name="close" class={bem('close')} onClick={onCancel} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Option = item => (
|
|
||||||
<div
|
|
||||||
class={[
|
|
||||||
bem('item', { disabled: item.disabled || item.loading }),
|
|
||||||
item.className,
|
|
||||||
'van-hairline--top'
|
|
||||||
]}
|
|
||||||
onClick={event => {
|
|
||||||
this.onSelect(event, item);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.loading ? (
|
|
||||||
<Loading class={bem('loading')} size="20px" />
|
|
||||||
) : (
|
|
||||||
[
|
|
||||||
<span class={bem('name')}>{item.name}</span>,
|
|
||||||
item.subname && <span class={bem('subname')}>{item.subname}</span>
|
|
||||||
]
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Footer = cancelText ? (
|
|
||||||
<div class={bem('cancel')} onClick={onCancel}>
|
|
||||||
{cancelText}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div class={bem('content')}>{this.slots()}</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popup
|
|
||||||
class={bem()}
|
|
||||||
value={this.value}
|
|
||||||
position="bottom"
|
|
||||||
onInput={value => {
|
|
||||||
this.$emit('input', value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{title ? Header() : this.actions.map(Option)}
|
|
||||||
{Footer}
|
|
||||||
</Popup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
119
packages/actionsheet/index.tsx
Normal file
119
packages/actionsheet/index.tsx
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import { use } from '../utils';
|
||||||
|
import { emit, inherit } from '../utils/functional';
|
||||||
|
import { PopupMixin } from '../mixins/popup';
|
||||||
|
import Icon from '../icon';
|
||||||
|
import Loading from '../loading';
|
||||||
|
import Popup from '../popup';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { CreateElement, RenderContext } from 'vue/types';
|
||||||
|
import { DefaultSlots } from '../utils/use/sfc';
|
||||||
|
import { PopupMixinProps } from '../mixins/popup/type';
|
||||||
|
|
||||||
|
export type ActionsheetItem = {
|
||||||
|
name: string;
|
||||||
|
subname?: string;
|
||||||
|
loading?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
|
callback?: (item: ActionsheetItem) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ActionsheetProps = PopupMixinProps & {
|
||||||
|
title?: string;
|
||||||
|
actions: ActionsheetItem[];
|
||||||
|
cancelText?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [sfc, bem] = use('actionsheet');
|
||||||
|
|
||||||
|
function Actionsheet(
|
||||||
|
h: CreateElement,
|
||||||
|
props: ActionsheetProps,
|
||||||
|
slots: DefaultSlots,
|
||||||
|
ctx: RenderContext<ActionsheetProps>
|
||||||
|
) {
|
||||||
|
const { title, cancelText } = props;
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
emit(ctx, 'input', false);
|
||||||
|
emit(ctx, 'cancel');
|
||||||
|
};
|
||||||
|
|
||||||
|
const Header = () => (
|
||||||
|
<div class={[bem('header'), 'van-hairline--top-bottom']}>
|
||||||
|
{title}
|
||||||
|
<Icon name="close" class={bem('close')} onClick={onCancel} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Option = (item: ActionsheetItem) => (
|
||||||
|
<div
|
||||||
|
class={[
|
||||||
|
bem('item', { disabled: item.disabled || item.loading }),
|
||||||
|
item.className,
|
||||||
|
'van-hairline--top'
|
||||||
|
]}
|
||||||
|
onClick={(event: Event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (!item.disabled && !item.loading) {
|
||||||
|
if (item.callback) {
|
||||||
|
item.callback(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(ctx, 'select', item);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.loading ? (
|
||||||
|
<Loading class={bem('loading')} size="20px" />
|
||||||
|
) : (
|
||||||
|
[
|
||||||
|
<span class={bem('name')}>{item.name}</span>,
|
||||||
|
item.subname && <span class={bem('subname')}>{item.subname}</span>
|
||||||
|
]
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Footer = cancelText ? (
|
||||||
|
<div class={bem('cancel')} onClick={onCancel}>
|
||||||
|
{cancelText}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div class={bem('content')}>{slots.default && slots.default()}</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popup
|
||||||
|
class={bem()}
|
||||||
|
value={props.value}
|
||||||
|
position="bottom"
|
||||||
|
onInput={(value: boolean) => {
|
||||||
|
emit(ctx, 'input', value);
|
||||||
|
}}
|
||||||
|
{...inherit(ctx)}
|
||||||
|
>
|
||||||
|
{title ? Header() : props.actions.map(Option)}
|
||||||
|
{Footer}
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Actionsheet.props = {
|
||||||
|
...PopupMixin.props,
|
||||||
|
title: String,
|
||||||
|
actions: Array,
|
||||||
|
cancelText: String,
|
||||||
|
overlay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
closeOnClickOverlay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default sfc<ActionsheetProps>(Actionsheet);
|
@ -3,6 +3,10 @@ import Actionsheet from '..';
|
|||||||
|
|
||||||
test('callback events', () => {
|
test('callback events', () => {
|
||||||
const callback = jest.fn();
|
const callback = jest.fn();
|
||||||
|
const onInput = jest.fn();
|
||||||
|
const onCancel = jest.fn();
|
||||||
|
const onSelect = jest.fn();
|
||||||
|
|
||||||
const wrapper = mount(Actionsheet, {
|
const wrapper = mount(Actionsheet, {
|
||||||
propsData: {
|
propsData: {
|
||||||
value: true,
|
value: true,
|
||||||
@ -11,6 +15,13 @@ test('callback events', () => {
|
|||||||
{ name: 'Option', disabled: true }
|
{ name: 'Option', disabled: true }
|
||||||
],
|
],
|
||||||
cancelText: 'Cancel'
|
cancelText: 'Cancel'
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
on: {
|
||||||
|
input: onInput,
|
||||||
|
cancel: onCancel,
|
||||||
|
select: onSelect
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -20,9 +31,9 @@ test('callback events', () => {
|
|||||||
wrapper.find('.van-actionsheet__cancel').trigger('click');
|
wrapper.find('.van-actionsheet__cancel').trigger('click');
|
||||||
|
|
||||||
expect(callback.mock.calls.length).toBe(1);
|
expect(callback.mock.calls.length).toBe(1);
|
||||||
expect(wrapper.emitted('cancel')).toBeTruthy();
|
expect(onCancel.mock.calls.length).toBeTruthy();
|
||||||
expect(wrapper.emitted('input')[0][0]).toBeFalsy();
|
expect(onInput.mock.calls[0][0]).toBeFalsy();
|
||||||
expect(wrapper.emitted('select')[0][0]).toBeTruthy();
|
expect(onSelect.mock.calls[0][0]).toBeTruthy();
|
||||||
expect(wrapper.emitted('select')[0][1]).toBeFalsy();
|
expect(onSelect.mock.calls[0][1]).toBeFalsy();
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
13
packages/mixins/popup/type.ts
Normal file
13
packages/mixins/popup/type.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export type GetContainer = (container: HTMLElement) => void;
|
||||||
|
|
||||||
|
export type PopupMixinProps = {
|
||||||
|
value: boolean;
|
||||||
|
zIndex: string | number;
|
||||||
|
overlay?: boolean;
|
||||||
|
lockScroll: boolean;
|
||||||
|
lazyRender: boolean;
|
||||||
|
overlayClass?: any;
|
||||||
|
overlayStyle?: object | object[];
|
||||||
|
getContainer?: string | GetContainer;
|
||||||
|
closeOnClickOverlay?: boolean;
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user