import React from 'react'; import cls from 'classnames'; import PropTypes from 'prop-types'; import { noop, get, isFunction, omit } from 'lodash'; import { cssClasses, numbers } from '@douyinfe/semi-foundation/popconfirm/constants'; import PopconfirmFoundation, { PopconfirmAdapter } from '@douyinfe/semi-foundation/popconfirm/popconfirmFoundation'; import { IconClose, IconAlertTriangle } from '@douyinfe/semi-icons'; import BaseComponent from '../_base/baseComponent'; import Popover, { PopoverProps } from '../popover'; import { Position, Trigger, RenderContentProps } from '../tooltip'; import Button, { ButtonProps } from '../button'; import { Type as ButtonType } from '../button/Button'; import ConfigContext, { ContextValue } from '../configProvider/context'; import LocaleConsumer from '../locale/localeConsumer'; import { Locale as LocaleObject } from '../locale/interface'; import '@douyinfe/semi-foundation/popconfirm/popconfirm.scss'; import { getDefaultPropsFromGlobalConfig } from "../_utils"; export interface PopconfirmProps extends PopoverProps { cancelText?: string; cancelButtonProps?: ButtonProps; cancelType?: ButtonType; defaultVisible?: boolean; disabled?: boolean; icon?: React.ReactNode; okText?: string; okType?: ButtonType; okButtonProps?: ButtonProps; motion?: boolean; title?: React.ReactNode; visible?: boolean; prefixCls?: string; zIndex?: number; trigger?: Trigger; showCloseIcon?: boolean; position?: Position; onCancel?: (e: React.MouseEvent) => Promise | void; onConfirm?: (e: React.MouseEvent) => Promise | void; onVisibleChange?: (visible: boolean) => void; onClickOutSide?: (e: React.MouseEvent) => void } export interface PopconfirmState { visible: boolean; cancelLoading: boolean; confirmLoading: boolean } interface PopProps { [x: string]: any } export default class Popconfirm extends BaseComponent { static contextType = ConfigContext; static propTypes = { motion: PropTypes.oneOfType([PropTypes.bool, PropTypes.func, PropTypes.object]), disabled: PropTypes.bool, content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), title: PropTypes.any, prefixCls: PropTypes.string, className: PropTypes.string, style: PropTypes.object, icon: PropTypes.node, okText: PropTypes.string, okType: PropTypes.string, cancelText: PropTypes.string, cancelType: PropTypes.string, onCancel: PropTypes.func, onConfirm: PropTypes.func, onClickOutSide: PropTypes.func, onVisibleChange: PropTypes.func, visible: PropTypes.bool, defaultVisible: PropTypes.bool, okButtonProps: PropTypes.object, cancelButtonProps: PropTypes.object, stopPropagation: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), showCloseIcon: PropTypes.bool, zIndex: PropTypes.number, // private trigger: PropTypes.string, position: PropTypes.string, }; static __SemiComponentName__ = "Popconfirm"; static defaultProps = getDefaultPropsFromGlobalConfig(Popconfirm.__SemiComponentName__, { stopPropagation: true, trigger: 'click', // position: 'bottomLeft', onVisibleChange: noop, disabled: false, icon: , okType: 'primary', cancelType: 'tertiary', prefixCls: cssClasses.PREFIX, zIndex: numbers.DEFAULT_Z_INDEX, showCloseIcon: true, onCancel: noop, onConfirm: noop, onClickOutSide: noop, }); footerRef: React.RefObject; popoverRef: React.RefObject; foundation: PopconfirmFoundation; constructor(props: PopconfirmProps) { super(props); this.state = { cancelLoading: false, confirmLoading: false, visible: props.defaultVisible || false, }; this.foundation = new PopconfirmFoundation(this.adapter); this.footerRef = React.createRef(); this.popoverRef = React.createRef(); } context: ContextValue; static getDerivedStateFromProps(props: PopconfirmProps, state: PopconfirmState) { const willUpdateStates: Partial = {}; const { hasOwnProperty } = Object.prototype; if (hasOwnProperty.call(props, 'visible')) { willUpdateStates.visible = props.visible; } return willUpdateStates; } get adapter(): PopconfirmAdapter { return { ...super.adapter, setVisible: (visible: boolean): void => this.setState({ visible }), updateConfirmLoading: (loading: boolean): void => this.setState({ confirmLoading: loading }), updateCancelLoading: (loading: boolean): void => this.setState({ cancelLoading: loading }), notifyConfirm: (e: React.MouseEvent): Promise | void => this.props.onConfirm(e), notifyCancel: (e: React.MouseEvent): Promise | void => this.props.onCancel(e), notifyVisibleChange: (visible: boolean): void => this.props.onVisibleChange(visible), notifyClickOutSide: (e: React.MouseEvent) => this.props.onClickOutSide(e), focusCancelButton: () => { const buttonNode = this.footerRef?.current?.querySelector('[data-type=cancel]') as HTMLElement; buttonNode?.focus({ preventScroll: true }); }, focusOkButton: () => { const buttonNode = this.footerRef?.current?.querySelector('[data-type=ok]') as HTMLElement; buttonNode?.focus({ preventScroll: true }); }, focusPrevFocusElement: () => { this.popoverRef.current?.focusTrigger(); } }; } handleCancel = (e: React.MouseEvent): void => this.foundation.handleCancel(e && e.nativeEvent); handleConfirm = (e: React.MouseEvent): void => this.foundation.handleConfirm(e && e.nativeEvent); handleVisibleChange = (visible: boolean): void => this.foundation.handleVisibleChange(visible); handleClickOutSide = (e: React.MouseEvent) => this.foundation.handleClickOutSide(e); stopImmediatePropagation = (e: React.SyntheticEvent): void => e && e.nativeEvent && e.nativeEvent.stopImmediatePropagation(); renderControls() { const { okText, cancelText, okType, cancelType, cancelButtonProps, okButtonProps } = this.props; const { cancelLoading, confirmLoading } = this.state; return ( {(locale: LocaleObject['Popconfirm'], localeCode: string) => ( <> )} ); } renderConfirmPopCard = ({ initialFocusRef }: { initialFocusRef?: RenderContentProps['initialFocusRef'] }) => { const { content, title, className, style, cancelType, icon, prefixCls, showCloseIcon } = this.props; const { direction } = this.context; const popCardCls = cls( prefixCls, className, { [`${prefixCls}-rtl`]: direction === 'rtl', } ); const showTitle = title !== null && typeof title !== 'undefined'; const showContent = !(content === null || typeof content === 'undefined'); const hasIcon = React.isValidElement(icon); const bodyCls = cls({ [`${prefixCls}-body`]: true, [`${prefixCls}-body-withIcon`]: hasIcon }); return ( /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */
{ hasIcon ? {icon} : null}
{showTitle ? (
{title}
) : null}
{ showCloseIcon ? (
{showContent ? (
{isFunction(content) ? content({ initialFocusRef }) : content}
) : null}
{this.renderControls()}
); } render() { // rtl changes the default position const { direction } = this.context; const defaultPosition = direction === 'rtl' ? 'bottomRight' : 'bottomLeft'; const { className, prefixCls, disabled, children, style, position = defaultPosition, ...attrs } = this.props; if (disabled) { return children; } const { visible } = this.state; const popProps: PopProps = { onVisibleChange: this.handleVisibleChange, className: cssClasses.POPOVER, onClickOutSide: this.handleClickOutSide, }; if (this.isControlled('visible')) { popProps.trigger = 'custom'; } return ( this.renderConfirmPopCard({ initialFocusRef })} visible={visible} position={position} {...popProps} > {children} ); } }