mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
159 lines
3.5 KiB
TypeScript
159 lines
3.5 KiB
TypeScript
import { createNamespace } from '../utils';
|
|
import { emit, inherit } from '../utils/functional';
|
|
import { PopupMixin } from '../mixins/popup';
|
|
import Icon from '../icon';
|
|
import Popup from '../popup';
|
|
import Loading from '../loading';
|
|
|
|
// Types
|
|
import { CreateElement, RenderContext } from 'vue/types';
|
|
import { DefaultSlots } from '../utils/types';
|
|
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[];
|
|
duration: number;
|
|
cancelText?: string;
|
|
closeOnClickAction?: boolean;
|
|
safeAreaInsetBottom?: boolean;
|
|
};
|
|
|
|
const [createComponent, bem] = createNamespace('action-sheet');
|
|
|
|
function ActionSheet(
|
|
h: CreateElement,
|
|
props: ActionSheetProps,
|
|
slots: DefaultSlots,
|
|
ctx: RenderContext<ActionSheetProps>
|
|
) {
|
|
const { title, cancelText } = props;
|
|
|
|
function onCancel() {
|
|
emit(ctx, 'input', false);
|
|
emit(ctx, 'cancel');
|
|
}
|
|
|
|
function Header() {
|
|
if (title) {
|
|
return (
|
|
<div class={[bem('header'), 'van-hairline--bottom']}>
|
|
{title}
|
|
<Icon name="close" class={bem('close')} onClick={onCancel} />
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
function Content() {
|
|
if (slots.default) {
|
|
return <div class={bem('content')}>{slots.default()}</div>;
|
|
}
|
|
}
|
|
|
|
function Option(item: ActionSheetItem, index: number) {
|
|
const disabled = item.disabled || item.loading;
|
|
|
|
function onClickOption(event: MouseEvent) {
|
|
event.stopPropagation();
|
|
|
|
if (item.disabled || item.loading) {
|
|
return;
|
|
}
|
|
|
|
if (item.callback) {
|
|
item.callback(item);
|
|
}
|
|
|
|
emit(ctx, 'select', item, index);
|
|
|
|
if (props.closeOnClickAction) {
|
|
emit(ctx, 'input', false);
|
|
}
|
|
}
|
|
|
|
function OptionContent() {
|
|
if (item.loading) {
|
|
return <Loading size="20px" />;
|
|
}
|
|
|
|
return [
|
|
<span class={bem('name')}>{item.name}</span>,
|
|
item.subname && <span class={bem('subname')}>{item.subname}</span>
|
|
];
|
|
}
|
|
|
|
return (
|
|
<div
|
|
class={[bem('item', { disabled }), item.className, 'van-hairline--top']}
|
|
onClick={onClickOption}
|
|
>
|
|
{OptionContent()}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function CancelText() {
|
|
if (cancelText) {
|
|
return (
|
|
<div class={bem('cancel')} onClick={onCancel}>
|
|
{cancelText}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Popup
|
|
class={bem({ 'safe-area-inset-bottom': props.safeAreaInsetBottom })}
|
|
position="bottom"
|
|
value={props.value}
|
|
overlay={props.overlay}
|
|
duration={props.duration}
|
|
lazyRender={props.lazyRender}
|
|
lockScroll={props.lockScroll}
|
|
getContainer={props.getContainer}
|
|
closeOnClickOverlay={props.closeOnClickOverlay}
|
|
{...inherit(ctx, true)}
|
|
>
|
|
{Header()}
|
|
{props.actions.map(Option)}
|
|
{Content()}
|
|
{CancelText()}
|
|
</Popup>
|
|
);
|
|
}
|
|
|
|
ActionSheet.props = {
|
|
...PopupMixin.props,
|
|
title: String,
|
|
actions: Array,
|
|
cancelText: String,
|
|
getContainer: [String, Function],
|
|
closeOnClickAction: Boolean,
|
|
safeAreaInsetBottom: Boolean,
|
|
duration: {
|
|
type: Number,
|
|
default: null
|
|
},
|
|
overlay: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
closeOnClickOverlay: {
|
|
type: Boolean,
|
|
default: true
|
|
}
|
|
};
|
|
|
|
export default createComponent<ActionSheetProps>(ActionSheet);
|