弹出窗口 (Popup)

弹出窗口在技术上并非一个组件。它更像是 ReactXP.App 命名空间中的一组方法,允许应用程序显示一个覆盖屏幕一部分的视图。弹出窗口可以“锚定”到已挂载的组件,并随组件在屏幕上移动(例如,响应滚动事件)。

当显示弹出窗口时,调用者会指定一个 PopupOptions 结构,其中包含多个回调,包括 renderPopup 方法。

弹出窗口默认不会像开关一样操作。当调用 Popup.show 时,它将始终显示弹出窗口。如果需要弹出窗口像开关一样操作,则应将 PopupOptions.dismissIfShown 设置为 true。在这种情况下,如果组件为弹出窗口调用 Popup.show 一次,它将显示弹出窗口。同一组件后续的调用将关闭弹出窗口,依此类推。

弹出窗口的整体尺寸在其生命周期内被假定保持不变。这允许只测量一次尺寸,然后弹出窗口可以相对于锚点进行定位。

弹出窗口由调用者指定的一个唯一 ID 标识。

类型

// 'context' mode makes it attempt to behave like a context menu -- defaulting
// to the lower right of the anchor element and working its way around.  It is not supported
// with inner positioning and will throw an exception if used there.
type PopupPosition  = 'top' | 'right' | 'bottom' | 'left' | 'context';

interface PopupOptions {
    // Returns a mounted component instance that serves as the
    // "anchor" for the popup. Often a button.
    getAnchor: () => React.Component<any, any>;

    // Renders the contents of the popup. This is called twice. The
    // first time it is called, the parameters are all defaults
    // (default position and 0 offset and dimensions). This allows
    // the popup to be measured and positioned. It is called a second
    // time with the position, offset and dimensions specified. This
    // allows the method to modify the appearance based on these
    // parameters. The dimensions should not be modified, however.
    renderPopup: (anchorPosition: PopupPosition, anchorOffset: number,
        popupWidth: number, popupHeight: number) => ReactNode;

    // Returns a mounted component instance that controls the triggering
    // of the popup. In the majority of cases, "anchor" of popup has
    // handlers to control when the popup will be seen and this function
    // is not required. In a few cases, where anchor is not the same as
    // the whole component that triggers when the popup wil be seen,
    // this can be used. For instance, a button combined with a chevron
    // icon, which on click triggers a popup below the chevron icon. In
    // this example, getElementTriggeringPopup() can return the container
    // with button and chevron icon.
    getElementTriggeringPopup?: () => React.Component<any, any>;

    // Called when the popup is dismissed. Popup.isDisplayed() will return
    // false for the popup being dismissed when this callback runs.
    onDismiss?: () => void;

    // Prioritized order of positions. Popup is positioned
    // relative to the anchor such that it remains on screen.
    // Default is ['bottom', 'right', 'top', 'left'].
    positionPriorities?: string[];

    // Position the popup inside its anchor.
    // In this mode only the first position priority will be used.
    useInnerPositioning?: boolean;

    // On pressed handler to notify whoever wanted to create the popup
    // that its anchor has been pressed.
    // IMPORTANT NOTE: This handler may be called when the component is
    // already unmounted as it uses a time delay accommodate
    // fade-out animations.
    onAnchorPressed?: (e: SyntheticEvent) => void;

    // Determines if the anchor invoking the popup should behave like a toggle.
    // If true, calling Popup.show will show the popup. A subsequent call
    // will hide the popup. If false or undefined (default), calling Popup.show
    // will always show the popup.
    dismissIfShown?: boolean;

    // By default, clicks or taps outside of a popup (unless they are on the
    // anchor) will not dismiss the active popup. If true, this overrides the
    // default behavior, in which case the popup must be dismissed explicitly.
    preventDismissOnPress?: boolean;

    // The popup may be left in the DOM after it's dismissed. This is a
    // performance optimization to make the popup appear faster when it's shown
    // again, intended for popups that tend to be shown repeatedly. Note that
    // this is only a hint, so callers shouldn't rely on caching behavior.
    cacheable?: boolean;

    // Android, iOS, and Windows only.
    // The id of the root view this popup is associated with.
    // Defaults to the view set by UserInterface.setMainView();
    rootViewId?: string;
}

方法

// Dismisses an already-displayed popup after a specified number
// of milliseconds
autoDismiss(popupId: string, dismissDelay: number = 0): void;

// Dismisses an already-displayed popup immediately
dismiss(popupId: string): void;

// Dismisses all popups immediately
dismissAll(): void;

// Displays a popup. Returns true if successful, false if the popup is
// already displayed
show(options: PopupOptions, popupId: string, showDelay: number = 0): boolean;

// Indicates whether the specified popup is displayed. If no id provided indicates if some popup is displayed.
isDisplayed(popupId?: string): boolean;

示例用法

const _popupId = 'myPopup';
let _popupDisplayed = false;

onHoverStart() {
    if (!this._popupDisplayed) {
        this.displayPopup();
    }
};

onHoverEnd() {
    RX.Popup.autoDismiss(_popupId, 2000);
};

displayPopup() {
    let popupOptions: RX.Types.PopupOptions = {
        getAnchor: () => {
            return this._mountedButton;
        },
        renderPopup: (anchorPosition: Types.PopupPosition, anchorOffset: number,
                popupWidth: number, popupHeight: number) => {
            return this._renderPopupView(anchorPosition,
                anchorOffset, popupWidth, popupHeight);
        },
        positionPriorities: ['right', 'left', 'bottom', 'top'],
        onDismiss: () => {
            this._popupDisplayed = false;
        }
    };

    RX.Popup.show(popupOptions, _popupId, 500);
    this._popupDisplayed = true;
}