"use strict";
/*---------------------------------------------------------------------------------------------
 * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
 * See LICENSE.md in the project root for license terms and full copyright notice.
 *--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Widget
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.useWidgetAllowedToDock = exports.useFloatingWidgetId = exports.getResizeBy = exports.FloatingWidget = exports.FloatingWidgetProvider = void 0;
require("./FloatingWidget.scss");
const classnames_1 = require("classnames");
const React = require("react");
const core_bentley_1 = require("@itwin/core-bentley");
const core_react_1 = require("@itwin/core-react");
const DragManager_1 = require("../base/DragManager");
const NineZone_1 = require("../base/NineZone");
const ContentContainer_1 = require("./ContentContainer");
const TabBar_1 = require("./TabBar");
const Widget_1 = require("./Widget");
const usePointerCaptor_1 = require("../base/usePointerCaptor");
const WidgetTarget_1 = require("../target/WidgetTarget");
const WidgetOutline_1 = require("../outline/WidgetOutline");
const LayoutStore_1 = require("../base/LayoutStore");
const WidgetStateHelpers_1 = require("../state/internal/WidgetStateHelpers");
const useMaximizedWidget_1 = require("../../preview/enable-maximized-widget/useMaximizedWidget");
/** @internal */
function FloatingWidgetProvider(props) {
    const floatingWidget = React.useContext(NineZone_1.FloatingWidgetNodeContext);
    return React.createElement(Widget_1.WidgetProvider, { id: props.id }, floatingWidget);
}
exports.FloatingWidgetProvider = FloatingWidgetProvider;
/** @internal */
function FloatingWidget(props) {
    const uiIsVisible = React.useContext(NineZone_1.UiIsVisibleContext);
    const id = useFloatingWidgetId();
    (0, core_bentley_1.assert)(!!id);
    const { autoSized, bounds, hideWithUiWhenFloating, isToolSettingsTab, minimized, resizable, } = useFloatingWidgetState();
    const hideFloatingWidget = !uiIsVisible && hideWithUiWhenFloating;
    const item = React.useMemo(() => ({
        id,
        type: "widget",
    }), [id]);
    const dragged = (0, DragManager_1.useIsDraggedItem)(item);
    const ref = useHandleAutoSize(dragged);
    const maximizedWidget = (0, useMaximizedWidget_1.useMaximizedFloatingWidget)();
    const className = (0, classnames_1.default)("nz-widget-floatingWidget", dragged && "nz-dragged", isToolSettingsTab && "nz-floating-toolSettings", minimized && "nz-minimized", hideFloatingWidget && "nz-hidden", maximizedWidget.classNames);
    const style = React.useMemo(() => {
        const boundsRect = core_react_1.Rectangle.create(bounds);
        const { height, width } = boundsRect.getSize();
        const position = boundsRect.topLeft();
        return {
            transform: `translate(${position.x}px, ${position.y}px)`,
            height: minimized || autoSized ? undefined : height,
            width: autoSized ? undefined : width,
            maxHeight: autoSized ? "60%" : undefined,
            maxWidth: autoSized ? "60%" : undefined,
        };
    }, [autoSized, bounds, minimized]);
    const content = React.useMemo(() => (React.createElement(ContentContainer_1.WidgetContentContainer, null,
        React.createElement(WidgetTarget_1.WidgetTarget, null),
        React.createElement(WidgetOutline_1.WidgetOutline, null))), []);
    const handles = React.useMemo(() => resizable && (React.createElement(React.Fragment, null,
        React.createElement(FloatingWidgetHandle, { handle: "left" }),
        React.createElement(FloatingWidgetHandle, { handle: "top" }),
        React.createElement(FloatingWidgetHandle, { handle: "right" }),
        React.createElement(FloatingWidgetHandle, { handle: "bottom" }),
        React.createElement(FloatingWidgetHandle, { handle: "topLeft" }),
        React.createElement(FloatingWidgetHandle, { handle: "topRight" }),
        React.createElement(FloatingWidgetHandle, { handle: "bottomLeft" }),
        React.createElement(FloatingWidgetHandle, { handle: "bottomRight" }))), [resizable]);
    return (React.createElement(Widget_1.Widget, { className: className, widgetId: id, style: {
            ...style,
            ...maximizedWidget.style,
        }, ref: ref, onMouseEnter: props.onMouseEnter, onMouseLeave: props.onMouseLeave },
        React.createElement(TabBar_1.WidgetTabBar, { separator: !minimized }),
        content,
        handles));
}
exports.FloatingWidget = FloatingWidget;
function useFloatingWidgetState() {
    const id = useFloatingWidgetId();
    (0, core_bentley_1.assert)(!!id);
    return (0, LayoutStore_1.useLayout)((state) => {
        const widget = (0, WidgetStateHelpers_1.getWidgetState)(state, id);
        const floatingWidget = state.floatingWidgets.byId[id];
        const tabs = widget.tabs;
        const activeTabId = widget.activeTabId;
        const activeTab = state.tabs[activeTabId];
        const userSized = floatingWidget.userSized;
        const singleTab = 1 === tabs.length;
        const toolSettingsTabId = state.toolSettings?.tabId;
        const isToolSettingsTab = widget.tabs[0] === toolSettingsTabId;
        const resizable = !!floatingWidget.resizable && !isToolSettingsTab;
        const autoSized = singleTab && !userSized;
        return {
            autoSized,
            bounds: floatingWidget.bounds,
            hideWithUiWhenFloating: activeTab.hideWithUiWhenFloating,
            isToolSettingsTab,
            minimized: widget.minimized,
            resizable,
        };
    }, true);
}
// Re-adjust bounds so that widget is behind pointer when auto-sized.
function useHandleAutoSize(dragged) {
    const dragManager = React.useContext(DragManager_1.DragManagerContext);
    const dispatch = React.useContext(NineZone_1.NineZoneDispatchContext);
    const measureNz = React.useContext(NineZone_1.MeasureContext);
    const id = useFloatingWidgetId();
    (0, core_bentley_1.assert)(!!id);
    const userSized = (0, LayoutStore_1.useLayout)((state) => state.floatingWidgets.byId[id].userSized);
    const maximizedWidget = (0, useMaximizedWidget_1.useIsMaximizedWidget)();
    const updatePosition = React.useRef(true);
    const ref = React.useRef(null);
    React.useLayoutEffect(() => {
        if (!updatePosition.current)
            return;
        if (!dragged)
            return;
        if (maximizedWidget)
            return;
        if (!dragManager.draggedItem)
            return;
        if (!ref.current)
            return;
        let bounds = core_react_1.Rectangle.create(ref.current.getBoundingClientRect());
        const nzBounds = measureNz();
        const pointerPosition = dragManager.draggedItem.info.pointerPosition;
        if (bounds.containsPoint(pointerPosition))
            return;
        // Pointer is outside of tab area. Need to re-adjust widget bounds so that tab is behind pointer
        if (pointerPosition.x > bounds.right) {
            const offset = pointerPosition.x - bounds.right + 20;
            bounds = bounds.offsetX(offset);
        }
        // Adjust bounds to be relative to 9z origin
        bounds = bounds.offset({ x: -nzBounds.left, y: -nzBounds.top });
        dispatch({
            type: "FLOATING_WIDGET_SET_BOUNDS",
            id,
            bounds: bounds.toProps(),
        });
        dispatch({
            type: "FLOATING_WIDGET_SET_USER_SIZED",
            id,
            userSized: true,
        });
        updatePosition.current = false;
    }, [dragged, dragManager, dispatch, id, measureNz, maximizedWidget]);
    const handleResize = React.useCallback(() => {
        if (!ref.current)
            return;
        if (dragged)
            return;
        if (maximizedWidget)
            return;
        if (userSized)
            return;
        let bounds = core_react_1.Rectangle.create(ref.current.getBoundingClientRect());
        // Adjust bounds to be relative to 9z origin
        const nzBounds = measureNz();
        bounds = bounds.offset({ x: -nzBounds.left, y: -nzBounds.top });
        dispatch({
            type: "FLOATING_WIDGET_SET_BOUNDS",
            id,
            bounds,
        });
    }, [dispatch, dragged, id, userSized, measureNz, maximizedWidget]);
    const roRef = (0, core_react_1.useResizeObserver)(handleResize);
    const refs = (0, core_react_1.useRefs)(ref, roRef);
    return refs;
}
function FloatingWidgetHandle(props) {
    const dispatch = React.useContext(NineZone_1.NineZoneDispatchContext);
    const id = useFloatingWidgetId();
    const { handle } = props;
    const relativePosition = React.useRef(new core_react_1.Point());
    (0, core_bentley_1.assert)(id !== undefined);
    const onDrag = React.useCallback((pointerPosition) => {
        (0, core_bentley_1.assert)(!!ref.current);
        const bounds = core_react_1.Rectangle.create(ref.current.getBoundingClientRect());
        const newRelativePosition = bounds.topLeft().getOffsetTo(pointerPosition);
        const offset = relativePosition.current.getOffsetTo(newRelativePosition);
        const resizeBy = getResizeBy(handle, offset);
        dispatch({
            type: "FLOATING_WIDGET_RESIZE",
            id,
            resizeBy,
        });
    }, [dispatch, handle, id]);
    const handleDragStart = (0, DragManager_1.useDragResizeHandle)({
        handle,
        widgetId: id,
        onDrag,
    });
    const handlePointerDown = React.useCallback((args) => {
        (0, core_bentley_1.assert)(!!ref.current);
        const bounds = core_react_1.Rectangle.create(ref.current.getBoundingClientRect());
        const initialPointerPosition = new core_react_1.Point(args.clientX, args.clientY);
        relativePosition.current = bounds
            .topLeft()
            .getOffsetTo(initialPointerPosition);
        handleDragStart({
            initialPointerPosition,
            pointerPosition: initialPointerPosition,
        });
        dispatch({
            type: "FLOATING_WIDGET_BRING_TO_FRONT",
            id,
        });
    }, [dispatch, handleDragStart, id]);
    const pointerCaptorRef = (0, usePointerCaptor_1.usePointerCaptor)(handlePointerDown);
    const ref = React.useRef(null);
    const refs = (0, core_react_1.useRefs)(ref, pointerCaptorRef);
    const isMaximized = (0, useMaximizedWidget_1.useIsMaximizedWidget)();
    const className = (0, classnames_1.default)("nz-widget-floatingWidget_handle", `nz-${handle}`);
    if (isMaximized)
        return null;
    return React.createElement("div", { className: className, ref: refs });
}
/** @internal */
function getResizeBy(handle, offset) {
    switch (handle) {
        case "left":
            return new core_react_1.Rectangle(-offset.x);
        case "top":
            return new core_react_1.Rectangle(0, -offset.y);
        case "right":
            return new core_react_1.Rectangle(0, 0, offset.x);
        case "bottom":
            return new core_react_1.Rectangle(0, 0, 0, offset.y);
        case "topLeft":
            return new core_react_1.Rectangle(-offset.x, -offset.y);
        case "topRight":
            return new core_react_1.Rectangle(0, -offset.y, offset.x);
        case "bottomLeft":
            return new core_react_1.Rectangle(-offset.x, 0, 0, offset.y);
        case "bottomRight":
            return new core_react_1.Rectangle(0, 0, offset.x, offset.y);
    }
}
exports.getResizeBy = getResizeBy;
/** @internal */
function useFloatingWidgetId() {
    const widgetId = React.useContext(Widget_1.WidgetIdContext);
    return (0, LayoutStore_1.useLayout)((state) => {
        if (!widgetId)
            return undefined;
        const floatingWidget = state.floatingWidgets.byId[widgetId];
        if (!floatingWidget)
            return undefined;
        return widgetId;
    });
}
exports.useFloatingWidgetId = useFloatingWidgetId;
/** @internal */
function useWidgetAllowedToDock() {
    const id = useFloatingWidgetId();
    return (0, LayoutStore_1.useLayout)((state) => {
        if (id) {
            const widget = (0, WidgetStateHelpers_1.getWidgetState)(state, id);
            const activeTab = state.tabs[widget.activeTabId];
            if (activeTab.allowedPanelTargets?.length === 0) {
                return false;
            }
        }
        return true;
    });
}
exports.useWidgetAllowedToDock = useWidgetAllowedToDock;
//# sourceMappingURL=FloatingWidget.js.map