Web Components Dock Manager Desktop Integration

    El componente Infragistics Web Components Dock Manager se puede utilizar en una aplicación de escritorio Electron de múltiples ventanas para administrar el diseño de cada ventana, arrastrar paneles fuera de una ventana para crear una nueva ventana y arrastrar/soltar paneles de una ventana a otra. Puede encontrar un ejemplo de implementación de dicha aplicación en el siguiente repositorio https://github.com/IgniteUI/dock-manager-electron-app.

    Web Components Dock Manager desktop integration

    Implementation

    Repasemos las partes más importantes de la implementación de esta aplicación.

    Project Structure

    Hemos utilizado la herramienta CLI de Electron Forge y su plantilla Typecript + Webpack para crear una aplicación de Electron. Electron tiene dos tipos de procesos: principal y renderizador.

    • El proceso principal crea páginas web mediante la creación de instancias de BrowserWindow. Cada instancia de BrowserWindow ejecuta la página web en su proceso de renderizado.
    • El proceso Renderer gestiona solo la página web correspondiente.

    El script index.ts especifica el punto de entrada de la aplicación Electron que ejecutará el proceso principal. La mayor parte del código de nuestra aplicación está dentro del archivo renderer.ts que se ejecuta en el proceso de Renderer. El index.html representa el contenido de la página web. Los estilos de la página web están alojados en el archivo index.css.

    Dock Manager Setup

    Después de instalar el paquete Dock Manager, hemos registrado el componente Dock Manager usando defineCustomElements() en el archivo renderer.ts. Esto permite agregar elen el archivo index.html.

    Para el contenido del panel del Dock Manager, hemos utilizado elementos iframe que alojan diferentes URL. En nuestro caso, estas urls apuntan a muestras Ignite UI for Angular. Dado que los elementos iframe son autónomos, es fácil moverlos de una ventana a otra.

    Drag and drop

    In order to support dragging panes outside the application window we have replaced the built-in drag/drop which creates in-application floating panes with a custom implementation based on the HTML Drag and Drop API. We have subscribed to the PaneHeaderConnected and TabHeaderConnected events which are fired when a header element is connected to the DOM. When a header element is connected we reset the built-in dragService and attach DragStart and DragEnd event listeners.

    const paneHeaderConnected = (event: CustomEvent<IgcPaneHeaderConnectionEventArgs>) => {
        const element = event.detail.element;
        element.dragService.destroy();
        element.dragService = null;
        element.draggable = true;
        element.ondragstart = ev => {
            paneHeaderDragStart(event.detail.pane, ev);
        };
        element.ondragend = ev => {
            paneHeaderDragEnd(ev);
        };
    }
    
    dockManager.addEventListener('paneHeaderConnected', paneHeaderConnected);
    

    In the PaneHeaderDragStart function we set the draggedPane property of the Dock Manager component which will notify it that a drag operation has been started.

    const paneHeaderDragStart = async (pane: IgcContentPane, event: DragEvent) => {
        event.dataTransfer.dropEffect = 'move';
        dockManager.draggedPane = pane;
        // ...
    }
    

    We have subscribed to the DragOver and drop events of the document element. In the DragOver listener we notify the Dock Manager that the mouse is dragged over it by setting its dropPosition) property. This forces the Dock Manager to display its docking indicators.

    const handleDocumentDragOver = (event: DragEvent) => {
        event.preventDefault();
        event.dataTransfer.dropEffect = 'move';
        dockManager.dropPosition = {
          x: event.clientX,
          y: event.clientY
        };
    }
    
    document.addEventListener('dragover', handleDocumentDragOver);
    document.addEventListener('drop', handleDocumentDrop);
    

    In the paneHeaderDragEnd function we detect if the pane was dropped outside the application window and we call the droppedOutOfWindow function.

    const paneHeaderDragEnd = async (event: DragEvent) => {
        event.preventDefault();
        // ...
    
        // dropped outside of the application
        if (event.dataTransfer.dropEffect === 'none') {
            await droppedOutOfWindow(event);
        }
        // ...
    }
    

    When the pane header is dropped inside a document we call the DropPane method which notifies the Dock Manager that the dragged pane was dropped. If the pane was dropped on a docking indicator the method returns true. If the pane was dropped in the same window it was dragged from, the pane will be docked to its new position automatically. However, if it was dropped in another window we call the droppedInAnotherWindow function which first removes the pane from the source Dock Manager and then adds it to the new one.

    const handleDocumentDrop = async (event: DragEvent) => {
        const contentId = (dockManager.draggedPane as IgcContentPane).contentId;
    
        const docked = await dockManager.dropPane();
    
        if (docked) {
            const contentElement = dockManager.querySelector('[slot=' + contentId + ']');
    
            // if the content element is missing from the current dock manager it means it comes from another window
            if (!contentElement) {
                await droppedInAnotherWindow();
            }
        }
    }
    

    When a pane is dropped out of its current window, we need to remove the draggedPane from its Dock Manager component and update the layout.

    const draggedPane = dockManager.draggedPane as IgcContentPane;
    await dockManager.removePane(draggedPane);
    dockManager.layout = { ...dockManager.layout };
    

    A continuación, debemos mover el elemento de contenido del panel a su nueva ventana. Para este propósito, utilizamos el método document.adoptNode(), que nos permite transferir el nodo del elemento de contenido al nuevo documento y, finalmente, agregarlo como hijo del nuevo componente Dock Manager.

    const contentElement = dockManager.querySelector('[slot=' + draggedPane.contentId + ']');
    const newDocument = childWindow.document;
    const newDockManager = newDocument.getElementById('dockManager') as IgcDockManagerComponent;
    const adoptedNode = newDocument.adoptNode(contentElement);
    newDockManager.appendChild(adoptedNode);
    

    Window Management

    We are using the native window.open() method to open a new window in the Renderer process. We set the nativeWindowOpen option to true when creating the BrowserWindow in the index.ts. This gives us direct access to the child Window object and its document. You could read more about opening windows from the Renderer process in this Electron topic. Please note that the nativeWindowOpen option is still experimental.

    mainWindow = new BrowserWindow({
        height: 800,
        width: 1000,
        webPreferences: {
            nativeWindowOpen: true
        }
    });
    

    In this application we have implemented an IDockManagerWindow type which could be either a main window (IMainDockManagerWindow) or a child window (IChildDockManagerWindow). The main window is the one created when the application starts. It contains references to all its child windows. A child window is created when a pane is dropped out of a window and has a reference to the main window of the application.

    Para obtener el código fuente completo, clone el repositorio.

    API References