import * as React from 'react'; import { Component } from 'react'; import cx from 'classnames'; import { Button } from 'zent'; import noop from 'zent/es/utils/noop'; import isPromise from 'zent/es/utils/isPromise'; import Popover, { PositionFunction } from '../popover'; import { exposePopover } from '../popover/withPopover'; import { I18nReceiver as Receiver, II18nLocalePop } from '../i18n'; import getPosition from '../utils/getArrowPosition'; import NoneTrigger from './NoneTrigger'; const { Trigger, withPopover } = Popover; const stateMap = { onConfirm: 'confirmPending', onCancel: 'cancelPending', }; export type PopPositions = | 'left-top' | 'left-center' | 'left-bottom' | 'right-top' | 'right-center' | 'right-bottom' | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'auto-bottom-center' | 'auto-bottom-left' | 'auto-bottom-right' | 'auto-top-center' | 'auto-top-left' | 'auto-top-right'; export type PopActionCallback = | ((close: () => void) => void) | (() => Promise); export type PopHookCallback = ( callback?: () => void, escape?: () => void ) => void; export interface IPopProps { content: React.ReactNode; trigger?: 'none' | 'click' | 'hover' | 'focus'; position?: PopPositions | PositionFunction; cushion?: number; centerArrow?: boolean; header?: React.ReactNode; block?: boolean; onShow?: () => void; onClose?: () => void; onBeforeShow?: PopHookCallback; onBeforeClose?: PopHookCallback; onConfirm?: PopActionCallback; onCancel?: PopActionCallback; confirmText?: string; cancelText?: string; type?: 'primary' | 'default' | 'danger' | 'success'; visible?: boolean; onVisibleChange?: (visible: boolean) => void; onPositionUpdated?: () => void; onPositionReady?: () => void; className?: string; wrapperClassName?: string; containerSelector?: string; prefix?: string; isOutside?: ( target: HTMLElement, node: { contentNode: HTMLElement; triggerNode: HTMLElement } ) => boolean; // trigger: click closeOnClickOutside?: boolean; // trigger: hover quirk?: boolean; mouseEnterDelay?: number; mouseLeaveDelay?: number; } export interface IPopActionProps { prefix: string; type: 'primary' | 'default' | 'danger' | 'success' | 'secondary'; onConfirm: PopActionCallback; onCancel: PopActionCallback; confirmText: string; cancelText: string; confirmPending: boolean; cancelPending: boolean; changePending: (key: string, state: boolean, callback?: () => void) => void; popover: any; } class PopAction extends Component { // 支持异步的回调函数 // onConfirm/onCancel异步等待的时候要禁用用户关闭 handleClick(callbackName) { const callback = this.props[callbackName]; const { popover } = this.props; if (typeof callback !== 'function') { return popover.close(); } const { changePending } = this.props; const stateKey = stateMap[callbackName]; const startClose = () => { changePending(stateKey, true); }; const finishClose = () => { changePending(stateKey, false, popover.close); }; if (callback.length >= 1) { startClose(); return callback(finishClose); } const maybePromise = callback(); if (isPromise(maybePromise)) { startClose(); maybePromise .then(finishClose) .catch(() => changePending(stateKey, false)); } else { popover.close(); } } handleConfirm = () => { this.handleClick('onConfirm'); }; handleCancel = () => { this.handleClick('onCancel'); }; render() { const { prefix, type, onConfirm, onCancel, confirmText, cancelText, confirmPending, cancelPending, } = this.props; if (!onConfirm && !onCancel) { return null; } return (
{(i18n: II18nLocalePop) => ( )} {(i18n: II18nLocalePop) => ( )}
); } } const BoundPopAction = withPopover(PopAction); export class Pop extends Component { static defaultProps = { trigger: 'none', position: 'top-center', cushion: 10, centerArrow: false, block: false, confirmText: '', cancelText: '', type: 'primary', closeOnClickOutside: true, mouseLeaveDelay: 200, mouseEnterDelay: 200, onPositionUpdated: noop, onPositionReady: noop, className: '', wrapperClassName: '', containerSelector: 'body', prefix: 'zent', quirk: true, }; static withPop = exposePopover('pop'); popover: Popover; isUnmounted = false; state = { confirmPending: false, cancelPending: false, }; changePending = (key, pending, callback) => { if (this.isUnmounted) { return; } this.setState( { [key]: pending, }, callback ); }; renderContent() { const { prefix, content, header, onConfirm, onCancel, confirmText, cancelText, type, } = this.props; const { confirmPending, cancelPending } = this.state; const hasHeader = !!header; return ( {hasHeader &&
{header}
}
{content}
); } renderTrigger() { const { trigger, closeOnClickOutside, isOutside, mouseLeaveDelay, mouseEnterDelay, children, quirk, } = this.props; if (trigger === 'click') { return ( {children} ); } if (trigger === 'hover') { return ( {children} ); } if (trigger === 'focus') { return {children}; } if (trigger === 'none') { return {children}; } return null; } componentWillUnmount() { this.isUnmounted = true; } render() { const { className, wrapperClassName, trigger, visible, prefix, block, onShow, onClose, position, cushion, header, centerArrow, onBeforeClose, onBeforeShow, onPositionUpdated, onPositionReady, containerSelector, } = this.props; const hasHeader = !!header; const cls = cx(`${prefix}-pop`, className, { [`${prefix}-pop--has-header`]: hasHeader, [`${prefix}-pop--no-header`]: !hasHeader, }); let { onVisibleChange } = this.props; if (trigger === 'none') { onVisibleChange = onVisibleChange || noop; } const { confirmPending, cancelPending } = this.state; const closePending = confirmPending || cancelPending; return ( {this.renderTrigger()} {this.renderContent()} ); } onPopoverRefChange = popoverInstance => { this.popover = popoverInstance; }; adjustPosition() { if (this.popover) { this.popover.adjustPosition(); } } getWrappedPopover() { return this.popover; } } export default Pop;