index.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { use } from '../utils';
  2. import { emit, inherit } from '../utils/functional';
  3. import { PopupMixin } from '../mixins/popup';
  4. import Icon from '../icon';
  5. import Loading from '../loading';
  6. import Popup from '../popup';
  7. // Types
  8. import { CreateElement, RenderContext } from 'vue/types';
  9. import { DefaultSlots } from '../utils/use/sfc';
  10. import { PopupMixinProps } from '../mixins/popup/type';
  11. export type ActionsheetItem = {
  12. name: string;
  13. subname?: string;
  14. loading?: boolean;
  15. disabled?: boolean;
  16. className?: string;
  17. callback?: (item: ActionsheetItem) => void;
  18. };
  19. export type ActionsheetProps = PopupMixinProps & {
  20. title?: string;
  21. actions: ActionsheetItem[];
  22. cancelText?: string;
  23. safeAreaInsetBottom?: boolean;
  24. };
  25. const [sfc, bem] = use('actionsheet');
  26. function Actionsheet(
  27. h: CreateElement,
  28. props: ActionsheetProps,
  29. slots: DefaultSlots,
  30. ctx: RenderContext<ActionsheetProps>
  31. ) {
  32. const { title, cancelText } = props;
  33. const onCancel = () => {
  34. emit(ctx, 'input', false);
  35. emit(ctx, 'cancel');
  36. };
  37. const Header = () => (
  38. <div class={[bem('header'), 'van-hairline--top-bottom']}>
  39. {title}
  40. <Icon name="close" class={bem('close')} onClick={onCancel} />
  41. </div>
  42. );
  43. const Option = (item: ActionsheetItem, index: number) => (
  44. <div
  45. class={[
  46. bem('item', { disabled: item.disabled || item.loading }),
  47. item.className,
  48. 'van-hairline--top'
  49. ]}
  50. onClick={(event: Event) => {
  51. event.stopPropagation();
  52. if (!item.disabled && !item.loading) {
  53. if (item.callback) {
  54. item.callback(item);
  55. }
  56. emit(ctx, 'select', item, index);
  57. }
  58. }}
  59. >
  60. {item.loading ? (
  61. <Loading class={bem('loading')} size="20px" />
  62. ) : (
  63. [
  64. <span class={bem('name')}>{item.name}</span>,
  65. item.subname && <span class={bem('subname')}>{item.subname}</span>
  66. ]
  67. )}
  68. </div>
  69. );
  70. return (
  71. <Popup
  72. class={bem({ 'safe-area-inset-bottom': props.safeAreaInsetBottom })}
  73. value={props.value}
  74. position="bottom"
  75. overlay={props.overlay}
  76. lazyRender={props.lazyRender}
  77. getContainer={props.getContainer}
  78. closeOnClickOverlay={props.closeOnClickOverlay}
  79. onInput={(value: boolean) => {
  80. emit(ctx, 'input', value);
  81. }}
  82. {...inherit(ctx)}
  83. >
  84. {title ? Header() : props.actions.map(Option)}
  85. {slots.default && <div class={bem('content')}>{slots.default()}</div>}
  86. {cancelText && (
  87. <div class={bem('cancel')} onClick={onCancel}>
  88. {cancelText}
  89. </div>
  90. )}
  91. </Popup>
  92. );
  93. }
  94. Actionsheet.props = {
  95. ...PopupMixin.props,
  96. title: String,
  97. actions: Array,
  98. cancelText: String,
  99. safeAreaInsetBottom: Boolean,
  100. overlay: {
  101. type: Boolean,
  102. default: true
  103. },
  104. closeOnClickOverlay: {
  105. type: Boolean,
  106. default: true
  107. }
  108. };
  109. export default sfc<ActionsheetProps>(Actionsheet);