Widget.react.js

import { Fragment } from 'react';
import PropTypes from 'prop-types';
import DashLuminoComponent from '../component.js'

import {
    Widget as l_Widget
} from '@lumino/widgets';
import { components, get_id } from '../registry.js';


/**
 * Wrap the default Lumino Widget with an event once the widget is closed
 * @private
 */
class LuminoWidget extends l_Widget {

    constructor(props) {
        super(props)
    }

    onCloseRequest(msg) {
        const dom_element = document.getElementById(this.id);
        const event = new CustomEvent('lumino:deleted', { msg: msg, id: this.id });

        //Note: this might dissapear if widgets can be deleted from the DockPanel
        // children in the future!
        let { setProps } = components[this.id].dash.props;
        setProps({ children: [], deleted: true });


        dom_element.dispatchEvent(event);
        super.onCloseRequest(msg);
    }

    onActivateRequest(msg) {
        const dom_element = document.getElementById(this.id);
        const event = new CustomEvent('lumino:activated', { msg: msg, id: this.id });
        dom_element.dispatchEvent(event);
        super.onActivateRequest(msg);        
    }


};


/**
 * The base class of the lumino widget hierarchy.  
 * {@link https://jupyterlab.github.io/lumino/widgets/classes/widget.html}
 * 
 * This class will typically be subclassed in order to create a useful
 * widget. However, it can be used directly to host externally created
 * content.
 * @hideconstructor
 * 
 * @example
 * //Python:
 * import dash
 * import dash_lumino_components as dlc
 * 
 * dock = dlc.DockPanel([
 *     dlc.Widget(
 *         "Content",
 *         id="test-widget",
 *         title="Title",
 *         icon="fa fa-folder-open",
 *         closable=True,
 *         caption="Hover label of the widget"
 *     )],
 *     id="dock-panel")
 */
class Widget extends DashLuminoComponent {


    constructor(props) {
        super(props);

        // register a new Panel
        let luminoComponent = super.register(new LuminoWidget({ node: document.createElement('div') }));

        // set properties
        luminoComponent.title.label = props.title;
        luminoComponent.title.closable = props.closable;
        luminoComponent.title.caption = props.caption;
        luminoComponent.title.iconClass = props.icon

        // the component will initially be renderd in an hidden container,
        // then it's moved to the right dom location of lumino
        this.containerName = get_id(props) + "-container";


        // add the children of the component to the widgets of the panel
        if (this.props.children) {
            super.parseChildrenToArray().forEach(el => {
                super.applyAfterDomCreation(el, this.containerName, (target, child) => {
                    target.div = child;
                    target.lumino.node.appendChild(child.children[0]);
                });
            })
        }

    }


    render() {
        return (
            <div id={this.containerName} style={{
                visibility: 'hidden',
                height: 0,
                width: 0,
                minHeight: 0,
                minWidth: 0,
                margin: 0,
                padding: 0,
                maxHeight: 0,
                position: 'absolute'
            }}>
                <div className="lm-panel">
                    <Fragment>{this.props.children}</Fragment>
                </div>
            </div>
        );
    }

}

Widget.defaultProps = {
    closable: true,
    deleted: false
};

/**
 * @typedef
 * @enum {}
 */
Widget.propTypes = {
    /**
     * ID of the widget
     * @type {string}
     */
    id: PropTypes.string.isRequired,

    /**
     * The children of this component
     * @type {Object | Object[]}
     */
    children: PropTypes.node,

    /**
     * The title of the widget
     * @type {string}
     */
    title: PropTypes.string.isRequired,

    /**
     * Is the widget closable
     * @type {boolean}
     */
    closable: PropTypes.bool,

    /**
     * The long title of the widget
     * @type {string}
     */
    caption: PropTypes.string,

    /**
     * Is the widget deleted.
     * Note: In the future this might dissapear and the deleted widgets are
     * automatically removed from the dom.
     * @type {boolean}
     */
    deleted: PropTypes.bool,

    /**
     * The icon of the widget (a cass class name)
     * @type {string}
     */
    icon: PropTypes.string,

    /**
     * Dash-assigned callback that should be called to report property changes
     * to Dash, to make them available for callbacks.
     * @private
     */
    setProps: PropTypes.func
};


/**
 * @private
 */
export default Widget;