###########################
Using the Web Components UI
###########################

|HCTHEN| brings support for custom Web Components to HOOPS Web Viewer. These components encapsulate the main features of HOOPS Web Viewer as HTML tags. These include the viewer itself and the model tree. There are also additional Web Components based on React which simplify UI implementation in your application.

.. admonition:: Note

    **The Web Components implementation is currently in a beta state.**

To demonstrate this feature, this package also includes a new HWV demo app. This is a sample React application that leverages the HOOPS Web Components. This is not an officially supported part of the SDK - rather, this is a sample application (partners wishing to use or adapt this for their own use must own the maintenance of their source code).

Here, we'll demonstrate how to get it working in the simplest way using **JavaScript**, **npm**, and **vite** (instructions for installing and using npm and vite are beyond the scope of this document). Follow these instructions to get it working on your system:


Minimal setup
-------------

1. Create a new vite project using ``npm create vite@latest``. In the vite install script, follow the prompts to enter a name for the app, choose "Vanilla", then JavaScript.
2. After the project is created, run ``npm install`` and ``npm run dev``. Note the localhost address that will be provided to you by Vite.

.. image:: images/vite-setup.png

3. Delete *index.html*, as well as all the files in the folders that are created (you can keep the folders themselves).
4. In the root directory, create a new *index.html*. 
5. Install the web viewer components using one of the following methods:

   **Option A: Direct npm installation**

   - Run ``npm install @ts3d-hoops/web-viewer-components``

   **Option B: Local installation from customer package**

   - Create a folder called *@ts3d-hoops* in the root directory
   - Locate *INSTALL_DIR/web_viewer/@ts3d-hoops* and copy all folders (except *beta-demo-app-src*) to your new *@ts3d-hoops* folder
   - Run ``npm install "./@ts3d-hoops/web-viewer-components"`` in a command prompt from your project directory

6. Copy any *.scs* file to the *public* folder of this new project. Sample files can be found in *INSTALL_DIR/quick_start/converted_models/standard/scs_models*.

7. Setup the WebAssembly engine files based on your installation method:

   **Option A: With npm installation**

   - Copy *engine.esm.wasm* from *node_modules/@ts3d-hoops/sc-engine/* to your *public* folder
   - Alternatively, configure your bundler (Vite, Webpack, etc.) to automatically copy the engine files during build

   **Option B: With local installation**

   - Copy *engine.esm.wasm* from *./@ts3d-hoops/sc-engine* to the *public* folder

8. Inside your new *index.html*, create a basic HTML structure and add the following code to the ``<body>``:

.. code-block:: js

    <hoops-web-viewer
        id="hwv"
        endPointUri="./microengine.scs"
        enginePath="./engine.esm.wasm">
    </hoops-web-viewer>

    <script type="module">
        import '@ts3d-hoops/web-viewer-components';
    </script>
    
9. For the ``endPointUri`` value, replace the filename with the *.scs* file you copied to the *public* directory. 

After navigating to the address generated by vite in step 2, you should see a model in your browser.


Adding a button
---------------

The new HOOPS UI comes with a few predefined Web Components. We'll start with a simple icon button.

Add the following HTML to the body of *index.html*:

.. code-block:: js

    <div id="hwvHomeButton" style="position:absolute; top: 10px; left: 10px;">
        <hoops-icon-button>
            <hoops-icon icon="home"></hoops-icon>
        </hoops-icon-button>
    </div>

This code shows the button on the screen, but it doesn't do anything yet. Add the following JavaScript inside the ``<script>`` tag (after the ``import`` statement):    

.. code-block:: js

    document.getElementById("hwv").addEventListener("hwvModelStructureReady", (e)=>{
        const { hwv } = e.detail;
        document.getElementById("hwvHomeButton").addEventListener("click", ()=>{
            hwv.reset();
        });
    });
    
You should now have a working "Home" button. Clicking this button will rotate the model back to its default position.   

.. image:: images/web_components_1.png


Customizing the appearance
..........................

Create a ``<style>`` tag in the *index.html* file and add the following to make the viewer appear full screen:

.. code-block:: js

    <style>

    body {
        margin: 0 auto;
    }
    
    hoops-web-viewer {
        width: 100vw;
        height: 100vh;
    }
    </style>
    
To change the appearance of the hoops-icon, you can customize the CSS variables that define its behavior, such as the stroke color. In the ``<style>`` element, add the following before the body:    

.. code-block:: js

    :root {
        --hoops-svg-stroke-color: blue;
    }
    
A list of all CSS variable can be found :ref:`here <css-vars>`.
    
    
Using the model tree
--------------------

The model tree is a HOOPS component that displays the assembly structure and its attributes hierarchically. It can be expanded and collapsed, and individual component visibility may be toggled. The model tree is represented as a web component. The associated HTML tag is ``<hoops-model-tree>``. To use it, you'll need to add the tree itself as well as its associated UI elements to your application. First, the model tree itself, along with an ``id``:

.. code-block:: html

    <hoops-model-tree id="hwvModelTree"></hoops-model-tree>
    
This positions the component and loads the model tree into memory, but it's not visible because visibility is disabled by default. Let's go through the process of adding the necessary logic to make it functional. We'll plan to toggle the visibility on and off with a button. There is already a HOOPS Web Component for this button, the ``<hoops-toolbar-model-tree>`` tag. Add the following code to your file for the toggle button:

.. code-block:: html

    <div id="ModelTree" style="position: absolute; top: 40px; left: 10px; z-index: 2;">
        <hoops-toolbar-model-tree id="hwvModelTreeButton"></hoops-toolbar-model-tree>
    </div>
    
In your ``<script>`` tag, add the following import statement and Boolean variable:

.. code-block:: js

    import { SelectionMode } from '@ts3d-hoops/web-viewer';
    let modelTreeVisible = false;
    
Then, in the ``hwvModelStructureReady`` callback, your code should look like the following:

.. code-block:: js
    
    document.getElementById("hwv").addEventListener("hwvModelStructureReady", (e) => {
    
        // logic to toggle the visibility of the model tree
        document.getElementById("hwvModelTreeButton").addEventListener("click", () => {
            if (modelTreeVisible) {
                document.getElementById("hwvModelTree").style.display = "none";
            }
            else {
                document.getElementById("hwvModelTree").style.display = "block";
            }
            modelTreeVisible = !modelTreeVisible;
        });
    });
    
This will enable the application to show and hide the model tree UI when you click the associated model tree button. The model tree can be expanded and collapsed, but it doesn't do much else. Let's expand on the previous code snippet to add further logic. Add this to the ``hwvModelStructureReady`` block:

.. code-block:: js    
    
    document.getElementById("hwvModelTree").model = hwv.model;
    const hwvModelTreeElement = document.getElementById("hwvModelTree");
    hwvModelTreeElement.addEventListener("hoops-model-tree-node-click", (e)=> {
        e.stopPropagation();
        hwv.selectionManager.clear(false);
        hwv.selectionManager.selectNode(e.detail.nodeId, SelectionMode.Add);
    });
    
Notice the call to ``selectNode``. This is what enables you to click a component in the model tree and automatically select the associated part in the model. Next, we'll enable the show/hide icon at the end of each line of the model tree. Continuing from the code above:

.. code-block:: js 

    hwvModelTreeElement.addEventListener("hoops-model-tree-node-visibility-change", (e)=> {
        e.stopPropagation();
        hwv.model.setNodesVisibility([e.detail.nodeId], e.detail.visibility);
        hwvModelTreeElement.updateNodeData(e.detail.nodeId, {
            visible: e.detail.visibility
        })
    });
    
Now, the model is selectable and its parts can be shown and hidden. 

.. admonition:: Note

    You need to update the tree if it changes from the Web Viewer. To update the entire tree, you can simply reassign the model to the tree. The underlying framework will detect a change in the model and update the tree automatically.

.. image:: images/model-tree.png


HOOPS layout
------------

The UI kit comes with a layout that can be used for common web page layouts.

The ``<hoops-layout>`` tag is a meta-component that may contain other components through slots, which are named as such: ``menu-bar``, ``toolbar-top``, ``panel-top``, ``toolbar-left``, ``panel-left``, ``central-widget``, ``panel-right``, ``toolbar-right``, ``panel-bottom``, ``toolbar-bottom``, and ``status-bar``.

To implement this in the code, you have to instantiate a ``hoops-layout``, create a component inside of it, and have its slot set to one of the above options. Your component should be placed accordingly in the layout. The code below illustrates how this works:

.. code-block:: html

    <hoops-layout floatingPanels>
        <div id="header" slot="menu-bar">Menu bar</div>
        <div id="toolbar" slot="toolbar-left">Toolbar</div>
        <div id="leftPanel" slot="panel-left">Left panel</div>
        <div slot="central-widget">
          <hoops-web-viewer
            className="viewer"
            endpointUri="microengine.scs"
            enginePath="."
          ></hoops-web-viewer>
        </div>
        <div id="rightPanel" slot="panel-right">Right panel</div>
    </hoops-layout>
