UNPKG

@lumino/widgets

Version:
15,999 lines • 614 kB
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@lumino/algorithm'), require('@lumino/coreutils'), require('@lumino/domutils'), require('@lumino/messaging'), require('@lumino/properties'), require('@lumino/signaling'), require('@lumino/dragdrop'), require('@lumino/commands'), require('@lumino/virtualdom'), require('@lumino/disposable'), require('@lumino/keyboard')) :
    typeof define === 'function' && define.amd ? define(['exports', '@lumino/algorithm', '@lumino/coreutils', '@lumino/domutils', '@lumino/messaging', '@lumino/properties', '@lumino/signaling', '@lumino/dragdrop', '@lumino/commands', '@lumino/virtualdom', '@lumino/disposable', '@lumino/keyboard'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.lumino_widgets = {}, global.lumino_algorithm, global.lumino_coreutils, global.lumino_domutils, global.lumino_messaging, global.lumino_properties, global.lumino_signaling, global.lumino_dragdrop, global.lumino_commands, global.lumino_virtualdom, global.lumino_disposable, global.lumino_keyboard));
})(this, (function (exports, algorithm, coreutils, domutils, messaging, properties, signaling, dragdrop, commands, virtualdom, disposable, keyboard) { 'use strict';

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A sizer object for use with the box engine layout functions.
     *
     * #### Notes
     * A box sizer holds the geometry information for an object along an
     * arbitrary layout orientation.
     *
     * For best performance, this class should be treated as a raw data
     * struct. It should not typically be subclassed.
     */
    class BoxSizer {
        constructor() {
            /**
             * The preferred size for the sizer.
             *
             * #### Notes
             * The sizer will be given this initial size subject to its size
             * bounds. The sizer will not deviate from this size unless such
             * deviation is required to fit into the available layout space.
             *
             * There is no limit to this value, but it will be clamped to the
             * bounds defined by {@link minSize} and {@link maxSize}.
             *
             * The default value is `0`.
             */
            this.sizeHint = 0;
            /**
             * The minimum size of the sizer.
             *
             * #### Notes
             * The sizer will never be sized less than this value, even if
             * it means the sizer will overflow the available layout space.
             *
             * It is assumed that this value lies in the range `[0, Infinity)`
             * and that it is `<=` to {@link maxSize}. Failure to adhere to this
             * constraint will yield undefined results.
             *
             * The default value is `0`.
             */
            this.minSize = 0;
            /**
             * The maximum size of the sizer.
             *
             * #### Notes
             * The sizer will never be sized greater than this value, even if
             * it means the sizer will underflow the available layout space.
             *
             * It is assumed that this value lies in the range `[0, Infinity]`
             * and that it is `>=` to {@link minSize}. Failure to adhere to this
             * constraint will yield undefined results.
             *
             * The default value is `Infinity`.
             */
            this.maxSize = Infinity;
            /**
             * The stretch factor for the sizer.
             *
             * #### Notes
             * This controls how much the sizer stretches relative to its sibling
             * sizers when layout space is distributed. A stretch factor of zero
             * is special and will cause the sizer to only be resized after all
             * other sizers with a stretch factor greater than zero have been
             * resized to their limits.
             *
             * It is assumed that this value is an integer that lies in the range
             * `[0, Infinity)`. Failure to adhere to this constraint will yield
             * undefined results.
             *
             * The default value is `1`.
             */
            this.stretch = 1;
            /**
             * The computed size of the sizer.
             *
             * #### Notes
             * This value is the output of a call to {@link BoxEngine.calc}. It represents
             * the computed size for the object along the layout orientation,
             * and will always lie in the range `[minSize, maxSize]`.
             *
             * This value is output only.
             *
             * Changing this value will have no effect.
             */
            this.size = 0;
            /**
             * An internal storage property for the layout algorithm.
             *
             * #### Notes
             * This value is used as temporary storage by the layout algorithm.
             *
             * Changing this value will have no effect.
             */
            this.done = false;
        }
    }
    /**
     * The namespace for the box engine layout functions.
     */
    exports.BoxEngine = void 0;
    (function (BoxEngine) {
        /**
         * Calculate the optimal layout sizes for a sequence of box sizers.
         *
         * This distributes the available layout space among the box sizers
         * according to the following algorithm:
         *
         * 1. Initialize the sizers's size to its size hint and compute the
         *    sums for each of size hint, min size, and max size.
         *
         * 2. If the total size hint equals the available space, return.
         *
         * 3. If the available space is less than the total min size, set all
         *    sizers to their min size and return.
         *
         * 4. If the available space is greater than the total max size, set
         *    all sizers to their max size and return.
         *
         * 5. If the layout space is less than the total size hint, distribute
         *    the negative delta as follows:
         *
         *    a. Shrink each sizer with a stretch factor greater than zero by
         *       an amount proportional to the negative space and the sum of
         *       stretch factors. If the sizer reaches its min size, remove
         *       it and its stretch factor from the computation.
         *
         *    b. If after adjusting all stretch sizers there remains negative
         *       space, distribute the space equally among the sizers with a
         *       stretch factor of zero. If a sizer reaches its min size,
         *       remove it from the computation.
         *
         * 6. If the layout space is greater than the total size hint,
         *    distribute the positive delta as follows:
         *
         *    a. Expand each sizer with a stretch factor greater than zero by
         *       an amount proportional to the postive space and the sum of
         *       stretch factors. If the sizer reaches its max size, remove
         *       it and its stretch factor from the computation.
         *
         *    b. If after adjusting all stretch sizers there remains positive
         *       space, distribute the space equally among the sizers with a
         *       stretch factor of zero. If a sizer reaches its max size,
         *       remove it from the computation.
         *
         * 7. return
         *
         * @param sizers - The sizers for a particular layout line.
         *
         * @param space - The available layout space for the sizers.
         *
         * @returns The delta between the provided available space and the
         *   actual consumed space. This value will be zero if the sizers
         *   can be adjusted to fit, negative if the available space is too
         *   small, and positive if the available space is too large.
         *
         * #### Notes
         * The {@link BoxSizer.size} of each sizer is updated with the computed size.
         *
         * This function can be called at any time to recompute the layout for
         * an existing sequence of sizers. The previously computed results will
         * have no effect on the new output. It is therefore not necessary to
         * create new sizer objects on each resize event.
         */
        function calc(sizers, space) {
            // Bail early if there is nothing to do.
            let count = sizers.length;
            if (count === 0) {
                return space;
            }
            // Setup the size and stretch counters.
            let totalMin = 0;
            let totalMax = 0;
            let totalSize = 0;
            let totalStretch = 0;
            let stretchCount = 0;
            // Setup the sizers and compute the totals.
            for (let i = 0; i < count; ++i) {
                let sizer = sizers[i];
                let min = sizer.minSize;
                let max = sizer.maxSize;
                let hint = sizer.sizeHint;
                sizer.done = false;
                sizer.size = Math.max(min, Math.min(hint, max));
                totalSize += sizer.size;
                totalMin += min;
                totalMax += max;
                if (sizer.stretch > 0) {
                    totalStretch += sizer.stretch;
                    stretchCount++;
                }
            }
            // If the space is equal to the total size, return early.
            if (space === totalSize) {
                return 0;
            }
            // If the space is less than the total min, minimize each sizer.
            if (space <= totalMin) {
                for (let i = 0; i < count; ++i) {
                    let sizer = sizers[i];
                    sizer.size = sizer.minSize;
                }
                return space - totalMin;
            }
            // If the space is greater than the total max, maximize each sizer.
            if (space >= totalMax) {
                for (let i = 0; i < count; ++i) {
                    let sizer = sizers[i];
                    sizer.size = sizer.maxSize;
                }
                return space - totalMax;
            }
            // The loops below perform sub-pixel precision sizing. A near zero
            // value is used for compares instead of zero to ensure that the
            // loop terminates when the subdivided space is reasonably small.
            let nearZero = 0.01;
            // A counter which is decremented each time a sizer is resized to
            // its limit. This ensures the loops terminate even if there is
            // space remaining to distribute.
            let notDoneCount = count;
            // Distribute negative delta space.
            if (space < totalSize) {
                // Shrink each stretchable sizer by an amount proportional to its
                // stretch factor. If a sizer reaches its min size it's marked as
                // done. The loop progresses in phases where each sizer is given
                // a chance to consume its fair share for the pass, regardless of
                // whether a sizer before it reached its limit. This continues
                // until the stretchable sizers or the free space is exhausted.
                let freeSpace = totalSize - space;
                while (stretchCount > 0 && freeSpace > nearZero) {
                    let distSpace = freeSpace;
                    let distStretch = totalStretch;
                    for (let i = 0; i < count; ++i) {
                        let sizer = sizers[i];
                        if (sizer.done || sizer.stretch === 0) {
                            continue;
                        }
                        let amt = (sizer.stretch * distSpace) / distStretch;
                        if (sizer.size - amt <= sizer.minSize) {
                            freeSpace -= sizer.size - sizer.minSize;
                            totalStretch -= sizer.stretch;
                            sizer.size = sizer.minSize;
                            sizer.done = true;
                            notDoneCount--;
                            stretchCount--;
                        }
                        else {
                            freeSpace -= amt;
                            sizer.size -= amt;
                        }
                    }
                }
                // Distribute any remaining space evenly among the non-stretchable
                // sizers. This progresses in phases in the same manner as above.
                while (notDoneCount > 0 && freeSpace > nearZero) {
                    let amt = freeSpace / notDoneCount;
                    for (let i = 0; i < count; ++i) {
                        let sizer = sizers[i];
                        if (sizer.done) {
                            continue;
                        }
                        if (sizer.size - amt <= sizer.minSize) {
                            freeSpace -= sizer.size - sizer.minSize;
                            sizer.size = sizer.minSize;
                            sizer.done = true;
                            notDoneCount--;
                        }
                        else {
                            freeSpace -= amt;
                            sizer.size -= amt;
                        }
                    }
                }
            }
            // Distribute positive delta space.
            else {
                // Expand each stretchable sizer by an amount proportional to its
                // stretch factor. If a sizer reaches its max size it's marked as
                // done. The loop progresses in phases where each sizer is given
                // a chance to consume its fair share for the pass, regardless of
                // whether a sizer before it reached its limit. This continues
                // until the stretchable sizers or the free space is exhausted.
                let freeSpace = space - totalSize;
                while (stretchCount > 0 && freeSpace > nearZero) {
                    let distSpace = freeSpace;
                    let distStretch = totalStretch;
                    for (let i = 0; i < count; ++i) {
                        let sizer = sizers[i];
                        if (sizer.done || sizer.stretch === 0) {
                            continue;
                        }
                        let amt = (sizer.stretch * distSpace) / distStretch;
                        if (sizer.size + amt >= sizer.maxSize) {
                            freeSpace -= sizer.maxSize - sizer.size;
                            totalStretch -= sizer.stretch;
                            sizer.size = sizer.maxSize;
                            sizer.done = true;
                            notDoneCount--;
                            stretchCount--;
                        }
                        else {
                            freeSpace -= amt;
                            sizer.size += amt;
                        }
                    }
                }
                // Distribute any remaining space evenly among the non-stretchable
                // sizers. This progresses in phases in the same manner as above.
                while (notDoneCount > 0 && freeSpace > nearZero) {
                    let amt = freeSpace / notDoneCount;
                    for (let i = 0; i < count; ++i) {
                        let sizer = sizers[i];
                        if (sizer.done) {
                            continue;
                        }
                        if (sizer.size + amt >= sizer.maxSize) {
                            freeSpace -= sizer.maxSize - sizer.size;
                            sizer.size = sizer.maxSize;
                            sizer.done = true;
                            notDoneCount--;
                        }
                        else {
                            freeSpace -= amt;
                            sizer.size += amt;
                        }
                    }
                }
            }
            // Indicate that the consumed space equals the available space.
            return 0;
        }
        BoxEngine.calc = calc;
        /**
         * Adjust a sizer by a delta and update its neighbors accordingly.
         *
         * @param sizers - The sizers which should be adjusted.
         *
         * @param index - The index of the sizer to grow.
         *
         * @param delta - The amount to adjust the sizer, positive or negative.
         *
         * #### Notes
         * This will adjust the indicated sizer by the specified amount, along
         * with the sizes of the appropriate neighbors, subject to the limits
         * specified by each of the sizers.
         *
         * This is useful when implementing box layouts where the boundaries
         * between the sizers are interactively adjustable by the user.
         */
        function adjust(sizers, index, delta) {
            // Bail early when there is nothing to do.
            if (sizers.length === 0 || delta === 0) {
                return;
            }
            // Dispatch to the proper implementation.
            if (delta > 0) {
                growSizer(sizers, index, delta);
            }
            else {
                shrinkSizer(sizers, index, -delta);
            }
        }
        BoxEngine.adjust = adjust;
        /**
         * Grow a sizer by a positive delta and adjust neighbors.
         */
        function growSizer(sizers, index, delta) {
            // Compute how much the items to the left can expand.
            let growLimit = 0;
            for (let i = 0; i <= index; ++i) {
                let sizer = sizers[i];
                growLimit += sizer.maxSize - sizer.size;
            }
            // Compute how much the items to the right can shrink.
            let shrinkLimit = 0;
            for (let i = index + 1, n = sizers.length; i < n; ++i) {
                let sizer = sizers[i];
                shrinkLimit += sizer.size - sizer.minSize;
            }
            // Clamp the delta adjustment to the limits.
            delta = Math.min(delta, growLimit, shrinkLimit);
            // Grow the sizers to the left by the delta.
            let grow = delta;
            for (let i = index; i >= 0 && grow > 0; --i) {
                let sizer = sizers[i];
                let limit = sizer.maxSize - sizer.size;
                if (limit >= grow) {
                    sizer.sizeHint = sizer.size + grow;
                    grow = 0;
                }
                else {
                    sizer.sizeHint = sizer.size + limit;
                    grow -= limit;
                }
            }
            // Shrink the sizers to the right by the delta.
            let shrink = delta;
            for (let i = index + 1, n = sizers.length; i < n && shrink > 0; ++i) {
                let sizer = sizers[i];
                let limit = sizer.size - sizer.minSize;
                if (limit >= shrink) {
                    sizer.sizeHint = sizer.size - shrink;
                    shrink = 0;
                }
                else {
                    sizer.sizeHint = sizer.size - limit;
                    shrink -= limit;
                }
            }
        }
        /**
         * Shrink a sizer by a positive delta and adjust neighbors.
         */
        function shrinkSizer(sizers, index, delta) {
            // Compute how much the items to the right can expand.
            let growLimit = 0;
            for (let i = index + 1, n = sizers.length; i < n; ++i) {
                let sizer = sizers[i];
                growLimit += sizer.maxSize - sizer.size;
            }
            // Compute how much the items to the left can shrink.
            let shrinkLimit = 0;
            for (let i = 0; i <= index; ++i) {
                let sizer = sizers[i];
                shrinkLimit += sizer.size - sizer.minSize;
            }
            // Clamp the delta adjustment to the limits.
            delta = Math.min(delta, growLimit, shrinkLimit);
            // Grow the sizers to the right by the delta.
            let grow = delta;
            for (let i = index + 1, n = sizers.length; i < n && grow > 0; ++i) {
                let sizer = sizers[i];
                let limit = sizer.maxSize - sizer.size;
                if (limit >= grow) {
                    sizer.sizeHint = sizer.size + grow;
                    grow = 0;
                }
                else {
                    sizer.sizeHint = sizer.size + limit;
                    grow -= limit;
                }
            }
            // Shrink the sizers to the left by the delta.
            let shrink = delta;
            for (let i = index; i >= 0 && shrink > 0; --i) {
                let sizer = sizers[i];
                let limit = sizer.size - sizer.minSize;
                if (limit >= shrink) {
                    sizer.sizeHint = sizer.size - shrink;
                    shrink = 0;
                }
                else {
                    sizer.sizeHint = sizer.size - limit;
                    shrink -= limit;
                }
            }
        }
    })(exports.BoxEngine || (exports.BoxEngine = {}));

    /**
     * An object which holds data related to an object's title.
     *
     * #### Notes
     * A title object is intended to hold the data necessary to display a
     * header for a particular object. A common example is the `TabPanel`,
     * which uses the widget title to populate the tab for a child widget.
     *
     * It is the responsibility of the owner to call the title disposal.
     */
    class Title {
        /**
         * Construct a new title.
         *
         * @param options - The options for initializing the title.
         */
        constructor(options) {
            this._label = '';
            this._caption = '';
            this._mnemonic = -1;
            this._icon = undefined;
            this._iconClass = '';
            this._iconLabel = '';
            this._className = '';
            this._closable = false;
            this._changed = new signaling.Signal(this);
            this._isDisposed = false;
            this.owner = options.owner;
            if (options.label !== undefined) {
                this._label = options.label;
            }
            if (options.mnemonic !== undefined) {
                this._mnemonic = options.mnemonic;
            }
            if (options.icon !== undefined) {
                this._icon = options.icon;
            }
            if (options.iconClass !== undefined) {
                this._iconClass = options.iconClass;
            }
            if (options.iconLabel !== undefined) {
                this._iconLabel = options.iconLabel;
            }
            if (options.caption !== undefined) {
                this._caption = options.caption;
            }
            if (options.className !== undefined) {
                this._className = options.className;
            }
            if (options.closable !== undefined) {
                this._closable = options.closable;
            }
            this._dataset = options.dataset || {};
        }
        /**
         * A signal emitted when the state of the title changes.
         */
        get changed() {
            return this._changed;
        }
        /**
         * Get the label for the title.
         *
         * #### Notes
         * The default value is an empty string.
         */
        get label() {
            return this._label;
        }
        /**
         * Set the label for the title.
         */
        set label(value) {
            if (this._label === value) {
                return;
            }
            this._label = value;
            this._changed.emit(undefined);
        }
        /**
         * Get the mnemonic index for the title.
         *
         * #### Notes
         * The default value is `-1`.
         */
        get mnemonic() {
            return this._mnemonic;
        }
        /**
         * Set the mnemonic index for the title.
         */
        set mnemonic(value) {
            if (this._mnemonic === value) {
                return;
            }
            this._mnemonic = value;
            this._changed.emit(undefined);
        }
        /**
         * Get the icon renderer for the title.
         *
         * #### Notes
         * The default value is undefined.
         */
        get icon() {
            return this._icon;
        }
        /**
         * Set the icon renderer for the title.
         *
         * #### Notes
         * A renderer is an object that supplies a render and unrender function.
         */
        set icon(value) {
            if (this._icon === value) {
                return;
            }
            this._icon = value;
            this._changed.emit(undefined);
        }
        /**
         * Get the icon class name for the title.
         *
         * #### Notes
         * The default value is an empty string.
         */
        get iconClass() {
            return this._iconClass;
        }
        /**
         * Set the icon class name for the title.
         *
         * #### Notes
         * Multiple class names can be separated with whitespace.
         */
        set iconClass(value) {
            if (this._iconClass === value) {
                return;
            }
            this._iconClass = value;
            this._changed.emit(undefined);
        }
        /**
         * Get the icon label for the title.
         *
         * #### Notes
         * The default value is an empty string.
         */
        get iconLabel() {
            return this._iconLabel;
        }
        /**
         * Set the icon label for the title.
         *
         * #### Notes
         * Multiple class names can be separated with whitespace.
         */
        set iconLabel(value) {
            if (this._iconLabel === value) {
                return;
            }
            this._iconLabel = value;
            this._changed.emit(undefined);
        }
        /**
         * Get the caption for the title.
         *
         * #### Notes
         * The default value is an empty string.
         */
        get caption() {
            return this._caption;
        }
        /**
         * Set the caption for the title.
         */
        set caption(value) {
            if (this._caption === value) {
                return;
            }
            this._caption = value;
            this._changed.emit(undefined);
        }
        /**
         * Get the extra class name for the title.
         *
         * #### Notes
         * The default value is an empty string.
         */
        get className() {
            return this._className;
        }
        /**
         * Set the extra class name for the title.
         *
         * #### Notes
         * Multiple class names can be separated with whitespace.
         */
        set className(value) {
            if (this._className === value) {
                return;
            }
            this._className = value;
            this._changed.emit(undefined);
        }
        /**
         * Get the closable state for the title.
         *
         * #### Notes
         * The default value is `false`.
         */
        get closable() {
            return this._closable;
        }
        /**
         * Set the closable state for the title.
         *
         * #### Notes
         * This controls the presence of a close icon when applicable.
         */
        set closable(value) {
            if (this._closable === value) {
                return;
            }
            this._closable = value;
            this._changed.emit(undefined);
        }
        /**
         * Get the dataset for the title.
         *
         * #### Notes
         * The default value is an empty dataset.
         */
        get dataset() {
            return this._dataset;
        }
        /**
         * Set the dataset for the title.
         *
         * #### Notes
         * This controls the data attributes when applicable.
         */
        set dataset(value) {
            if (this._dataset === value) {
                return;
            }
            this._dataset = value;
            this._changed.emit(undefined);
        }
        /**
         * Test whether the title has been disposed.
         */
        get isDisposed() {
            return this._isDisposed;
        }
        /**
         * Dispose of the resources held by the title.
         *
         * #### Notes
         * It is the responsibility of the owner to call the title disposal.
         */
        dispose() {
            if (this.isDisposed) {
                return;
            }
            this._isDisposed = true;
            signaling.Signal.clearData(this);
        }
    }

    /**
     * The base class of the lumino widget hierarchy.
     *
     * #### Notes
     * This class will typically be subclassed in order to create a useful
     * widget. However, it can be used directly to host externally created
     * content.
     */
    class Widget {
        /**
         * Construct a new widget.
         *
         * @param options - The options for initializing the widget.
         */
        constructor(options = {}) {
            this._flags = 0;
            this._layout = null;
            this._parent = null;
            this._disposed = new signaling.Signal(this);
            this._hiddenMode = Widget.HiddenMode.Display;
            this.node = Private$j.createNode(options);
            this.addClass('lm-Widget');
        }
        /**
         * Dispose of the widget and its descendant widgets.
         *
         * #### Notes
         * It is unsafe to use the widget after it has been disposed.
         *
         * All calls made to this method after the first are a no-op.
         */
        dispose() {
            // Do nothing if the widget is already disposed.
            if (this.isDisposed) {
                return;
            }
            // Set the disposed flag and emit the disposed signal.
            this.setFlag(Widget.Flag.IsDisposed);
            this._disposed.emit(undefined);
            // Remove or detach the widget if necessary.
            if (this.parent) {
                this.parent = null;
            }
            else if (this.isAttached) {
                Widget.detach(this);
            }
            // Dispose of the widget layout.
            if (this._layout) {
                this._layout.dispose();
                this._layout = null;
            }
            // Dispose the title
            this.title.dispose();
            // Clear the extra data associated with the widget.
            signaling.Signal.clearData(this);
            messaging.MessageLoop.clearData(this);
            properties.AttachedProperty.clearData(this);
        }
        /**
         * A signal emitted when the widget is disposed.
         */
        get disposed() {
            return this._disposed;
        }
        /**
         * Test whether the widget has been disposed.
         */
        get isDisposed() {
            return this.testFlag(Widget.Flag.IsDisposed);
        }
        /**
         * Test whether the widget's node is attached to the DOM.
         */
        get isAttached() {
            return this.testFlag(Widget.Flag.IsAttached);
        }
        /**
         * Test whether the widget is explicitly hidden.
         *
         * #### Notes
         * You should prefer `!{@link isVisible}` over `{@link isHidden}` if you want to know if the
         * widget is hidden as this does not test if the widget is hidden because one of its ancestors is hidden.
         */
        get isHidden() {
            return this.testFlag(Widget.Flag.IsHidden);
        }
        /**
         * Test whether the widget is visible.
         *
         * #### Notes
         * A widget is visible when it is attached to the DOM, is not
         * explicitly hidden, and has no explicitly hidden ancestors.
         *
         * Since 2.7.0, this does not rely on the {@link Widget.Flag.IsVisible} flag.
         * It recursively checks the visibility of all parent widgets.
         */
        get isVisible() {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            let parent = this;
            do {
                if (parent.isHidden || !parent.isAttached) {
                    return false;
                }
                parent = parent.parent;
            } while (parent != null);
            return true;
        }
        /**
         * The title object for the widget.
         *
         * #### Notes
         * The title object is used by some container widgets when displaying
         * the widget alongside some title, such as a tab panel or side bar.
         *
         * Since not all widgets will use the title, it is created on demand.
         *
         * The `owner` property of the title is set to this widget.
         */
        get title() {
            return Private$j.titleProperty.get(this);
        }
        /**
         * Get the id of the widget's DOM node.
         */
        get id() {
            return this.node.id;
        }
        /**
         * Set the id of the widget's DOM node.
         */
        set id(value) {
            this.node.id = value;
        }
        /**
         * The dataset for the widget's DOM node.
         */
        get dataset() {
            return this.node.dataset;
        }
        /**
         * Get the method for hiding the widget.
         */
        get hiddenMode() {
            return this._hiddenMode;
        }
        /**
         * Set the method for hiding the widget.
         */
        set hiddenMode(value) {
            if (this._hiddenMode === value) {
                return;
            }
            if (this.isHidden) {
                // Reset styles set by previous mode.
                this._toggleHidden(false);
            }
            if (value == Widget.HiddenMode.Scale) {
                this.node.style.willChange = 'transform';
            }
            else {
                this.node.style.willChange = 'auto';
            }
            this._hiddenMode = value;
            if (this.isHidden) {
                // Set styles for new mode.
                this._toggleHidden(true);
            }
        }
        /**
         * Get the parent of the widget.
         */
        get parent() {
            return this._parent;
        }
        /**
         * Set the parent of the widget.
         *
         * #### Notes
         * Children are typically added to a widget by using a layout, which
         * means user code will not normally set the parent widget directly.
         *
         * The widget will be automatically removed from its old parent.
         *
         * This is a no-op if there is no effective parent change.
         */
        set parent(value) {
            if (this._parent === value) {
                return;
            }
            if (value && this.contains(value)) {
                throw new Error('Invalid parent widget.');
            }
            if (this._parent && !this._parent.isDisposed) {
                let msg = new Widget.ChildMessage('child-removed', this);
                messaging.MessageLoop.sendMessage(this._parent, msg);
            }
            this._parent = value;
            if (this._parent && !this._parent.isDisposed) {
                let msg = new Widget.ChildMessage('child-added', this);
                messaging.MessageLoop.sendMessage(this._parent, msg);
            }
            if (!this.isDisposed) {
                messaging.MessageLoop.sendMessage(this, Widget.Msg.ParentChanged);
            }
        }
        /**
         * Get the layout for the widget.
         */
        get layout() {
            return this._layout;
        }
        /**
         * Set the layout for the widget.
         *
         * #### Notes
         * The layout is single-use only. It cannot be changed after the
         * first assignment.
         *
         * The layout is disposed automatically when the widget is disposed.
         */
        set layout(value) {
            if (this._layout === value) {
                return;
            }
            if (this.testFlag(Widget.Flag.DisallowLayout)) {
                throw new Error('Cannot set widget layout.');
            }
            if (this._layout) {
                throw new Error('Cannot change widget layout.');
            }
            if (value.parent) {
                throw new Error('Cannot change layout parent.');
            }
            this._layout = value;
            value.parent = this;
        }
        /**
         * Create an iterator over the widget's children.
         *
         * @returns A new iterator over the children of the widget.
         *
         * #### Notes
         * The widget must have a populated layout in order to have children.
         *
         * If a layout is not installed, the returned iterator will be empty.
         */
        *children() {
            if (this._layout) {
                yield* this._layout;
            }
        }
        /**
         * Test whether a widget is a descendant of this widget.
         *
         * @param widget - The descendant widget of interest.
         *
         * @returns `true` if the widget is a descendant, `false` otherwise.
         */
        contains(widget) {
            for (let value = widget; value; value = value._parent) {
                if (value === this) {
                    return true;
                }
            }
            return false;
        }
        /**
         * Test whether the widget's DOM node has the given class name.
         *
         * @param name - The class name of interest.
         *
         * @returns `true` if the node has the class, `false` otherwise.
         */
        hasClass(name) {
            return this.node.classList.contains(name);
        }
        /**
         * Add a class name to the widget's DOM node.
         *
         * @param name - The class name to add to the node.
         *
         * #### Notes
         * If the class name is already added to the node, this is a no-op.
         *
         * The class name must not contain whitespace.
         */
        addClass(name) {
            this.node.classList.add(name);
        }
        /**
         * Remove a class name from the widget's DOM node.
         *
         * @param name - The class name to remove from the node.
         *
         * #### Notes
         * If the class name is not yet added to the node, this is a no-op.
         *
         * The class name must not contain whitespace.
         */
        removeClass(name) {
            this.node.classList.remove(name);
        }
        /**
         * Toggle a class name on the widget's DOM node.
         *
         * @param name - The class name to toggle on the node.
         *
         * @param force - Whether to force add the class (`true`) or force
         *   remove the class (`false`). If not provided, the presence of
         *   the class will be toggled from its current state.
         *
         * @returns `true` if the class is now present, `false` otherwise.
         *
         * #### Notes
         * The class name must not contain whitespace.
         */
        toggleClass(name, force) {
            if (force === true) {
                this.node.classList.add(name);
                return true;
            }
            if (force === false) {
                this.node.classList.remove(name);
                return false;
            }
            return this.node.classList.toggle(name);
        }
        /**
         * Post an `'update-request'` message to the widget.
         *
         * #### Notes
         * This is a simple convenience method for posting the message.
         */
        update() {
            messaging.MessageLoop.postMessage(this, Widget.Msg.UpdateRequest);
        }
        /**
         * Post a `'fit-request'` message to the widget.
         *
         * #### Notes
         * This is a simple convenience method for posting the message.
         */
        fit() {
            messaging.MessageLoop.postMessage(this, Widget.Msg.FitRequest);
        }
        /**
         * Post an `'activate-request'` message to the widget.
         *
         * #### Notes
         * This is a simple convenience method for posting the message.
         */
        activate() {
            messaging.MessageLoop.postMessage(this, Widget.Msg.ActivateRequest);
        }
        /**
         * Send a `'close-request'` message to the widget.
         *
         * #### Notes
         * This is a simple convenience method for sending the message.
         */
        close() {
            messaging.MessageLoop.sendMessage(this, Widget.Msg.CloseRequest);
        }
        /**
         * Show the widget and make it visible to its parent widget.
         *
         * #### Notes
         * This causes the {@link isHidden} property to be `false`.
         *
         * If the widget is not explicitly hidden, this is a no-op.
         */
        show() {
            if (!this.testFlag(Widget.Flag.IsHidden)) {
                return;
            }
            if (this.isAttached && (!this.parent || this.parent.isVisible)) {
                messaging.MessageLoop.sendMessage(this, Widget.Msg.BeforeShow);
            }
            this.clearFlag(Widget.Flag.IsHidden);
            this._toggleHidden(false);
            if (this.isAttached && (!this.parent || this.parent.isVisible)) {
                messaging.MessageLoop.sendMessage(this, Widget.Msg.AfterShow);
            }
            if (this.parent) {
                let msg = new Widget.ChildMessage('child-shown', this);
                messaging.MessageLoop.sendMessage(this.parent, msg);
            }
        }
        /**
         * Hide the widget and make it hidden to its parent widget.
         *
         * #### Notes
         * This causes the {@link isHidden} property to be `true`.
         *
         * If the widget is explicitly hidden, this is a no-op.
         */
        hide() {
            if (this.testFlag(Widget.Flag.IsHidden)) {
                return;
            }
            if (this.isAttached && (!this.parent || this.parent.isVisible)) {
                messaging.MessageLoop.sendMessage(this, Widget.Msg.BeforeHide);
            }
            this.setFlag(Widget.Flag.IsHidden);
            this._toggleHidden(true);
            if (this.isAttached && (!this.parent || this.parent.isVisible)) {
                messaging.MessageLoop.sendMessage(this, Widget.Msg.AfterHide);
            }
            if (this.parent) {
                let msg = new Widget.ChildMessage('child-hidden', this);
                messaging.MessageLoop.sendMessage(this.parent, msg);
            }
        }
        /**
         * Show or hide the widget according to a boolean value.
         *
         * @param hidden - `true` to hide the widget, or `false` to show it.
         *
         * #### Notes
         * This is a convenience method for `hide()` and `show()`.
         */
        setHidden(hidden) {
            if (hidden) {
                this.hide();
            }
            else {
                this.show();
            }
        }
        /**
         * Test whether the given widget flag is set.
         *
         * #### Notes
         * This will not typically be called directly by user code.
         *
         * Since 2.7.0, {@link Widget.Flag.IsVisible} is deprecated.
         * It will be removed in a future version.
         */
        testFlag(flag) {
            return (this._flags & flag) !== 0;
        }
        /**
         * Set the given widget flag.
         *
         * #### Notes
         * This will not typically be called directly by user code.
         *
         * Since 2.7.0, {@link Widget.Flag.IsVisible} is deprecated.
         * It will be removed in a future version.
         */
        setFlag(flag) {
            this._flags |= flag;
        }
        /**
         * Clear the given widget flag.
         *
         * #### Notes
         * This will not typically be called directly by user code.
         *
         * Since 2.7.0, {@link Widget.Flag.IsVisible} is deprecated.
         * It will be removed in a future version.
         */
        clearFlag(flag) {
            this._flags &= ~flag;
        }
        /**
         * Process a message sent to the widget.
         *
         * @param msg - The message sent to the widget.
         *
         * #### Notes
         * Subclasses may reimplement this method as needed.
         */
        processMessage(msg) {
            switch (msg.type) {
                case 'resize':
                    this.notifyLayout(msg);
                    this.onResize(msg);
                    break;
                case 'update-request':
                    this.notifyLayout(msg);
                    this.onUpdateRequest(msg);
                    break;
                case 'fit-request':
                    this.notifyLayout(msg);
                    this.onFitRequest(msg);
                    break;
                case 'before-show':
                    this.notifyLayout(msg);
                    this.onBeforeShow(msg);
                    break;
                case 'after-show':
                    this.setFlag(Widget.Flag.IsVisible);
                    this.notifyLayout(msg);
                    this.onAfterShow(msg);
                    break;
                case 'before-hide':
                    this.notifyLayout(msg);
                    this.onBeforeHide(msg);
                    break;
                case 'after-hide':
                    this.clearFlag(Widget.Flag.IsVisible);
                    this.notifyLayout(msg);
                    this.onAfterHide(msg);
                    break;
                case 'before-attach':
                    this.notifyLayout(msg);
                    this.onBeforeAttach(msg);
                    break;
                case 'after-attach':
                    if (!this.isHidden && (!this.parent || this.parent.isVisible)) {
                        this.setFlag(Widget.Flag.IsVisible);
                    }
                    this.setFlag(Widget.Flag.IsAttached);
                    this.notifyLayout(msg);
                    this.onAfterAttach(msg);
                    break;
                case 'before-detach':
                    this.notifyLayout(msg);
                    this.onBeforeDetach(msg);
                    break;
                case 'after-detach':
                    this.clearFlag(Widget.Flag.IsVisible);
                    this.clearFlag(Widget.Flag.IsAttached);
                    this.notifyLayout(msg);
                    this.onAfterDetach(msg);
                    break;
                case 'activate-request':
                    this.notifyLayout(msg);
                    this.onActivateRequest(msg);
                    break;
                case 'close-request':
                    this.notifyLayout(msg);
                    this.onCloseRequest(msg);
                    break;
                case 'child-added':
                    this.notifyLayout(msg);
                    this.onChildAdded(msg);
                    break;
                case 'child-removed':
                    this.notifyLayout(msg);
                    this.onChildRemoved(msg);
                    break;
                default:
                    this.notifyLayout(msg);
                    break;
            }
        }
        /**
         * Invoke the message processing routine of the widget's layout.
         *
         * @param msg - The message to dispatch to the layout.
         *
         * #### Notes
         * This is a no-op if the widget does not have a layout.
         *
         * This will not typically be called directly by user code.
         */
        notifyLayout(msg) {
            if (this._layout) {
                this._layout.processParentMessage(msg);
            }
        }
        /**
         * A message handler invoked on a `'close-request'` message.
         *
         * #### Notes
         * The default implementation unparents or detaches the widget.
         */
        onCloseRequest(msg) {
            if (this.parent) {
                this.parent = null;
            }
            else if (this.isAttached) {
                Widget.detach(this);
            }
        }
        /**
         * A message handler invoked on a `'resize'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onResize(msg) { }
        /**
         * A message handler invoked on an `'update-request'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onUpdateRequest(msg) { }
        /**
         * A message handler invoked on a `'fit-request'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onFitRequest(msg) { }
        /**
         * A message handler invoked on an `'activate-request'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onActivateRequest(msg) { }
        /**
         * A message handler invoked on a `'before-show'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onBeforeShow(msg) { }
        /**
         * A message handler invoked on an `'after-show'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onAfterShow(msg) { }
        /**
         * A message handler invoked on a `'before-hide'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onBeforeHide(msg) { }
        /**
         * A message handler invoked on an `'after-hide'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onAfterHide(msg) { }
        /**
         * A message handler invoked on a `'before-attach'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onBeforeAttach(msg) { }
        /**
         * A message handler invoked on an `'after-attach'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onAfterAttach(msg) { }
        /**
         * A message handler invoked on a `'before-detach'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onBeforeDetach(msg) { }
        /**
         * A message handler invoked on an `'after-detach'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onAfterDetach(msg) { }
        /**
         * A message handler invoked on a `'child-added'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onChildAdded(msg) { }
        /**
         * A message handler invoked on a `'child-removed'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onChildRemoved(msg) { }
        _toggleHidden(hidden) {
            if (hidden) {
                switch (this._hiddenMode) {
                    case Widget.HiddenMode.Display:
                        this.addClass('lm-mod-hidden');
                        break;
                    case Widget.HiddenMode.Scale:
                        this.node.style.transform = 'scale(0)';
                        this.node.setAttribute('aria-hidden', 'true');
                        break;
                    case Widget.HiddenMode.ContentVisibility:
                        // @ts-expect-error content-visibility unknown by DOM lib types
                        this.node.style.contentVisibility = 'hidden';
                        this.node.style.zIndex = '-1';
                        // Set opacity to 0 so that the widget's box (e.g. borders or
                        // background) does not shine through despite the content being
                        // skipped by `content-visibility`.
                        this.node.style.opacity = '0';
                        break;
                }
            }
            else {
                switch (this._hiddenMode) {
                    case Widget.HiddenMode.Display:
                        this.removeClass('lm-mod-hidden');
                        break;
                    case Widget.HiddenMode.Scale:
                        this.node.style.transform = '';
                        this.node.removeAttribute('aria-hidden');
                        break;
                    case Widget.HiddenMode.ContentVisibility:
                        // @ts-expect-error content-visibility unknown by DOM lib types
                        this.node.style.contentVisibility = '';
                        this.node.style.zIndex = '';
                        this.node.style.opacity = '';
                        break;
                }
            }
        }
    }
    /**
     * The namespace for the `Widget` class statics.
     */
    (function (Widget) {
        (function (HiddenMode) {
            /**
             * Set a `lm-mod-hidden` CSS class to hide the widget using `display:none`
             * CSS from the standard Lumino CSS.
             */
            HiddenMode[HiddenMode["Display"] = 0] = "Display";
            /**
             * Hide the widget by setting the `transform` to `'scale(0)'`.
             */
            HiddenMode[HiddenMode["Scale"] = 1] = "Scale";
            /**
             *Hide the widget by setting the `content-visibility` to `'hidden'`.
             */
            HiddenMode[HiddenMode["ContentVisibility"] = 2] = "ContentVisibility";
        })(Widget.HiddenMode || (Widget.HiddenMode = {}));
        (function (Flag) {
            /**
             * The widget has been disposed.
             */
            Flag[Flag["IsDisposed"] = 1] = "IsDisposed";
            /**
             * The widget is attached to the DOM.
             */
            Flag[Flag["IsAttached"] = 2] = "IsAttached";
            /**
             * The widget is hidden.
             */
            Flag[Flag["IsHidden"] = 4] = "IsHidden";
            /**
             * The widget is visible.
             *
             * @deprecated since 2.7.0, apply that flag consistently was not reliable
             * so it was dropped in favor of a recursive check of the visibility of all parents.
             */
            Flag[Flag["IsVisible"] = 8] = "IsVisible";
            /**
             * A layout cannot be set on the widget.
             */
            Flag[Flag["DisallowLayout"] = 16] = "DisallowLayout";
        })(Widget.Flag || (Widget.Flag = {}));
        (function (Msg) {
            /**
             * A singleton `'before-show'` message.
             *
             * #### Notes
             * This message is sent to a widget before it becomes visible.
             *
             * This message is **not** sent when the widget is being attached.
             */
            Msg.BeforeShow = new messaging.Message('before-show');
            /**
             * A singleton `'after-show'` message.
             *
             * #### Notes
             * This message is sent to a widget after it becomes visible.
             *
             * This message is **not** sent when the widget is being attached.
             */
            Msg.AfterShow = new messaging.Message('after-show');
            /**
             * A singleton `'before-hide'` message.
             *
             * #### Notes
             * This message is sent to a widget before it becomes not-visible.
             *
             * This message is **not** sent when the widget is being detached.
             */
            Msg.BeforeHide = new messaging.Message('before-hide');
            /**
             * A singleton `'after-hide'` message.
             *
             * #### Notes
             * This message is sent to a widget after it becomes not-visible.
             *
             * This message is **not** sent when the widget is being detached.
             */
            Msg.AfterHide = new messaging.Message('after-hide');
            /**
             * A singleton `'before-attach'` message.
             *
             * #### Notes
             * This message is sent to a widget before it is attached.
             */
            Msg.BeforeAttach = new messaging.Message('before-attach');
            /**
             * A singleton `'after-attach'` message.
             *
             * #### Notes
             * This message is sent to a widget after it is attached.
             */
            Msg.AfterAttach = new messaging.Message('after-attach');
            /**
             * A singleton `'before-detach'` message.
             *
             * #### Notes
             * This message is sent to a widget before it is detached.
             */
            Msg.BeforeDetach = new messaging.Message('before-detach');
            /**
             * A singleton `'after-detach'` message.
             *
             * #### Notes
             * This message is sent to a widget after it is detached.
             */
            Msg.AfterDetach = new messaging.Message('after-detach');
            /**
             * A singleton `'parent-changed'` message.
             *
             * #### Notes
             * This message is sent to a widget when its parent has changed.
             */
            Msg.ParentChanged = new messaging.Message('parent-changed');
            /**
             * A singleton conflatable `'update-request'` message.
             *
             * #### Notes
             * This message can be dispatched to supporting widgets in order to
             * update their content based on the current widget state. Not all
             * widgets will respond to messages of this type.
             *
             * For widgets with a layout, this message will inform the layout to
             * update the position and size of its child widgets.
             */
            Msg.UpdateRequest = new messaging.ConflatableMessage('update-request');
            /**
             * A singleton conflatable `'fit-request'` message.
             *
             * #### Notes
             * For widgets with a layout, this message will inform the layout to
             * recalculate its size constraints to fit the space requirements of
             * its child widgets, and to update their position and size. Not all
             * layouts will respond to messages of this type.
             */
            Msg.FitRequest = new messaging.ConflatableMessage('fit-request');
            /**
             * A singleton conflatable `'activate-request'` message.
             *
             * #### Notes
             * This message should be dispatched to a widget when it should
             * perform the actions necessary to activate the widget, which
             * may include focusing its node or descendant node.
             */
            Msg.ActivateRequest = new messaging.ConflatableMessage('activate-request');
            /**
             * A singleton conflatable `'close-request'` message.
             *
             * #### Notes
             * This message should be dispatched to a widget when it should close
             * and remove itself from the widget hierarchy.
             */
            Msg.CloseRequest = new messaging.ConflatableMessage('close-request');
        })(Widget.Msg || (Widget.Msg = {}));
        /**
         * A message class for child related messages.
         */
        class ChildMessage extends messaging.Message {
            /**
             * Construct a new child message.
             *
             * @param type - The message type.
             *
             * @param child - The child widget for the message.
             */
            constructor(type, child) {
                super(type);
                this.child = child;
            }
        }
        Widget.ChildMessage = ChildMessage;
        /**
         * A message class for `'resize'` messages.
         */
        class ResizeMessage extends messaging.Message {
            /**
             * Construct a new resize message.
             *
             * @param width - The **offset width** of the widget, or `-1` if
             *   the width is not known.
             *
             * @param height - The **offset height** of the widget, or `-1` if
             *   the height is not known.
             */
            constructor(width, height) {
                super('resize');
                this.width = width;
                this.height = height;
            }
        }
        Widget.ResizeMessage = ResizeMessage;
        /**
         * The namespace for the `ResizeMessage` class statics.
         */
        (function (ResizeMessage) {
            /**
             * A singleton `'resize'` message with an unknown size.
             */
            ResizeMessage.UnknownSize = new ResizeMessage(-1, -1);
        })(ResizeMessage = Widget.ResizeMessage || (Widget.ResizeMessage = {}));
        /**
         * Attach a widget to a host DOM node.
         *
         * @param widget - The widget of interest.
         *
         * @param host - The DOM node to use as the widget's host.
         *
         * @param ref - The child of `host` to use as the reference element.
         *   If this is provided, the widget will be inserted before this
         *   node in the host. The default is `null`, which will cause the
         *   widget to be added as the last child of the host.
         *
         * #### Notes
         * This will throw an error if the widget is not a root widget, if
         * the widget is already attached, or if the host is not attached
         * to the DOM.
         */
        function attach(widget, host, ref = null) {
            if (widget.parent) {
                throw new Error('Cannot attach a child widget.');
            }
            if (widget.isAttached || widget.node.isConnected) {
                throw new Error('Widget is already attached.');
            }
            if (!host.isConnected) {
                throw new Error('Host is not attached.');
            }
            messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            host.insertBefore(widget.node, ref);
            messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
        }
        Widget.attach = attach;
        /**
         * Detach the widget from its host DOM node.
         *
         * @param widget - The widget of interest.
         *
         * #### Notes
         * This will throw an error if the widget is not a root widget,
         * or if the widget is not attached to the DOM.
         */
        function detach(widget) {
            if (widget.parent) {
                throw new Error('Cannot detach a child widget.');
            }
            if (!widget.isAttached || !widget.node.isConnected) {
                throw new Error('Widget is not attached.');
            }
            messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            widget.node.parentNode.removeChild(widget.node);
            messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
        }
        Widget.detach = detach;
    })(Widget || (Widget = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$j;
    (function (Private) {
        /**
         * An attached property for the widget title object.
         */
        Private.titleProperty = new properties.AttachedProperty({
            name: 'title',
            create: owner => new Title({ owner })
        });
        /**
         * Create a DOM node for the given widget options.
         */
        function createNode(options) {
            return options.node || document.createElement(options.tag || 'div');
        }
        Private.createNode = createNode;
    })(Private$j || (Private$j = {}));

    /**
     * An abstract base class for creating lumino layouts.
     *
     * #### Notes
     * A layout is used to add widgets to a parent and to arrange those
     * widgets within the parent's DOM node.
     *
     * This class implements the base functionality which is required of
     * nearly all layouts. It must be subclassed in order to be useful.
     *
     * Notably, this class does not define a uniform interface for adding
     * widgets to the layout. A subclass should define that API in a way
     * which is meaningful for its intended use.
     */
    class Layout {
        /**
         * Construct a new layout.
         *
         * @param options - The options for initializing the layout.
         */
        constructor(options = {}) {
            this._disposed = false;
            this._parent = null;
            this._fitPolicy = options.fitPolicy || 'set-min-size';
        }
        /**
         * Dispose of the resources held by the layout.
         *
         * #### Notes
         * This should be reimplemented to clear and dispose of the widgets.
         *
         * All reimplementations should call the superclass method.
         *
         * This method is called automatically when the parent is disposed.
         */
        dispose() {
            this._parent = null;
            this._disposed = true;
            signaling.Signal.clearData(this);
            properties.AttachedProperty.clearData(this);
        }
        /**
         * Test whether the layout is disposed.
         */
        get isDisposed() {
            return this._disposed;
        }
        /**
         * Get the parent widget of the layout.
         */
        get parent() {
            return this._parent;
        }
        /**
         * Set the parent widget of the layout.
         *
         * #### Notes
         * This is set automatically when installing the layout on the parent
         * widget. The parent widget should not be set directly by user code.
         */
        set parent(value) {
            if (this._parent === value) {
                return;
            }
            if (this._parent) {
                throw new Error('Cannot change parent widget.');
            }
            if (value.layout !== this) {
                throw new Error('Invalid parent widget.');
            }
            this._parent = value;
            this.init();
        }
        /**
         * Get the fit policy for the layout.
         *
         * #### Notes
         * The fit policy controls the computed size constraints which are
         * applied to the parent widget by the layout.
         *
         * Some layout implementations may ignore the fit policy.
         */
        get fitPolicy() {
            return this._fitPolicy;
        }
        /**
         * Set the fit policy for the layout.
         *
         * #### Notes
         * The fit policy controls the computed size constraints which are
         * applied to the parent widget by the layout.
         *
         * Some layout implementations may ignore the fit policy.
         *
         * Changing the fit policy will clear the current size constraint
         * for the parent widget and then re-fit the parent.
         */
        set fitPolicy(value) {
            // Bail if the policy does not change
            if (this._fitPolicy === value) {
                return;
            }
            // Update the internal policy.
            this._fitPolicy = value;
            // Clear the size constraints and schedule a fit of the parent.
            if (this._parent) {
                let style = this._parent.node.style;
                style.minWidth = '';
                style.minHeight = '';
                style.maxWidth = '';
                style.maxHeight = '';
                this._parent.fit();
            }
        }
        /**
         * Process a message sent to the parent widget.
         *
         * @param msg - The message sent to the parent widget.
         *
         * #### Notes
         * This method is called by the parent widget to process a message.
         *
         * Subclasses may reimplement this method as needed.
         */
        processParentMessage(msg) {
            switch (msg.type) {
                case 'resize':
                    this.onResize(msg);
                    break;
                case 'update-request':
                    this.onUpdateRequest(msg);
                    break;
                case 'fit-request':
                    this.onFitRequest(msg);
                    break;
                case 'before-show':
                    this.onBeforeShow(msg);
                    break;
                case 'after-show':
                    this.onAfterShow(msg);
                    break;
                case 'before-hide':
                    this.onBeforeHide(msg);
                    break;
                case 'after-hide':
                    this.onAfterHide(msg);
                    break;
                case 'before-attach':
                    this.onBeforeAttach(msg);
                    break;
                case 'after-attach':
                    this.onAfterAttach(msg);
                    break;
                case 'before-detach':
                    this.onBeforeDetach(msg);
                    break;
                case 'after-detach':
                    this.onAfterDetach(msg);
                    break;
                case 'child-removed':
                    this.onChildRemoved(msg);
                    break;
                case 'child-shown':
                    this.onChildShown(msg);
                    break;
                case 'child-hidden':
                    this.onChildHidden(msg);
                    break;
            }
        }
        /**
         * Perform layout initialization which requires the parent widget.
         *
         * #### Notes
         * This method is invoked immediately after the layout is installed
         * on the parent widget.
         *
         * The default implementation reparents all of the widgets to the
         * layout parent widget.
         *
         * Subclasses should reimplement this method and attach the child
         * widget nodes to the parent widget's node.
         */
        init() {
            for (const widget of this) {
                widget.parent = this.parent;
            }
        }
        /**
         * A message handler invoked on a `'resize'` message.
         *
         * #### Notes
         * The layout should ensure that its widgets are resized according
         * to the specified layout space, and that they are sent a `'resize'`
         * message if appropriate.
         *
         * The default implementation of this method sends an `UnknownSize`
         * resize message to all widgets.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onResize(msg) {
            for (const widget of this) {
                messaging.MessageLoop.sendMessage(widget, Widget.ResizeMessage.UnknownSize);
            }
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         *
         * #### Notes
         * The layout should ensure that its widgets are resized according
         * to the available layout space, and that they are sent a `'resize'`
         * message if appropriate.
         *
         * The default implementation of this method sends an `UnknownSize`
         * resize message to all widgets.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onUpdateRequest(msg) {
            for (const widget of this) {
                messaging.MessageLoop.sendMessage(widget, Widget.ResizeMessage.UnknownSize);
            }
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         *
         * #### Notes
         * The default implementation of this method forwards the message
         * to all widgets. It assumes all widget nodes are attached to the
         * parent widget node.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onBeforeAttach(msg) {
            for (const widget of this) {
                messaging.MessageLoop.sendMessage(widget, msg);
            }
        }
        /**
         * A message handler invoked on an `'after-attach'` message.
         *
         * #### Notes
         * The default implementation of this method forwards the message
         * to all widgets. It assumes all widget nodes are attached to the
         * parent widget node.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onAfterAttach(msg) {
            for (const widget of this) {
                messaging.MessageLoop.sendMessage(widget, msg);
            }
        }
        /**
         * A message handler invoked on a `'before-detach'` message.
         *
         * #### Notes
         * The default implementation of this method forwards the message
         * to all widgets. It assumes all widget nodes are attached to the
         * parent widget node.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onBeforeDetach(msg) {
            for (const widget of this) {
                messaging.MessageLoop.sendMessage(widget, msg);
            }
        }
        /**
         * A message handler invoked on an `'after-detach'` message.
         *
         * #### Notes
         * The default implementation of this method forwards the message
         * to all widgets. It assumes all widget nodes are attached to the
         * parent widget node.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onAfterDetach(msg) {
            for (const widget of this) {
                messaging.MessageLoop.sendMessage(widget, msg);
            }
        }
        /**
         * A message handler invoked on a `'before-show'` message.
         *
         * #### Notes
         * The default implementation of this method forwards the message to
         * all non-hidden widgets. It assumes all widget nodes are attached
         * to the parent widget node.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onBeforeShow(msg) {
            for (const widget of this) {
                if (!widget.isHidden) {
                    messaging.MessageLoop.sendMessage(widget, msg);
                }
            }
        }
        /**
         * A message handler invoked on an `'after-show'` message.
         *
         * #### Notes
         * The default implementation of this method forwards the message to
         * all non-hidden widgets. It assumes all widget nodes are attached
         * to the parent widget node.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onAfterShow(msg) {
            for (const widget of this) {
                if (!widget.isHidden) {
                    messaging.MessageLoop.sendMessage(widget, msg);
                }
            }
        }
        /**
         * A message handler invoked on a `'before-hide'` message.
         *
         * #### Notes
         * The default implementation of this method forwards the message to
         * all non-hidden widgets. It assumes all widget nodes are attached
         * to the parent widget node.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onBeforeHide(msg) {
            for (const widget of this) {
                if (!widget.isHidden) {
                    messaging.MessageLoop.sendMessage(widget, msg);
                }
            }
        }
        /**
         * A message handler invoked on an `'after-hide'` message.
         *
         * #### Notes
         * The default implementation of this method forwards the message to
         * all non-hidden widgets. It assumes all widget nodes are attached
         * to the parent widget node.
         *
         * This may be reimplemented by subclasses as needed.
         */
        onAfterHide(msg) {
            for (const widget of this) {
                if (!widget.isHidden) {
                    messaging.MessageLoop.sendMessage(widget, msg);
                }
            }
        }
        /**
         * A message handler invoked on a `'child-removed'` message.
         *
         * #### Notes
         * This will remove the child widget from the layout.
         *
         * Subclasses should **not** typically reimplement this method.
         */
        onChildRemoved(msg) {
            this.removeWidget(msg.child);
        }
        /**
         * A message handler invoked on a `'fit-request'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onFitRequest(msg) { }
        /**
         * A message handler invoked on a `'child-shown'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onChildShown(msg) { }
        /**
         * A message handler invoked on a `'child-hidden'` message.
         *
         * #### Notes
         * The default implementation of this handler is a no-op.
         */
        onChildHidden(msg) { }
    }
    /**
     * The namespace for the `Layout` class statics.
     */
    (function (Layout) {
        /**
         * Get the horizontal alignment for a widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The horizontal alignment for the widget.
         *
         * #### Notes
         * If the layout width allocated to a widget is larger than its max
         * width, the horizontal alignment controls how the widget is placed
         * within the extra horizontal space.
         *
         * If the allocated width is less than the widget's max width, the
         * horizontal alignment has no effect.
         *
         * Some layout implementations may ignore horizontal alignment.
         */
        function getHorizontalAlignment(widget) {
            return Private$i.horizontalAlignmentProperty.get(widget);
        }
        Layout.getHorizontalAlignment = getHorizontalAlignment;
        /**
         * Set the horizontal alignment for a widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the horizontal alignment.
         *
         * #### Notes
         * If the layout width allocated to a widget is larger than its max
         * width, the horizontal alignment controls how the widget is placed
         * within the extra horizontal space.
         *
         * If the allocated width is less than the widget's max width, the
         * horizontal alignment has no effect.
         *
         * Some layout implementations may ignore horizontal alignment.
         *
         * Changing the horizontal alignment will post an `update-request`
         * message to widget's parent, provided the parent has a layout
         * installed.
         */
        function setHorizontalAlignment(widget, value) {
            Private$i.horizontalAlignmentProperty.set(widget, value);
        }
        Layout.setHorizontalAlignment = setHorizontalAlignment;
        /**
         * Get the vertical alignment for a widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The vertical alignment for the widget.
         *
         * #### Notes
         * If the layout height allocated to a widget is larger than its max
         * height, the vertical alignment controls how the widget is placed
         * within the extra vertical space.
         *
         * If the allocated height is less than the widget's max height, the
         * vertical alignment has no effect.
         *
         * Some layout implementations may ignore vertical alignment.
         */
        function getVerticalAlignment(widget) {
            return Private$i.verticalAlignmentProperty.get(widget);
        }
        Layout.getVerticalAlignment = getVerticalAlignment;
        /**
         * Set the vertical alignment for a widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the vertical alignment.
         *
         * #### Notes
         * If the layout height allocated to a widget is larger than its max
         * height, the vertical alignment controls how the widget is placed
         * within the extra vertical space.
         *
         * If the allocated height is less than the widget's max height, the
         * vertical alignment has no effect.
         *
         * Some layout implementations may ignore vertical alignment.
         *
         * Changing the horizontal alignment will post an `update-request`
         * message to widget's parent, provided the parent has a layout
         * installed.
         */
        function setVerticalAlignment(widget, value) {
            Private$i.verticalAlignmentProperty.set(widget, value);
        }
        Layout.setVerticalAlignment = setVerticalAlignment;
    })(Layout || (Layout = {}));
    /**
     * An object which assists in the absolute layout of widgets.
     *
     * #### Notes
     * This class is useful when implementing a layout which arranges its
     * widgets using absolute positioning.
     *
     * This class is used by nearly all of the built-in lumino layouts.
     */
    class LayoutItem {
        /**
         * Construct a new layout item.
         *
         * @param widget - The widget to be managed by the item.
         *
         * #### Notes
         * The widget will be set to absolute positioning.
         * The widget will use strict CSS containment.
         */
        constructor(widget) {
            this._top = NaN;
            this._left = NaN;
            this._width = NaN;
            this._height = NaN;
            this._minWidth = 0;
            this._minHeight = 0;
            this._maxWidth = Infinity;
            this._maxHeight = Infinity;
            this._disposed = false;
            this.widget = widget;
            this.widget.node.style.position = 'absolute';
            this.widget.node.style.contain = 'strict';
        }
        /**
         * Dispose of the the layout item.
         *
         * #### Notes
         * This will reset the positioning of the widget.
         */
        dispose() {
            // Do nothing if the item is already disposed.
            if (this._disposed) {
                return;
            }
            // Mark the item as disposed.
            this._disposed = true;
            // Reset the widget style.
            let style = this.widget.node.style;
            style.position = '';
            style.top = '';
            style.left = '';
            style.width = '';
            style.height = '';
            style.contain = '';
        }
        /**
         * The computed minimum width of the widget.
         *
         * #### Notes
         * This value can be updated by calling the `fit` method.
         */
        get minWidth() {
            return this._minWidth;
        }
        /**
         * The computed minimum height of the widget.
         *
         * #### Notes
         * This value can be updated by calling the `fit` method.
         */
        get minHeight() {
            return this._minHeight;
        }
        /**
         * The computed maximum width of the widget.
         *
         * #### Notes
         * This value can be updated by calling the `fit` method.
         */
        get maxWidth() {
            return this._maxWidth;
        }
        /**
         * The computed maximum height of the widget.
         *
         * #### Notes
         * This value can be updated by calling the `fit` method.
         */
        get maxHeight() {
            return this._maxHeight;
        }
        /**
         * Whether the layout item is disposed.
         */
        get isDisposed() {
            return this._disposed;
        }
        /**
         * Whether the managed widget is hidden.
         */
        get isHidden() {
            return this.widget.isHidden;
        }
        /**
         * Whether the managed widget is visible.
         */
        get isVisible() {
            return this.widget.isVisible;
        }
        /**
         * Whether the managed widget is attached.
         */
        get isAttached() {
            return this.widget.isAttached;
        }
        /**
         * Update the computed size limits of the managed widget.
         */
        fit() {
            let limits = domutils.ElementExt.sizeLimits(this.widget.node);
            this._minWidth = limits.minWidth;
            this._minHeight = limits.minHeight;
            this._maxWidth = limits.maxWidth;
            this._maxHeight = limits.maxHeight;
        }
        /**
         * Update the position and size of the managed widget.
         *
         * @param left - The left edge position of the layout box.
         *
         * @param top - The top edge position of the layout box.
         *
         * @param width - The width of the layout box.
         *
         * @param height - The height of the layout box.
         */
        update(left, top, width, height) {
            // Clamp the size to the computed size limits.
            let clampW = Math.max(this._minWidth, Math.min(width, this._maxWidth));
            let clampH = Math.max(this._minHeight, Math.min(height, this._maxHeight));
            // Adjust the left edge for the horizontal alignment, if needed.
            if (clampW < width) {
                switch (Layout.getHorizontalAlignment(this.widget)) {
                    case 'left':
                        break;
                    case 'center':
                        left += (width - clampW) / 2;
                        break;
                    case 'right':
                        left += width - clampW;
                        break;
                    default:
                        throw 'unreachable';
                }
            }
            // Adjust the top edge for the vertical alignment, if needed.
            if (clampH < height) {
                switch (Layout.getVerticalAlignment(this.widget)) {
                    case 'top':
                        break;
                    case 'center':
                        top += (height - clampH) / 2;
                        break;
                    case 'bottom':
                        top += height - clampH;
                        break;
                    default:
                        throw 'unreachable';
                }
            }
            // Set up the resize variables.
            let resized = false;
            let style = this.widget.node.style;
            // Update the top edge of the widget if needed.
            if (this._top !== top) {
                this._top = top;
                style.top = `${top}px`;
            }
            // Update the left edge of the widget if needed.
            if (this._left !== left) {
                this._left = left;
                style.left = `${left}px`;
            }
            // Update the width of the widget if needed.
            if (this._width !== clampW) {
                resized = true;
                this._width = clampW;
                style.width = `${clampW}px`;
            }
            // Update the height of the widget if needed.
            if (this._height !== clampH) {
                resized = true;
                this._height = clampH;
                style.height = `${clampH}px`;
            }
            // Send a resize message to the widget if needed.
            if (resized) {
                let msg = new Widget.ResizeMessage(clampW, clampH);
                messaging.MessageLoop.sendMessage(this.widget, msg);
            }
        }
    }
    /**
     * The namespace for the module implementation details.
     */
    var Private$i;
    (function (Private) {
        /**
         * The attached property for a widget horizontal alignment.
         */
        Private.horizontalAlignmentProperty = new properties.AttachedProperty({
            name: 'horizontalAlignment',
            create: () => 'center',
            changed: onAlignmentChanged
        });
        /**
         * The attached property for a widget vertical alignment.
         */
        Private.verticalAlignmentProperty = new properties.AttachedProperty({
            name: 'verticalAlignment',
            create: () => 'top',
            changed: onAlignmentChanged
        });
        /**
         * The change handler for the attached alignment properties.
         */
        function onAlignmentChanged(child) {
            if (child.parent && child.parent.layout) {
                child.parent.update();
            }
        }
    })(Private$i || (Private$i = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A concrete layout implementation suitable for many use cases.
     *
     * #### Notes
     * This class is suitable as a base class for implementing a variety of
     * layouts, but can also be used directly with standard CSS to layout a
     * collection of widgets.
     */
    class PanelLayout extends Layout {
        constructor() {
            super(...arguments);
            this._widgets = [];
        }
        /**
         * Dispose of the resources held by the layout.
         *
         * #### Notes
         * This will clear and dispose all widgets in the layout.
         *
         * All reimplementations should call the superclass method.
         *
         * This method is called automatically when the parent is disposed.
         */
        dispose() {
            while (this._widgets.length > 0) {
                this._widgets.pop().dispose();
            }
            super.dispose();
        }
        /**
         * A read-only array of the widgets in the layout.
         */
        get widgets() {
            return this._widgets;
        }
        /**
         * Create an iterator over the widgets in the layout.
         *
         * @returns A new iterator over the widgets in the layout.
         */
        *[Symbol.iterator]() {
            yield* this._widgets;
        }
        /**
         * Add a widget to the end of the layout.
         *
         * @param widget - The widget to add to the layout.
         *
         * #### Notes
         * If the widget is already contained in the layout, it will be moved.
         */
        addWidget(widget) {
            this.insertWidget(this._widgets.length, widget);
        }
        /**
         * Insert a widget into the layout at the specified index.
         *
         * @param index - The index at which to insert the widget.
         *
         * @param widget - The widget to insert into the layout.
         *
         * #### Notes
         * The index will be clamped to the bounds of the widgets.
         *
         * If the widget is already added to the layout, it will be moved.
         *
         * #### Undefined Behavior
         * An `index` which is non-integral.
         */
        insertWidget(index, widget) {
            // Remove the widget from its current parent. This is a no-op
            // if the widget's parent is already the layout parent widget.
            widget.parent = this.parent;
            // Look up the current index of the widget.
            let i = this._widgets.indexOf(widget);
            // Clamp the insert index to the array bounds.
            let j = Math.max(0, Math.min(index, this._widgets.length));
            // If the widget is not in the array, insert it.
            if (i === -1) {
                // Insert the widget into the array.
                algorithm.ArrayExt.insert(this._widgets, j, widget);
                // If the layout is parented, attach the widget to the DOM.
                if (this.parent) {
                    this.attachWidget(j, widget);
                }
                // There is nothing more to do.
                return;
            }
            // Otherwise, the widget exists in the array and should be moved.
            // Adjust the index if the location is at the end of the array.
            if (j === this._widgets.length) {
                j--;
            }
            // Bail if there is no effective move.
            if (i === j) {
                return;
            }
            // Move the widget to the new location.
            algorithm.ArrayExt.move(this._widgets, i, j);
            // If the layout is parented, move the widget in the DOM.
            if (this.parent) {
                this.moveWidget(i, j, widget);
            }
        }
        /**
         * Remove a widget from the layout.
         *
         * @param widget - The widget to remove from the layout.
         *
         * #### Notes
         * A widget is automatically removed from the layout when its `parent`
         * is set to `null`. This method should only be invoked directly when
         * removing a widget from a layout which has yet to be installed on a
         * parent widget.
         *
         * This method does *not* modify the widget's `parent`.
         */
        removeWidget(widget) {
            this.removeWidgetAt(this._widgets.indexOf(widget));
        }
        /**
         * Remove the widget at a given index from the layout.
         *
         * @param index - The index of the widget to remove.
         *
         * #### Notes
         * A widget is automatically removed from the layout when its `parent`
         * is set to `null`. This method should only be invoked directly when
         * removing a widget from a layout which has yet to be installed on a
         * parent widget.
         *
         * This method does *not* modify the widget's `parent`.
         *
         * #### Undefined Behavior
         * An `index` which is non-integral.
         */
        removeWidgetAt(index) {
            // Remove the widget from the array.
            let widget = algorithm.ArrayExt.removeAt(this._widgets, index);
            // If the layout is parented, detach the widget from the DOM.
            if (widget && this.parent) {
                this.detachWidget(index, widget);
            }
        }
        /**
         * Perform layout initialization which requires the parent widget.
         */
        init() {
            super.init();
            let index = 0;
            for (const widget of this) {
                this.attachWidget(index++, widget);
            }
        }
        /**
         * Attach a widget to the parent's DOM node.
         *
         * @param index - The current index of the widget in the layout.
         *
         * @param widget - The widget to attach to the parent.
         *
         * #### Notes
         * This method is called automatically by the panel layout at the
         * appropriate time. It should not be called directly by user code.
         *
         * The default implementation adds the widgets's node to the parent's
         * node at the proper location, and sends the appropriate attach
         * messages to the widget if the parent is attached to the DOM.
         *
         * Subclasses may reimplement this method to control how the widget's
         * node is added to the parent's node.
         */
        attachWidget(index, widget) {
            // Look up the next sibling reference node.
            let ref = this.parent.node.children[index];
            // Send a `'before-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            }
            // Insert the widget's node before the sibling.
            this.parent.node.insertBefore(widget.node, ref);
            // Send an `'after-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
            }
        }
        /**
         * Move a widget in the parent's DOM node.
         *
         * @param fromIndex - The previous index of the widget in the layout.
         *
         * @param toIndex - The current index of the widget in the layout.
         *
         * @param widget - The widget to move in the parent.
         *
         * #### Notes
         * This method is called automatically by the panel layout at the
         * appropriate time. It should not be called directly by user code.
         *
         * The default implementation moves the widget's node to the proper
         * location in the parent's node and sends the appropriate attach and
         * detach messages to the widget if the parent is attached to the DOM.
         *
         * Subclasses may reimplement this method to control how the widget's
         * node is moved in the parent's node.
         */
        moveWidget(fromIndex, toIndex, widget) {
            // Send a `'before-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            }
            // Remove the widget's node from the parent.
            this.parent.node.removeChild(widget.node);
            // Send an `'after-detach'` and  message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
            }
            // Look up the next sibling reference node.
            let ref = this.parent.node.children[toIndex];
            // Send a `'before-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            }
            // Insert the widget's node before the sibling.
            this.parent.node.insertBefore(widget.node, ref);
            // Send an `'after-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
            }
        }
        /**
         * Detach a widget from the parent's DOM node.
         *
         * @param index - The previous index of the widget in the layout.
         *
         * @param widget - The widget to detach from the parent.
         *
         * #### Notes
         * This method is called automatically by the panel layout at the
         * appropriate time. It should not be called directly by user code.
         *
         * The default implementation removes the widget's node from the
         * parent's node, and sends the appropriate detach messages to the
         * widget if the parent is attached to the DOM.
         *
         * Subclasses may reimplement this method to control how the widget's
         * node is removed from the parent's node.
         */
        detachWidget(index, widget) {
            // Send a `'before-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            }
            // Remove the widget's node from the parent.
            this.parent.node.removeChild(widget.node);
            // Send an `'after-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
            }
        }
    }

    /*
     * Copyright (c) Jupyter Development Team.
     * Distributed under the terms of the Modified BSD License.
     */
    var Utils;
    (function (Utils) {
        /**
         * Clamp a dimension value to an integer >= 0.
         */
        function clampDimension(value) {
            return Math.max(0, Math.floor(value));
        }
        Utils.clampDimension = clampDimension;
        /**
         * Whether a pointer event came from touch input.
         */
        function isTouchEvent(event) {
            return 'pointerType' in event && event.pointerType === 'touch';
        }
        Utils.isTouchEvent = isTouchEvent;
    })(Utils || (Utils = {}));
    var Utils$1 = Utils;

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A layout which arranges its widgets into resizable sections.
     */
    class SplitLayout extends PanelLayout {
        /**
         * Construct a new split layout.
         *
         * @param options - The options for initializing the layout.
         */
        constructor(options) {
            super();
            this.widgetOffset = 0;
            this._fixed = 0;
            this._spacing = 4;
            this._dirty = false;
            this._hasNormedSizes = false;
            this._sizers = [];
            this._items = [];
            this._handles = [];
            this._box = null;
            this._alignment = 'start';
            this._orientation = 'horizontal';
            this.renderer = options.renderer;
            if (options.orientation !== undefined) {
                this._orientation = options.orientation;
            }
            if (options.alignment !== undefined) {
                this._alignment = options.alignment;
            }
            if (options.spacing !== undefined) {
                this._spacing = Utils.clampDimension(options.spacing);
            }
        }
        /**
         * Dispose of the resources held by the layout.
         */
        dispose() {
            // Dispose of the layout items.
            for (const item of this._items) {
                item.dispose();
            }
            // Clear the layout state.
            this._box = null;
            this._items.length = 0;
            this._sizers.length = 0;
            this._handles.length = 0;
            // Dispose of the rest of the layout.
            super.dispose();
        }
        /**
         * Get the layout orientation for the split layout.
         */
        get orientation() {
            return this._orientation;
        }
        /**
         * Set the layout orientation for the split layout.
         */
        set orientation(value) {
            if (this._orientation === value) {
                return;
            }
            this._orientation = value;
            if (!this.parent) {
                return;
            }
            this.parent.dataset['orientation'] = value;
            this.parent.fit();
        }
        /**
         * Get the content alignment for the split layout.
         *
         * #### Notes
         * This is the alignment of the widgets in the layout direction.
         *
         * The alignment has no effect if the widgets can expand  to fill the
         * entire split layout.
         */
        get alignment() {
            return this._alignment;
        }
        /**
         * Set the content alignment for the split layout.
         *
         * #### Notes
         * This is the alignment of the widgets in the layout direction.
         *
         * The alignment has no effect if the widgets can expand  to fill the
         * entire split layout.
         */
        set alignment(value) {
            if (this._alignment === value) {
                return;
            }
            this._alignment = value;
            if (!this.parent) {
                return;
            }
            this.parent.dataset['alignment'] = value;
            this.parent.update();
        }
        /**
         * Get the inter-element spacing for the split layout.
         */
        get spacing() {
            return this._spacing;
        }
        /**
         * Set the inter-element spacing for the split layout.
         */
        set spacing(value) {
            value = Utils.clampDimension(value);
            if (this._spacing === value) {
                return;
            }
            this._spacing = value;
            if (!this.parent) {
                return;
            }
            this.parent.fit();
        }
        /**
         * A read-only array of the split handles in the layout.
         */
        get handles() {
            return this._handles;
        }
        /**
         * Get the absolute sizes of the widgets in the layout.
         *
         * @returns A new array of the absolute sizes of the widgets.
         *
         * This method **does not** measure the DOM nodes.
         */
        absoluteSizes() {
            return this._sizers.map(sizer => sizer.size);
        }
        /**
         * Get the relative sizes of the widgets in the layout.
         *
         * @returns A new array of the relative sizes of the widgets.
         *
         * #### Notes
         * The returned sizes reflect the sizes of the widgets normalized
         * relative to their siblings.
         *
         * This method **does not** measure the DOM nodes.
         */
        relativeSizes() {
            return Private$h.normalize(this._sizers.map(sizer => sizer.size));
        }
        /**
         * Set the relative sizes for the widgets in the layout.
         *
         * @param sizes - The relative sizes for the widgets in the panel.
         * @param update - Update the layout after setting relative sizes.
         * Default is True.
         *
         * #### Notes
         * Extra values are ignored, too few will yield an undefined layout.
         *
         * The actual geometry of the DOM nodes is updated asynchronously.
         */
        setRelativeSizes(sizes, update = true) {
            // Copy the sizes and pad with zeros as needed.
            let n = this._sizers.length;
            let temp = sizes.slice(0, n);
            while (temp.length < n) {
                temp.push(0);
            }
            // Normalize the padded sizes.
            let normed = Private$h.normalize(temp);
            // Apply the normalized sizes to the sizers.
            for (let i = 0; i < n; ++i) {
                let sizer = this._sizers[i];
                sizer.sizeHint = normed[i];
                sizer.size = normed[i];
            }
            // Set the flag indicating the sizes are normalized.
            this._hasNormedSizes = true;
            // Trigger an update of the parent widget.
            if (update && this.parent) {
                this.parent.update();
            }
        }
        /**
         * Move the offset position of a split handle.
         *
         * @param index - The index of the handle of the interest.
         *
         * @param position - The desired offset position of the handle.
         *
         * #### Notes
         * The position is relative to the offset parent.
         *
         * This will move the handle as close as possible to the desired
         * position. The sibling widgets will be adjusted as necessary.
         */
        moveHandle(index, position) {
            // Bail if the index is invalid or the handle is hidden.
            let handle = this._handles[index];
            if (!handle || handle.classList.contains('lm-mod-hidden')) {
                return;
            }
            // Compute the desired delta movement for the handle.
            let delta;
            if (this._orientation === 'horizontal') {
                delta = position - handle.offsetLeft;
            }
            else {
                delta = position - handle.offsetTop;
            }
            // Bail if there is no handle movement.
            if (delta === 0) {
                return;
            }
            // Prevent widget resizing unless needed.
            for (let sizer of this._sizers) {
                if (sizer.size > 0) {
                    sizer.sizeHint = sizer.size;
                }
            }
            // Adjust the sizers to reflect the handle movement.
            exports.BoxEngine.adjust(this._sizers, index, delta);
            // Update the layout of the widgets.
            if (this.parent) {
                this.parent.update();
            }
        }
        /**
         * Perform layout initialization which requires the parent widget.
         */
        init() {
            this.parent.dataset['orientation'] = this.orientation;
            this.parent.dataset['alignment'] = this.alignment;
            super.init();
        }
        /**
         * Attach a widget to the parent's DOM node.
         *
         * @param index - The current index of the widget in the layout.
         *
         * @param widget - The widget to attach to the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        attachWidget(index, widget) {
            // Create the item, handle, and sizer for the new widget.
            let item = new LayoutItem(widget);
            let handle = Private$h.createHandle(this.renderer);
            let average = Private$h.averageSize(this._sizers);
            let sizer = Private$h.createSizer(average);
            // Insert the item, handle, and sizer into the internal arrays.
            algorithm.ArrayExt.insert(this._items, index, item);
            algorithm.ArrayExt.insert(this._sizers, index, sizer);
            algorithm.ArrayExt.insert(this._handles, index, handle);
            // Send a `'before-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            }
            // Add the widget and handle nodes to the parent.
            this.parent.node.appendChild(widget.node);
            this.parent.node.appendChild(handle);
            // Send an `'after-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
            }
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * Move a widget in the parent's DOM node.
         *
         * @param fromIndex - The previous index of the widget in the layout.
         *
         * @param toIndex - The current index of the widget in the layout.
         *
         * @param widget - The widget to move in the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        moveWidget(fromIndex, toIndex, widget) {
            // Move the item, sizer, and handle for the widget.
            algorithm.ArrayExt.move(this._items, fromIndex, toIndex);
            algorithm.ArrayExt.move(this._sizers, fromIndex, toIndex);
            algorithm.ArrayExt.move(this._handles, fromIndex, toIndex);
            // Post a fit request to the parent to show/hide last handle.
            this.parent.fit();
        }
        /**
         * Detach a widget from the parent's DOM node.
         *
         * @param index - The previous index of the widget in the layout.
         *
         * @param widget - The widget to detach from the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        detachWidget(index, widget) {
            // Remove the item, handle, and sizer for the widget.
            let item = algorithm.ArrayExt.removeAt(this._items, index);
            let handle = algorithm.ArrayExt.removeAt(this._handles, index);
            algorithm.ArrayExt.removeAt(this._sizers, index);
            // Send a `'before-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            }
            // Remove the widget and handle nodes from the parent.
            this.parent.node.removeChild(widget.node);
            this.parent.node.removeChild(handle);
            // Send an `'after-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
            }
            // Dispose of the layout item.
            item.dispose();
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'before-show'` message.
         */
        onBeforeShow(msg) {
            super.onBeforeShow(msg);
            this.parent.update();
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            super.onBeforeAttach(msg);
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-shown'` message.
         */
        onChildShown(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-hidden'` message.
         */
        onChildHidden(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'resize'` message.
         */
        onResize(msg) {
            if (this.parent.isVisible) {
                this._update(msg.width, msg.height);
            }
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            if (this.parent.isVisible) {
                this._update(-1, -1);
            }
        }
        /**
         * A message handler invoked on a `'fit-request'` message.
         */
        onFitRequest(msg) {
            if (this.parent.isAttached) {
                this._fit();
            }
        }
        /**
         * Update the item position.
         *
         * @param i Item index
         * @param isHorizontal Whether the layout is horizontal or not
         * @param left Left position in pixels
         * @param top Top position in pixels
         * @param height Item height
         * @param width Item width
         * @param size Item size
         */
        updateItemPosition(i, isHorizontal, left, top, height, width, size) {
            const item = this._items[i];
            if (item.isHidden) {
                return;
            }
            // Fetch the style for the handle.
            let handleStyle = this._handles[i].style;
            // Update the widget and handle, and advance the relevant edge.
            if (isHorizontal) {
                left += this.widgetOffset;
                item.update(left, top, size, height);
                left += size;
                handleStyle.top = `${top}px`;
                handleStyle.left = `${left}px`;
                handleStyle.width = `${this._spacing}px`;
                handleStyle.height = `${height}px`;
            }
            else {
                top += this.widgetOffset;
                item.update(left, top, width, size);
                top += size;
                handleStyle.top = `${top}px`;
                handleStyle.left = `${left}px`;
                handleStyle.width = `${width}px`;
                handleStyle.height = `${this._spacing}px`;
            }
        }
        /**
         * Fit the layout to the total size required by the widgets.
         */
        _fit() {
            // Update the handles and track the visible widget count.
            let nVisible = 0;
            let lastHandleIndex = -1;
            for (let i = 0, n = this._items.length; i < n; ++i) {
                if (this._items[i].isHidden) {
                    this._handles[i].classList.add('lm-mod-hidden');
                }
                else {
                    this._handles[i].classList.remove('lm-mod-hidden');
                    lastHandleIndex = i;
                    nVisible++;
                }
            }
            // Hide the handle for the last visible widget.
            if (lastHandleIndex !== -1) {
                this._handles[lastHandleIndex].classList.add('lm-mod-hidden');
            }
            // Update the fixed space for the visible items.
            this._fixed =
                this._spacing * Math.max(0, nVisible - 1) +
                    this.widgetOffset * this._items.length;
            // Setup the computed minimum size.
            let horz = this._orientation === 'horizontal';
            let minW = horz ? this._fixed : 0;
            let minH = horz ? 0 : this._fixed;
            // Update the sizers and computed size limits.
            for (let i = 0, n = this._items.length; i < n; ++i) {
                // Fetch the item and corresponding box sizer.
                let item = this._items[i];
                let sizer = this._sizers[i];
                // Prevent resizing unless necessary.
                if (sizer.size > 0) {
                    sizer.sizeHint = sizer.size;
                }
                // If the item is hidden, it should consume zero size.
                if (item.isHidden) {
                    sizer.minSize = 0;
                    sizer.maxSize = 0;
                    continue;
                }
                // Update the size limits for the item.
                item.fit();
                // Update the stretch factor.
                sizer.stretch = SplitLayout.getStretch(item.widget);
                // Update the sizer limits and computed min size.
                if (horz) {
                    sizer.minSize = item.minWidth;
                    sizer.maxSize = item.maxWidth;
                    minW += item.minWidth;
                    minH = Math.max(minH, item.minHeight);
                }
                else {
                    sizer.minSize = item.minHeight;
                    sizer.maxSize = item.maxHeight;
                    minH += item.minHeight;
                    minW = Math.max(minW, item.minWidth);
                }
            }
            // Update the box sizing and add it to the computed min size.
            let box = (this._box = domutils.ElementExt.boxSizing(this.parent.node));
            minW += box.horizontalSum;
            minH += box.verticalSum;
            // Update the parent's min size constraints.
            let style = this.parent.node.style;
            style.minWidth = `${minW}px`;
            style.minHeight = `${minH}px`;
            // Set the dirty flag to ensure only a single update occurs.
            this._dirty = true;
            // Notify the ancestor that it should fit immediately. This may
            // cause a resize of the parent, fulfilling the required update.
            if (this.parent.parent) {
                messaging.MessageLoop.sendMessage(this.parent.parent, Widget.Msg.FitRequest);
            }
            // If the dirty flag is still set, the parent was not resized.
            // Trigger the required update on the parent widget immediately.
            if (this._dirty) {
                messaging.MessageLoop.sendMessage(this.parent, Widget.Msg.UpdateRequest);
            }
        }
        /**
         * Update the layout position and size of the widgets.
         *
         * The parent offset dimensions should be `-1` if unknown.
         */
        _update(offsetWidth, offsetHeight) {
            // Clear the dirty flag to indicate the update occurred.
            this._dirty = false;
            // Compute the visible item count.
            let nVisible = 0;
            for (let i = 0, n = this._items.length; i < n; ++i) {
                nVisible += +!this._items[i].isHidden;
            }
            // Bail early if there are no visible items to layout.
            if (nVisible === 0 && this.widgetOffset === 0) {
                return;
            }
            // Measure the parent if the offset dimensions are unknown.
            if (offsetWidth < 0) {
                offsetWidth = this.parent.node.offsetWidth;
            }
            if (offsetHeight < 0) {
                offsetHeight = this.parent.node.offsetHeight;
            }
            // Ensure the parent box sizing data is computed.
            if (!this._box) {
                this._box = domutils.ElementExt.boxSizing(this.parent.node);
            }
            // Compute the actual layout bounds adjusted for border and padding.
            let top = this._box.paddingTop;
            let left = this._box.paddingLeft;
            let width = offsetWidth - this._box.horizontalSum;
            let height = offsetHeight - this._box.verticalSum;
            // Set up the variables for justification and alignment offset.
            let extra = 0;
            let offset = 0;
            let horz = this._orientation === 'horizontal';
            if (nVisible > 0) {
                // Compute the adjusted layout space.
                let space;
                if (horz) {
                    // left += this.widgetOffset;
                    space = Math.max(0, width - this._fixed);
                }
                else {
                    // top += this.widgetOffset;
                    space = Math.max(0, height - this._fixed);
                }
                // Scale the size hints if they are normalized.
                if (this._hasNormedSizes) {
                    for (let sizer of this._sizers) {
                        sizer.sizeHint *= space;
                    }
                    this._hasNormedSizes = false;
                }
                // Distribute the layout space to the box sizers.
                let delta = exports.BoxEngine.calc(this._sizers, space);
                // Account for alignment if there is extra layout space.
                if (delta > 0) {
                    switch (this._alignment) {
                        case 'start':
                            break;
                        case 'center':
                            extra = 0;
                            offset = delta / 2;
                            break;
                        case 'end':
                            extra = 0;
                            offset = delta;
                            break;
                        case 'justify':
                            extra = delta / nVisible;
                            offset = 0;
                            break;
                        default:
                            throw 'unreachable';
                    }
                }
            }
            // Layout the items using the computed box sizes.
            for (let i = 0, n = this._items.length; i < n; ++i) {
                // Fetch the item.
                const item = this._items[i];
                // Fetch the computed size for the widget.
                const size = item.isHidden ? 0 : this._sizers[i].size + extra;
                this.updateItemPosition(i, horz, horz ? left + offset : left, horz ? top : top + offset, height, width, size);
                const fullOffset = this.widgetOffset +
                    (this._handles[i].classList.contains('lm-mod-hidden')
                        ? 0
                        : this._spacing);
                if (horz) {
                    left += size + fullOffset;
                }
                else {
                    top += size + fullOffset;
                }
            }
        }
    }
    /**
     * The namespace for the `SplitLayout` class statics.
     */
    (function (SplitLayout) {
        /**
         * Get the split layout stretch factor for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The split layout stretch factor for the widget.
         */
        function getStretch(widget) {
            return Private$h.stretchProperty.get(widget);
        }
        SplitLayout.getStretch = getStretch;
        /**
         * Set the split layout stretch factor for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the stretch factor.
         */
        function setStretch(widget, value) {
            Private$h.stretchProperty.set(widget, value);
        }
        SplitLayout.setStretch = setStretch;
    })(SplitLayout || (SplitLayout = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$h;
    (function (Private) {
        /**
         * The property descriptor for a widget stretch factor.
         */
        Private.stretchProperty = new properties.AttachedProperty({
            name: 'stretch',
            create: () => 0,
            coerce: (owner, value) => Math.max(0, Math.floor(value)),
            changed: onChildSizingChanged
        });
        /**
         * Create a new box sizer with the given size hint.
         */
        function createSizer(size) {
            let sizer = new BoxSizer();
            sizer.sizeHint = Math.floor(size);
            return sizer;
        }
        Private.createSizer = createSizer;
        /**
         * Create a new split handle node using the given renderer.
         */
        function createHandle(renderer) {
            let handle = renderer.createHandle();
            handle.style.position = 'absolute';
            // Do not use size containment to allow the handle to fill the available space
            handle.style.contain = 'style';
            return handle;
        }
        Private.createHandle = createHandle;
        /**
         * Compute the average size of an array of box sizers.
         */
        function averageSize(sizers) {
            return sizers.reduce((v, s) => v + s.size, 0) / sizers.length || 0;
        }
        Private.averageSize = averageSize;
        /**
         * Normalize an array of values.
         */
        function normalize(values) {
            let n = values.length;
            if (n === 0) {
                return [];
            }
            let sum = values.reduce((a, b) => a + Math.abs(b), 0);
            return sum === 0 ? values.map(v => 1 / n) : values.map(v => v / sum);
        }
        Private.normalize = normalize;
        /**
         * The change handler for the attached sizing properties.
         */
        function onChildSizingChanged(child) {
            if (child.parent && child.parent.layout instanceof SplitLayout) {
                child.parent.fit();
            }
        }
    })(Private$h || (Private$h = {}));

    /*
     * Copyright (c) Jupyter Development Team.
     * Distributed under the terms of the Modified BSD License.
     */
    /**
     * A layout which arranges its widgets into collapsible resizable sections.
     */
    class AccordionLayout extends SplitLayout {
        /**
         * Construct a new accordion layout.
         *
         * @param options - The options for initializing the layout.
         *
         * #### Notes
         * The default orientation will be vertical.
         *
         * Titles must be rotated for horizontal accordion panel using CSS: see accordionpanel.css
         */
        constructor(options) {
            super({ ...options, orientation: options.orientation || 'vertical' });
            this._titles = [];
            this.titleSpace = options.titleSpace || 22;
        }
        /**
         * The section title height or width depending on the orientation.
         */
        get titleSpace() {
            return this.widgetOffset;
        }
        set titleSpace(value) {
            value = Utils$1.clampDimension(value);
            if (this.widgetOffset === value) {
                return;
            }
            this.widgetOffset = value;
            if (!this.parent) {
                return;
            }
            this.parent.fit();
        }
        /**
         * A read-only array of the section titles in the panel.
         */
        get titles() {
            return this._titles;
        }
        /**
         * Dispose of the resources held by the layout.
         */
        dispose() {
            if (this.isDisposed) {
                return;
            }
            // Clear the layout state.
            this._titles.length = 0;
            // Dispose of the rest of the layout.
            super.dispose();
        }
        updateTitle(index, widget) {
            const oldTitle = this._titles[index];
            const expanded = oldTitle.classList.contains('lm-mod-expanded');
            const newTitle = Private$g.createTitle(this.renderer, widget.title, expanded);
            this._titles[index] = newTitle;
            // Add the title node to the parent before the widget.
            this.parent.node.replaceChild(newTitle, oldTitle);
        }
        /**
         * Insert a widget into the layout at the specified index.
         *
         * @param index - The index at which to insert the widget.
         *
         * @param widget - The widget to insert into the layout.
         *
         * #### Notes
         * The index will be clamped to the bounds of the widgets.
         *
         * If the widget is already added to the layout, it will be moved.
         *
         * #### Undefined Behavior
         * An `index` which is non-integral.
         */
        insertWidget(index, widget) {
            if (!widget.id) {
                widget.id = `id-${coreutils.UUID.uuid4()}`;
            }
            super.insertWidget(index, widget);
        }
        /**
         * Attach a widget to the parent's DOM node.
         *
         * @param index - The current index of the widget in the layout.
         *
         * @param widget - The widget to attach to the parent.
         */
        attachWidget(index, widget) {
            const title = Private$g.createTitle(this.renderer, widget.title, !widget.isHidden);
            algorithm.ArrayExt.insert(this._titles, index, title);
            // Add the title node to the parent before the widget.
            this.parent.node.appendChild(title);
            widget.node.setAttribute('role', 'region');
            widget.node.setAttribute('aria-labelledby', title.id);
            super.attachWidget(index, widget);
        }
        /**
         * Move a widget in the parent's DOM node.
         *
         * @param fromIndex - The previous index of the widget in the layout.
         *
         * @param toIndex - The current index of the widget in the layout.
         *
         * @param widget - The widget to move in the parent.
         */
        moveWidget(fromIndex, toIndex, widget) {
            algorithm.ArrayExt.move(this._titles, fromIndex, toIndex);
            super.moveWidget(fromIndex, toIndex, widget);
        }
        /**
         * Detach a widget from the parent's DOM node.
         *
         * @param index - The previous index of the widget in the layout.
         *
         * @param widget - The widget to detach from the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        detachWidget(index, widget) {
            const title = algorithm.ArrayExt.removeAt(this._titles, index);
            this.parent.node.removeChild(title);
            super.detachWidget(index, widget);
        }
        /**
         * Update the item position.
         *
         * @param i Item index
         * @param isHorizontal Whether the layout is horizontal or not
         * @param left Left position in pixels
         * @param top Top position in pixels
         * @param height Item height
         * @param width Item width
         * @param size Item size
         */
        updateItemPosition(i, isHorizontal, left, top, height, width, size) {
            const titleStyle = this._titles[i].style;
            // Titles must be rotated for horizontal accordion panel using CSS: see accordionpanel.css
            titleStyle.top = `${top}px`;
            titleStyle.left = `${left}px`;
            titleStyle.height = `${this.widgetOffset}px`;
            if (isHorizontal) {
                titleStyle.width = `${height}px`;
            }
            else {
                titleStyle.width = `${width}px`;
            }
            super.updateItemPosition(i, isHorizontal, left, top, height, width, size);
        }
    }
    var Private$g;
    (function (Private) {
        /**
         * Create the title HTML element.
         *
         * @param renderer Accordion renderer
         * @param data Widget title
         * @returns Title HTML element
         */
        function createTitle(renderer, data, expanded = true) {
            const title = renderer.createSectionTitle(data);
            title.style.position = 'absolute';
            title.style.contain = 'strict';
            title.setAttribute('aria-label', `${data.label} Section`);
            title.setAttribute('aria-expanded', expanded ? 'true' : 'false');
            title.setAttribute('aria-controls', data.owner.id);
            if (expanded) {
                title.classList.add('lm-mod-expanded');
            }
            return title;
        }
        Private.createTitle = createTitle;
    })(Private$g || (Private$g = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A simple and convenient panel widget class.
     *
     * #### Notes
     * This class is suitable as a base class for implementing a variety of
     * convenience panel widgets, but can also be used directly with CSS to
     * arrange a collection of widgets.
     *
     * This class provides a convenience wrapper around a {@link PanelLayout}.
     */
    class Panel extends Widget {
        /**
         * Construct a new panel.
         *
         * @param options - The options for initializing the panel.
         */
        constructor(options = {}) {
            super();
            this.addClass('lm-Panel');
            this.layout = Private$f.createLayout(options);
        }
        /**
         * A read-only array of the widgets in the panel.
         */
        get widgets() {
            return this.layout.widgets;
        }
        /**
         * Add a widget to the end of the panel.
         *
         * @param widget - The widget to add to the panel.
         *
         * #### Notes
         * If the widget is already contained in the panel, it will be moved.
         */
        addWidget(widget) {
            this.layout.addWidget(widget);
        }
        /**
         * Insert a widget at the specified index.
         *
         * @param index - The index at which to insert the widget.
         *
         * @param widget - The widget to insert into to the panel.
         *
         * #### Notes
         * If the widget is already contained in the panel, it will be moved.
         */
        insertWidget(index, widget) {
            this.layout.insertWidget(index, widget);
        }
    }
    /**
     * The namespace for the module implementation details.
     */
    var Private$f;
    (function (Private) {
        /**
         * Create a panel layout for the given panel options.
         */
        function createLayout(options) {
            return options.layout || new PanelLayout();
        }
        Private.createLayout = createLayout;
    })(Private$f || (Private$f = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A panel which arranges its widgets into resizable sections.
     *
     * #### Notes
     * This class provides a convenience wrapper around a {@link SplitLayout}.
     */
    class SplitPanel extends Panel {
        /**
         * Construct a new split panel.
         *
         * @param options - The options for initializing the split panel.
         */
        constructor(options = {}) {
            super({ layout: Private$e.createLayout(options) });
            this._handleMoved = new signaling.Signal(this);
            this._pressData = null;
            this.addClass('lm-SplitPanel');
        }
        /**
         * Dispose of the resources held by the panel.
         */
        dispose() {
            this._releaseMouse();
            super.dispose();
        }
        /**
         * Get the layout orientation for the split panel.
         */
        get orientation() {
            return this.layout.orientation;
        }
        /**
         * Set the layout orientation for the split panel.
         */
        set orientation(value) {
            this.layout.orientation = value;
        }
        /**
         * Get the content alignment for the split panel.
         *
         * #### Notes
         * This is the alignment of the widgets in the layout direction.
         *
         * The alignment has no effect if the widgets can expand to fill the
         * entire split panel.
         */
        get alignment() {
            return this.layout.alignment;
        }
        /**
         * Set the content alignment for the split panel.
         *
         * #### Notes
         * This is the alignment of the widgets in the layout direction.
         *
         * The alignment has no effect if the widgets can expand to fill the
         * entire split panel.
         */
        set alignment(value) {
            this.layout.alignment = value;
        }
        /**
         * Get the inter-element spacing for the split panel.
         */
        get spacing() {
            return this.layout.spacing;
        }
        /**
         * Set the inter-element spacing for the split panel.
         */
        set spacing(value) {
            this.layout.spacing = value;
        }
        /**
         * The renderer used by the split panel.
         */
        get renderer() {
            return this.layout.renderer;
        }
        /**
         * A signal emitted when a split handle has moved.
         */
        get handleMoved() {
            return this._handleMoved;
        }
        /**
         * A read-only array of the split handles in the panel.
         */
        get handles() {
            return this.layout.handles;
        }
        /**
         * Get the relative sizes of the widgets in the panel.
         *
         * @returns A new array of the relative sizes of the widgets.
         *
         * #### Notes
         * The returned sizes reflect the sizes of the widgets normalized
         * relative to their siblings.
         *
         * This method **does not** measure the DOM nodes.
         */
        relativeSizes() {
            return this.layout.relativeSizes();
        }
        /**
         * Set the relative sizes for the widgets in the panel.
         *
         * @param sizes - The relative sizes for the widgets in the panel.
         * @param update - Update the layout after setting relative sizes.
         * Default is True.
         *
         * #### Notes
         * Extra values are ignored, too few will yield an undefined layout.
         *
         * The actual geometry of the DOM nodes is updated asynchronously.
         */
        setRelativeSizes(sizes, update = true) {
            this.layout.setRelativeSizes(sizes, update);
        }
        /**
         * Handle the DOM events for the split panel.
         *
         * @param event - The DOM event sent to the panel.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the panel's DOM node. It should
         * not be called directly by user code.
         */
        handleEvent(event) {
            switch (event.type) {
                case 'pointerdown':
                    this._evtPointerDown(event);
                    break;
                case 'pointermove':
                    this._evtPointerMove(event);
                    break;
                case 'pointerup':
                    this._evtPointerUp(event);
                    break;
                case 'keydown':
                    this._evtKeyDown(event);
                    break;
                case 'contextmenu':
                    event.preventDefault();
                    event.stopPropagation();
                    break;
            }
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            this.node.addEventListener('pointerdown', this);
        }
        /**
         * A message handler invoked on an `'after-detach'` message.
         */
        onAfterDetach(msg) {
            this.node.removeEventListener('pointerdown', this);
            this._releaseMouse();
        }
        /**
         * A message handler invoked on a `'child-added'` message.
         */
        onChildAdded(msg) {
            msg.child.addClass('lm-SplitPanel-child');
            this._releaseMouse();
        }
        /**
         * A message handler invoked on a `'child-removed'` message.
         */
        onChildRemoved(msg) {
            msg.child.removeClass('lm-SplitPanel-child');
            this._releaseMouse();
        }
        /**
         * Handle the `'keydown'` event for the split panel.
         */
        _evtKeyDown(event) {
            // Stop input events during drag.
            if (this._pressData) {
                event.preventDefault();
                event.stopPropagation();
            }
            // Release the mouse if `Escape` is pressed.
            if (event.keyCode === 27) {
                this._releaseMouse();
            }
        }
        /**
         * Handle the `'pointerdown'` event for the split panel.
         */
        _evtPointerDown(event) {
            // Do nothing if the primary button is not pressed.
            if (event.button !== 0) {
                return;
            }
            // Find the handle which contains the target, if any.
            let layout = this.layout;
            let index = algorithm.ArrayExt.findFirstIndex(layout.handles, handle => {
                return handle.contains(event.target);
            });
            // Bail early if the mouse press was not on a handle.
            if (index === -1) {
                return;
            }
            // Stop the event when a split handle is pressed.
            event.preventDefault();
            event.stopPropagation();
            // Add the extra document listeners.
            document.addEventListener('pointerup', this, true);
            document.addEventListener('pointermove', this, true);
            document.addEventListener('keydown', this, true);
            document.addEventListener('contextmenu', this, true);
            // Compute the offset delta for the handle press.
            let delta;
            let handle = layout.handles[index];
            let rect = handle.getBoundingClientRect();
            if (layout.orientation === 'horizontal') {
                delta = event.clientX - rect.left;
            }
            else {
                delta = event.clientY - rect.top;
            }
            // Override the cursor and store the press data.
            let style = window.getComputedStyle(handle);
            let override = dragdrop.Drag.overrideCursor(style.cursor);
            this._pressData = { index, delta, override };
        }
        /**
         * Handle the `'pointermove'` event for the split panel.
         */
        _evtPointerMove(event) {
            // Stop the event when dragging a split handle.
            event.preventDefault();
            event.stopPropagation();
            // Compute the desired offset position for the handle.
            let pos;
            let layout = this.layout;
            let rect = this.node.getBoundingClientRect();
            if (layout.orientation === 'horizontal') {
                pos = event.clientX - rect.left - this._pressData.delta;
            }
            else {
                pos = event.clientY - rect.top - this._pressData.delta;
            }
            // Move the handle as close to the desired position as possible.
            layout.moveHandle(this._pressData.index, pos);
        }
        /**
         * Handle the `'pointerup'` event for the split panel.
         */
        _evtPointerUp(event) {
            // Do nothing if the primary button is not released.
            if (event.button !== 0) {
                return;
            }
            // Stop the event when releasing a handle.
            event.preventDefault();
            event.stopPropagation();
            // Finalize the mouse release.
            this._releaseMouse();
        }
        /**
         * Release the mouse grab for the split panel.
         */
        _releaseMouse() {
            // Bail early if no drag is in progress.
            if (!this._pressData) {
                return;
            }
            // Clear the override cursor.
            this._pressData.override.dispose();
            this._pressData = null;
            // Emit the handle moved signal.
            this._handleMoved.emit();
            // Remove the extra document listeners.
            document.removeEventListener('keydown', this, true);
            document.removeEventListener('pointerup', this, true);
            document.removeEventListener('pointermove', this, true);
            document.removeEventListener('contextmenu', this, true);
        }
    }
    /**
     * The namespace for the `SplitPanel` class statics.
     */
    (function (SplitPanel) {
        /**
         * The default implementation of `IRenderer`.
         */
        class Renderer {
            /**
             * Create a new handle for use with a split panel.
             *
             * @returns A new handle element for a split panel.
             */
            createHandle() {
                let handle = document.createElement('div');
                handle.className = 'lm-SplitPanel-handle';
                return handle;
            }
        }
        SplitPanel.Renderer = Renderer;
        /**
         * The default `Renderer` instance.
         */
        SplitPanel.defaultRenderer = new Renderer();
        /**
         * Get the split panel stretch factor for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The split panel stretch factor for the widget.
         */
        function getStretch(widget) {
            return SplitLayout.getStretch(widget);
        }
        SplitPanel.getStretch = getStretch;
        /**
         * Set the split panel stretch factor for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the stretch factor.
         */
        function setStretch(widget, value) {
            SplitLayout.setStretch(widget, value);
        }
        SplitPanel.setStretch = setStretch;
    })(SplitPanel || (SplitPanel = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$e;
    (function (Private) {
        /**
         * Create a split layout for the given panel options.
         */
        function createLayout(options) {
            return (options.layout ||
                new SplitLayout({
                    renderer: options.renderer || SplitPanel.defaultRenderer,
                    orientation: options.orientation,
                    alignment: options.alignment,
                    spacing: options.spacing
                }));
        }
        Private.createLayout = createLayout;
    })(Private$e || (Private$e = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /**
     * A panel which arranges its widgets into resizable sections separated by a title widget.
     *
     * #### Notes
     * This class provides a convenience wrapper around {@link AccordionLayout}.
     *
     * See also the related [example](../../examples/accordionpanel/index.html) and
     * its [source](https://github.com/jupyterlab/lumino/tree/main/examples/example-accordionpanel).
     */
    class AccordionPanel extends SplitPanel {
        /**
         * Construct a new accordion panel.
         *
         * @param options - The options for initializing the accordion panel.
         *
         */
        constructor(options = {}) {
            super({ ...options, layout: Private$d.createLayout(options) });
            this._widgetSizesCache = new WeakMap();
            this._expansionToggled = new signaling.Signal(this);
            this.addClass('lm-AccordionPanel');
        }
        /**
         * The renderer used by the accordion panel.
         */
        get renderer() {
            return this.layout.renderer;
        }
        /**
         * The section title space.
         *
         * This is the height if the panel is vertical and the width if it is
         * horizontal.
         */
        get titleSpace() {
            return this.layout.titleSpace;
        }
        set titleSpace(value) {
            this.layout.titleSpace = value;
        }
        /**
         * A read-only array of the section titles in the panel.
         */
        get titles() {
            return this.layout.titles;
        }
        /**
         * A signal emitted when a widget of the AccordionPanel is collapsed or expanded.
         */
        get expansionToggled() {
            return this._expansionToggled;
        }
        /**
         * Add a widget to the end of the panel.
         *
         * @param widget - The widget to add to the panel.
         *
         * #### Notes
         * If the widget is already contained in the panel, it will be moved.
         */
        addWidget(widget) {
            super.addWidget(widget);
            widget.title.changed.connect(this._onTitleChanged, this);
        }
        /**
         * Collapse the widget at position `index`.
         *
         * #### Notes
         * If no widget is found for `index`, this will bail.
         *
         * @param index Widget index
         */
        collapse(index) {
            const widget = this.layout.widgets[index];
            if (widget && !widget.isHidden) {
                this._toggleExpansion(index);
            }
        }
        /**
         * Expand the widget at position `index`.
         *
         * #### Notes
         * If no widget is found for `index`, this will bail.
         *
         * @param index Widget index
         */
        expand(index) {
            const widget = this.layout.widgets[index];
            if (widget && widget.isHidden) {
                this._toggleExpansion(index);
            }
        }
        /**
         * Insert a widget at the specified index.
         *
         * @param index - The index at which to insert the widget.
         *
         * @param widget - The widget to insert into to the panel.
         *
         * #### Notes
         * If the widget is already contained in the panel, it will be moved.
         */
        insertWidget(index, widget) {
            super.insertWidget(index, widget);
            widget.title.changed.connect(this._onTitleChanged, this);
        }
        /**
         * Handle the DOM events for the accordion panel.
         *
         * @param event - The DOM event sent to the panel.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the panel's DOM node. It should
         * not be called directly by user code.
         */
        handleEvent(event) {
            super.handleEvent(event);
            switch (event.type) {
                case 'click':
                    this._evtClick(event);
                    break;
                case 'keydown':
                    this._eventKeyDown(event);
                    break;
            }
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            this.node.addEventListener('click', this);
            this.node.addEventListener('keydown', this);
            super.onBeforeAttach(msg);
        }
        /**
         * A message handler invoked on an `'after-detach'` message.
         */
        onAfterDetach(msg) {
            super.onAfterDetach(msg);
            this.node.removeEventListener('click', this);
            this.node.removeEventListener('keydown', this);
        }
        /**
         * Handle the `changed` signal of a title object.
         */
        _onTitleChanged(sender) {
            const index = algorithm.ArrayExt.findFirstIndex(this.widgets, widget => {
                return widget.contains(sender.owner);
            });
            if (index >= 0) {
                this.layout.updateTitle(index, sender.owner);
                this.update();
            }
        }
        /**
         * Compute the size of widgets in this panel on the title click event.
         * On closing, the size of the widget is cached and we will try to expand
         * the last opened widget.
         * On opening, we will use the cached size if it is available to restore the
         * widget.
         * In both cases, if we can not compute the size of widgets, we will let
         * `SplitLayout` decide.
         *
         * @param index - The index of widget to be opened of closed
         *
         * @returns Relative size of widgets in this panel, if this size can
         * not be computed, return `undefined`
         */
        _computeWidgetSize(index) {
            const layout = this.layout;
            const widget = layout.widgets[index];
            if (!widget) {
                return undefined;
            }
            const isHidden = widget.isHidden;
            const widgetSizes = layout.absoluteSizes();
            const delta = (isHidden ? -1 : 1) * this.spacing;
            const totalSize = widgetSizes.reduce((prev, curr) => prev + curr);
            let newSize = [...widgetSizes];
            if (!isHidden) {
                // Hide the widget
                const currentSize = widgetSizes[index];
                this._widgetSizesCache.set(widget, currentSize);
                newSize[index] = 0;
                const widgetToCollapse = newSize.map(sz => sz > 0).lastIndexOf(true);
                if (widgetToCollapse === -1) {
                    // All widget are closed, let the `SplitLayout` compute widget sizes.
                    return undefined;
                }
                newSize[widgetToCollapse] =
                    widgetSizes[widgetToCollapse] + currentSize + delta;
            }
            else {
                // Show the widget
                const previousSize = this._widgetSizesCache.get(widget);
                if (!previousSize) {
                    // Previous size is unavailable, let the `SplitLayout` compute widget sizes.
                    return undefined;
                }
                newSize[index] += previousSize;
                const widgetToCollapse = newSize
                    .map(sz => sz - previousSize > 0)
                    .lastIndexOf(true);
                if (widgetToCollapse === -1) {
                    // Can not reduce the size of one widget, reduce all opened widgets
                    // proportionally with its size.
                    newSize.forEach((_, idx) => {
                        if (idx !== index) {
                            newSize[idx] -=
                                (widgetSizes[idx] / totalSize) * (previousSize - delta);
                        }
                    });
                }
                else {
                    newSize[widgetToCollapse] -= previousSize - delta;
                }
            }
            return newSize.map(sz => sz / (totalSize + delta));
        }
        /**
         * Handle the `'click'` event for the accordion panel
         */
        _evtClick(event) {
            const target = event.target;
            if (target) {
                const index = algorithm.ArrayExt.findFirstIndex(this.titles, title => {
                    return title.contains(target);
                });
                if (index >= 0) {
                    event.preventDefault();
                    event.stopPropagation();
                    this._toggleExpansion(index);
                }
            }
        }
        /**
         * Handle the `'keydown'` event for the accordion panel.
         */
        _eventKeyDown(event) {
            if (event.defaultPrevented) {
                return;
            }
            const target = event.target;
            let handled = false;
            if (target) {
                const index = algorithm.ArrayExt.findFirstIndex(this.titles, title => {
                    return title.contains(target);
                });
                if (index >= 0) {
                    const keyCode = event.keyCode.toString();
                    // If Space or Enter is pressed on title, emulate click event
                    if (event.key.match(/Space|Enter/) || keyCode.match(/13|32/)) {
                        target.click();
                        handled = true;
                    }
                    else if (this.orientation === 'horizontal'
                        ? event.key.match(/ArrowLeft|ArrowRight/) || keyCode.match(/37|39/)
                        : event.key.match(/ArrowUp|ArrowDown/) || keyCode.match(/38|40/)) {
                        // If Up or Down (for vertical) / Left or Right (for horizontal) is pressed on title, loop on titles
                        const direction = event.key.match(/ArrowLeft|ArrowUp/) || keyCode.match(/37|38/)
                            ? -1
                            : 1;
                        const length = this.titles.length;
                        const newIndex = (index + length + direction) % length;
                        this.titles[newIndex].focus();
                        handled = true;
                    }
                    else if (event.key === 'End' || keyCode === '35') {
                        // If End is pressed on title, focus on the last title
                        this.titles[this.titles.length - 1].focus();
                        handled = true;
                    }
                    else if (event.key === 'Home' || keyCode === '36') {
                        // If Home is pressed on title, focus on the first title
                        this.titles[0].focus();
                        handled = true;
                    }
                }
                if (handled) {
                    event.preventDefault();
                }
            }
        }
        _toggleExpansion(index) {
            const title = this.titles[index];
            const widget = this.layout.widgets[index];
            const newSize = this._computeWidgetSize(index);
            if (newSize) {
                this.setRelativeSizes(newSize, false);
            }
            if (widget.isHidden) {
                title.classList.add('lm-mod-expanded');
                title.setAttribute('aria-expanded', 'true');
                widget.show();
            }
            else {
                title.classList.remove('lm-mod-expanded');
                title.setAttribute('aria-expanded', 'false');
                widget.hide();
            }
            // Emit the expansion state signal.
            this._expansionToggled.emit(index);
        }
    }
    /**
     * The namespace for the `AccordionPanel` class statics.
     */
    (function (AccordionPanel) {
        /**
         * The default implementation of `IRenderer`.
         */
        class Renderer extends SplitPanel.Renderer {
            constructor() {
                super();
                /**
                 * A selector which matches any title node in the accordion.
                 */
                this.titleClassName = 'lm-AccordionPanel-title';
                this._titleID = 0;
                this._titleKeys = new WeakMap();
                this._uuid = ++Renderer._nInstance;
            }
            /**
             * Render the collapse indicator for a section title.
             *
             * @param data - The data to use for rendering the section title.
             *
             * @returns A element representing the collapse indicator.
             */
            createCollapseIcon(data) {
                return document.createElement('span');
            }
            /**
             * Render the element for a section title.
             *
             * @param data - The data to use for rendering the section title.
             *
             * @returns A element representing the section title.
             */
            createSectionTitle(data) {
                const handle = document.createElement('h3');
                handle.setAttribute('tabindex', '0');
                handle.id = this.createTitleKey(data);
                handle.className = this.titleClassName;
                for (const aData in data.dataset) {
                    handle.dataset[aData] = data.dataset[aData];
                }
                const collapser = handle.appendChild(this.createCollapseIcon(data));
                collapser.className = 'lm-AccordionPanel-titleCollapser';
                const label = handle.appendChild(document.createElement('span'));
                label.className = 'lm-AccordionPanel-titleLabel';
                label.textContent = data.label;
                label.title = data.caption || data.label;
                return handle;
            }
            /**
             * Create a unique render key for the title.
             *
             * @param data - The data to use for the title.
             *
             * @returns The unique render key for the title.
             *
             * #### Notes
             * This method caches the key against the section title the first time
             * the key is generated.
             */
            createTitleKey(data) {
                let key = this._titleKeys.get(data);
                if (key === undefined) {
                    key = `title-key-${this._uuid}-${this._titleID++}`;
                    this._titleKeys.set(data, key);
                }
                return key;
            }
        }
        Renderer._nInstance = 0;
        AccordionPanel.Renderer = Renderer;
        /**
         * The default `Renderer` instance.
         */
        AccordionPanel.defaultRenderer = new Renderer();
    })(AccordionPanel || (AccordionPanel = {}));
    var Private$d;
    (function (Private) {
        /**
         * Create an accordion layout for the given panel options.
         *
         * @param options Panel options
         * @returns Panel layout
         */
        function createLayout(options) {
            return (options.layout ||
                new AccordionLayout({
                    renderer: options.renderer || AccordionPanel.defaultRenderer,
                    orientation: options.orientation,
                    alignment: options.alignment,
                    spacing: options.spacing,
                    titleSpace: options.titleSpace
                }));
        }
        Private.createLayout = createLayout;
    })(Private$d || (Private$d = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A layout which arranges its widgets in a single row or column.
     */
    class BoxLayout extends PanelLayout {
        /**
         * Construct a new box layout.
         *
         * @param options - The options for initializing the layout.
         */
        constructor(options = {}) {
            super();
            this._fixed = 0;
            this._spacing = 4;
            this._dirty = false;
            this._sizers = [];
            this._items = [];
            this._box = null;
            this._alignment = 'start';
            this._direction = 'top-to-bottom';
            if (options.direction !== undefined) {
                this._direction = options.direction;
            }
            if (options.alignment !== undefined) {
                this._alignment = options.alignment;
            }
            if (options.spacing !== undefined) {
                this._spacing = Utils$1.clampDimension(options.spacing);
            }
        }
        /**
         * Dispose of the resources held by the layout.
         */
        dispose() {
            // Dispose of the layout items.
            for (const item of this._items) {
                item.dispose();
            }
            // Clear the layout state.
            this._box = null;
            this._items.length = 0;
            this._sizers.length = 0;
            // Dispose of the rest of the layout.
            super.dispose();
        }
        /**
         * Get the layout direction for the box layout.
         */
        get direction() {
            return this._direction;
        }
        /**
         * Set the layout direction for the box layout.
         */
        set direction(value) {
            if (this._direction === value) {
                return;
            }
            this._direction = value;
            if (!this.parent) {
                return;
            }
            this.parent.dataset['direction'] = value;
            this.parent.fit();
        }
        /**
         * Get the content alignment for the box layout.
         *
         * #### Notes
         * This is the alignment of the widgets in the layout direction.
         *
         * The alignment has no effect if the widgets can expand to fill the
         * entire box layout.
         */
        get alignment() {
            return this._alignment;
        }
        /**
         * Set the content alignment for the box layout.
         *
         * #### Notes
         * This is the alignment of the widgets in the layout direction.
         *
         * The alignment has no effect if the widgets can expand to fill the
         * entire box layout.
         */
        set alignment(value) {
            if (this._alignment === value) {
                return;
            }
            this._alignment = value;
            if (!this.parent) {
                return;
            }
            this.parent.dataset['alignment'] = value;
            this.parent.update();
        }
        /**
         * Get the inter-element spacing for the box layout.
         */
        get spacing() {
            return this._spacing;
        }
        /**
         * Set the inter-element spacing for the box layout.
         */
        set spacing(value) {
            value = Utils$1.clampDimension(value);
            if (this._spacing === value) {
                return;
            }
            this._spacing = value;
            if (!this.parent) {
                return;
            }
            this.parent.fit();
        }
        /**
         * Perform layout initialization which requires the parent widget.
         */
        init() {
            this.parent.dataset['direction'] = this.direction;
            this.parent.dataset['alignment'] = this.alignment;
            super.init();
        }
        /**
         * Attach a widget to the parent's DOM node.
         *
         * @param index - The current index of the widget in the layout.
         *
         * @param widget - The widget to attach to the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        attachWidget(index, widget) {
            // Create and add a new layout item for the widget.
            algorithm.ArrayExt.insert(this._items, index, new LayoutItem(widget));
            // Create and add a new sizer for the widget.
            algorithm.ArrayExt.insert(this._sizers, index, new BoxSizer());
            // Send a `'before-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            }
            // Add the widget's node to the parent.
            this.parent.node.appendChild(widget.node);
            // Send an `'after-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
            }
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * Move a widget in the parent's DOM node.
         *
         * @param fromIndex - The previous index of the widget in the layout.
         *
         * @param toIndex - The current index of the widget in the layout.
         *
         * @param widget - The widget to move in the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        moveWidget(fromIndex, toIndex, widget) {
            // Move the layout item for the widget.
            algorithm.ArrayExt.move(this._items, fromIndex, toIndex);
            // Move the sizer for the widget.
            algorithm.ArrayExt.move(this._sizers, fromIndex, toIndex);
            // Post an update request for the parent widget.
            this.parent.update();
        }
        /**
         * Detach a widget from the parent's DOM node.
         *
         * @param index - The previous index of the widget in the layout.
         *
         * @param widget - The widget to detach from the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        detachWidget(index, widget) {
            // Remove the layout item for the widget.
            let item = algorithm.ArrayExt.removeAt(this._items, index);
            // Remove the sizer for the widget.
            algorithm.ArrayExt.removeAt(this._sizers, index);
            // Send a `'before-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            }
            // Remove the widget's node from the parent.
            this.parent.node.removeChild(widget.node);
            // Send an `'after-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
            }
            // Dispose of the layout item.
            item.dispose();
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'before-show'` message.
         */
        onBeforeShow(msg) {
            super.onBeforeShow(msg);
            this.parent.update();
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            super.onBeforeAttach(msg);
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-shown'` message.
         */
        onChildShown(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-hidden'` message.
         */
        onChildHidden(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'resize'` message.
         */
        onResize(msg) {
            if (this.parent.isVisible) {
                this._update(msg.width, msg.height);
            }
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            if (this.parent.isVisible) {
                this._update(-1, -1);
            }
        }
        /**
         * A message handler invoked on a `'fit-request'` message.
         */
        onFitRequest(msg) {
            if (this.parent.isAttached) {
                this._fit();
            }
        }
        /**
         * Fit the layout to the total size required by the widgets.
         */
        _fit() {
            // Compute the visible item count.
            let nVisible = 0;
            for (let i = 0, n = this._items.length; i < n; ++i) {
                nVisible += +!this._items[i].isHidden;
            }
            // Update the fixed space for the visible items.
            this._fixed = this._spacing * Math.max(0, nVisible - 1);
            // Setup the computed minimum size.
            let horz = Private$c.isHorizontal(this._direction);
            let minW = horz ? this._fixed : 0;
            let minH = horz ? 0 : this._fixed;
            // Update the sizers and computed minimum size.
            for (let i = 0, n = this._items.length; i < n; ++i) {
                // Fetch the item and corresponding box sizer.
                let item = this._items[i];
                let sizer = this._sizers[i];
                // If the item is hidden, it should consume zero size.
                if (item.isHidden) {
                    sizer.minSize = 0;
                    sizer.maxSize = 0;
                    continue;
                }
                // Update the size limits for the item.
                item.fit();
                // Update the size basis and stretch factor.
                sizer.sizeHint = BoxLayout.getSizeBasis(item.widget);
                sizer.stretch = BoxLayout.getStretch(item.widget);
                // Update the sizer limits and computed min size.
                if (horz) {
                    sizer.minSize = item.minWidth;
                    sizer.maxSize = item.maxWidth;
                    minW += item.minWidth;
                    minH = Math.max(minH, item.minHeight);
                }
                else {
                    sizer.minSize = item.minHeight;
                    sizer.maxSize = item.maxHeight;
                    minH += item.minHeight;
                    minW = Math.max(minW, item.minWidth);
                }
            }
            // Update the box sizing and add it to the computed min size.
            let box = (this._box = domutils.ElementExt.boxSizing(this.parent.node));
            minW += box.horizontalSum;
            minH += box.verticalSum;
            // Update the parent's min size constraints.
            let style = this.parent.node.style;
            style.minWidth = `${minW}px`;
            style.minHeight = `${minH}px`;
            // Set the dirty flag to ensure only a single update occurs.
            this._dirty = true;
            // Notify the ancestor that it should fit immediately. This may
            // cause a resize of the parent, fulfilling the required update.
            if (this.parent.parent) {
                messaging.MessageLoop.sendMessage(this.parent.parent, Widget.Msg.FitRequest);
            }
            // If the dirty flag is still set, the parent was not resized.
            // Trigger the required update on the parent widget immediately.
            if (this._dirty) {
                messaging.MessageLoop.sendMessage(this.parent, Widget.Msg.UpdateRequest);
            }
        }
        /**
         * Update the layout position and size of the widgets.
         *
         * The parent offset dimensions should be `-1` if unknown.
         */
        _update(offsetWidth, offsetHeight) {
            // Clear the dirty flag to indicate the update occurred.
            this._dirty = false;
            // Compute the visible item count.
            let nVisible = 0;
            for (let i = 0, n = this._items.length; i < n; ++i) {
                nVisible += +!this._items[i].isHidden;
            }
            // Bail early if there are no visible items to layout.
            if (nVisible === 0) {
                return;
            }
            // Measure the parent if the offset dimensions are unknown.
            if (offsetWidth < 0) {
                offsetWidth = this.parent.node.offsetWidth;
            }
            if (offsetHeight < 0) {
                offsetHeight = this.parent.node.offsetHeight;
            }
            // Ensure the parent box sizing data is computed.
            if (!this._box) {
                this._box = domutils.ElementExt.boxSizing(this.parent.node);
            }
            // Compute the layout area adjusted for border and padding.
            let top = this._box.paddingTop;
            let left = this._box.paddingLeft;
            let width = offsetWidth - this._box.horizontalSum;
            let height = offsetHeight - this._box.verticalSum;
            // Distribute the layout space and adjust the start position.
            let delta;
            switch (this._direction) {
                case 'left-to-right':
                    delta = exports.BoxEngine.calc(this._sizers, Math.max(0, width - this._fixed));
                    break;
                case 'top-to-bottom':
                    delta = exports.BoxEngine.calc(this._sizers, Math.max(0, height - this._fixed));
                    break;
                case 'right-to-left':
                    delta = exports.BoxEngine.calc(this._sizers, Math.max(0, width - this._fixed));
                    left += width;
                    break;
                case 'bottom-to-top':
                    delta = exports.BoxEngine.calc(this._sizers, Math.max(0, height - this._fixed));
                    top += height;
                    break;
                default:
                    throw 'unreachable';
            }
            // Setup the variables for justification and alignment offset.
            let extra = 0;
            let offset = 0;
            // Account for alignment if there is extra layout space.
            if (delta > 0) {
                switch (this._alignment) {
                    case 'start':
                        break;
                    case 'center':
                        extra = 0;
                        offset = delta / 2;
                        break;
                    case 'end':
                        extra = 0;
                        offset = delta;
                        break;
                    case 'justify':
                        extra = delta / nVisible;
                        offset = 0;
                        break;
                    default:
                        throw 'unreachable';
                }
            }
            // Layout the items using the computed box sizes.
            for (let i = 0, n = this._items.length; i < n; ++i) {
                // Fetch the item.
                let item = this._items[i];
                // Ignore hidden items.
                if (item.isHidden) {
                    continue;
                }
                // Fetch the computed size for the widget.
                let size = this._sizers[i].size;
                // Update the widget geometry and advance the relevant edge.
                switch (this._direction) {
                    case 'left-to-right':
                        item.update(left + offset, top, size + extra, height);
                        left += size + extra + this._spacing;
                        break;
                    case 'top-to-bottom':
                        item.update(left, top + offset, width, size + extra);
                        top += size + extra + this._spacing;
                        break;
                    case 'right-to-left':
                        item.update(left - offset - size - extra, top, size + extra, height);
                        left -= size + extra + this._spacing;
                        break;
                    case 'bottom-to-top':
                        item.update(left, top - offset - size - extra, width, size + extra);
                        top -= size + extra + this._spacing;
                        break;
                    default:
                        throw 'unreachable';
                }
            }
        }
    }
    /**
     * The namespace for the `BoxLayout` class statics.
     */
    (function (BoxLayout) {
        /**
         * Get the box layout stretch factor for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The box layout stretch factor for the widget.
         */
        function getStretch(widget) {
            return Private$c.stretchProperty.get(widget);
        }
        BoxLayout.getStretch = getStretch;
        /**
         * Set the box layout stretch factor for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the stretch factor.
         */
        function setStretch(widget, value) {
            Private$c.stretchProperty.set(widget, value);
        }
        BoxLayout.setStretch = setStretch;
        /**
         * Get the box layout size basis for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The box layout size basis for the widget.
         */
        function getSizeBasis(widget) {
            return Private$c.sizeBasisProperty.get(widget);
        }
        BoxLayout.getSizeBasis = getSizeBasis;
        /**
         * Set the box layout size basis for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the size basis.
         */
        function setSizeBasis(widget, value) {
            Private$c.sizeBasisProperty.set(widget, value);
        }
        BoxLayout.setSizeBasis = setSizeBasis;
    })(BoxLayout || (BoxLayout = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$c;
    (function (Private) {
        /**
         * The property descriptor for a widget stretch factor.
         */
        Private.stretchProperty = new properties.AttachedProperty({
            name: 'stretch',
            create: () => 0,
            coerce: (owner, value) => Math.max(0, Math.floor(value)),
            changed: onChildSizingChanged
        });
        /**
         * The property descriptor for a widget size basis.
         */
        Private.sizeBasisProperty = new properties.AttachedProperty({
            name: 'sizeBasis',
            create: () => 0,
            coerce: (owner, value) => Math.max(0, Math.floor(value)),
            changed: onChildSizingChanged
        });
        /**
         * Test whether a direction has horizontal orientation.
         */
        function isHorizontal(dir) {
            return dir === 'left-to-right' || dir === 'right-to-left';
        }
        Private.isHorizontal = isHorizontal;
        /**
         * Clamp a spacing value to an integer >= 0.
         */
        function clampSpacing(value) {
            return Math.max(0, Math.floor(value));
        }
        Private.clampSpacing = clampSpacing;
        /**
         * The change handler for the attached sizing properties.
         */
        function onChildSizingChanged(child) {
            if (child.parent && child.parent.layout instanceof BoxLayout) {
                child.parent.fit();
            }
        }
    })(Private$c || (Private$c = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A panel which arranges its widgets in a single row or column.
     *
     * #### Notes
     * This class provides a convenience wrapper around a {@link BoxLayout}.
     */
    class BoxPanel extends Panel {
        /**
         * Construct a new box panel.
         *
         * @param options - The options for initializing the box panel.
         */
        constructor(options = {}) {
            super({ layout: Private$b.createLayout(options) });
            this.addClass('lm-BoxPanel');
        }
        /**
         * Get the layout direction for the box panel.
         */
        get direction() {
            return this.layout.direction;
        }
        /**
         * Set the layout direction for the box panel.
         */
        set direction(value) {
            this.layout.direction = value;
        }
        /**
         * Get the content alignment for the box panel.
         *
         * #### Notes
         * This is the alignment of the widgets in the layout direction.
         *
         * The alignment has no effect if the widgets can expand to fill the
         * entire box layout.
         */
        get alignment() {
            return this.layout.alignment;
        }
        /**
         * Set the content alignment for the box panel.
         *
         * #### Notes
         * This is the alignment of the widgets in the layout direction.
         *
         * The alignment has no effect if the widgets can expand to fill the
         * entire box layout.
         */
        set alignment(value) {
            this.layout.alignment = value;
        }
        /**
         * Get the inter-element spacing for the box panel.
         */
        get spacing() {
            return this.layout.spacing;
        }
        /**
         * Set the inter-element spacing for the box panel.
         */
        set spacing(value) {
            this.layout.spacing = value;
        }
        /**
         * A message handler invoked on a `'child-added'` message.
         */
        onChildAdded(msg) {
            msg.child.addClass('lm-BoxPanel-child');
        }
        /**
         * A message handler invoked on a `'child-removed'` message.
         */
        onChildRemoved(msg) {
            msg.child.removeClass('lm-BoxPanel-child');
        }
    }
    /**
     * The namespace for the `BoxPanel` class statics.
     */
    (function (BoxPanel) {
        /**
         * Get the box panel stretch factor for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The box panel stretch factor for the widget.
         */
        function getStretch(widget) {
            return BoxLayout.getStretch(widget);
        }
        BoxPanel.getStretch = getStretch;
        /**
         * Set the box panel stretch factor for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the stretch factor.
         */
        function setStretch(widget, value) {
            BoxLayout.setStretch(widget, value);
        }
        BoxPanel.setStretch = setStretch;
        /**
         * Get the box panel size basis for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The box panel size basis for the widget.
         */
        function getSizeBasis(widget) {
            return BoxLayout.getSizeBasis(widget);
        }
        BoxPanel.getSizeBasis = getSizeBasis;
        /**
         * Set the box panel size basis for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the size basis.
         */
        function setSizeBasis(widget, value) {
            BoxLayout.setSizeBasis(widget, value);
        }
        BoxPanel.setSizeBasis = setSizeBasis;
    })(BoxPanel || (BoxPanel = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$b;
    (function (Private) {
        /**
         * Create a box layout for the given panel options.
         */
        function createLayout(options) {
            return options.layout || new BoxLayout(options);
        }
        Private.createLayout = createLayout;
    })(Private$b || (Private$b = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A widget which displays command items as a searchable palette.
     */
    class CommandPalette extends Widget {
        /**
         * Construct a new command palette.
         *
         * @param options - The options for initializing the palette.
         */
        constructor(options) {
            super({ node: Private$a.createNode() });
            this._activeIndex = -1;
            this._itemTriggered = new signaling.Signal(this);
            this._items = [];
            this._results = null;
            this.addClass('lm-CommandPalette');
            this.setFlag(Widget.Flag.DisallowLayout);
            this.commands = options.commands;
            this.renderer = options.renderer || CommandPalette.defaultRenderer;
            this.commands.commandChanged.connect(this._onGenericChange, this);
            this.commands.keyBindingChanged.connect(this._onGenericChange, this);
        }
        /**
         * Dispose of the resources held by the widget.
         */
        dispose() {
            this._items.length = 0;
            this._results = null;
            super.dispose();
        }
        /**
         * A signal emitted when the user triggers a command item.
         *
         * #### Notes
         * This signal is emitted when an enabled item is clicked, or when
         * `Enter` is pressed while an enabled item is active. It is not
         * emitted for a header, or for a command which is executed by other
         * means, such as a key binding or a menu.
         *
         * The signal is emitted before the command is executed and before
         * the query text is cleared.
         */
        get itemTriggered() {
            return this._itemTriggered;
        }
        /**
         * The command palette search node.
         *
         * #### Notes
         * This is the node which contains the search-related elements.
         */
        get searchNode() {
            return this.node.getElementsByClassName('lm-CommandPalette-search')[0];
        }
        /**
         * The command palette input node.
         *
         * #### Notes
         * This is the actual input node for the search area.
         */
        get inputNode() {
            return this.node.getElementsByClassName('lm-CommandPalette-input')[0];
        }
        /**
         * The command palette content node.
         *
         * #### Notes
         * This is the node which holds the command item nodes.
         *
         * Modifying this node directly can lead to undefined behavior.
         */
        get contentNode() {
            return this.node.getElementsByClassName('lm-CommandPalette-content')[0];
        }
        /**
         * A read-only array of the command items in the palette.
         */
        get items() {
            return this._items;
        }
        /**
         * Add a command item to the command palette.
         *
         * @param options - The options for creating the command item.
         *
         * @returns The command item added to the palette.
         */
        addItem(options) {
            // Create a new command item for the options.
            let item = Private$a.createItem(this.commands, options);
            // Add the item to the array.
            this._items.push(item);
            // Refresh the search results.
            this.refresh();
            // Return the item added to the palette.
            return item;
        }
        /**
         * Adds command items to the command palette.
         *
         * @param items - An array of options for creating each command item.
         *
         * @returns The command items added to the palette.
         */
        addItems(items) {
            const newItems = items.map(item => Private$a.createItem(this.commands, item));
            newItems.forEach(item => this._items.push(item));
            this.refresh();
            return newItems;
        }
        /**
         * Remove an item from the command palette.
         *
         * @param item - The item to remove from the palette.
         *
         * #### Notes
         * This is a no-op if the item is not in the palette.
         */
        removeItem(item) {
            this.removeItemAt(this._items.indexOf(item));
        }
        /**
         * Remove the item at a given index from the command palette.
         *
         * @param index - The index of the item to remove.
         *
         * #### Notes
         * This is a no-op if the index is out of range.
         */
        removeItemAt(index) {
            // Remove the item from the array.
            let item = algorithm.ArrayExt.removeAt(this._items, index);
            // Bail if the index is out of range.
            if (!item) {
                return;
            }
            // Refresh the search results.
            this.refresh();
        }
        /**
         * Remove all items from the command palette.
         */
        clearItems() {
            // Bail if there is nothing to remove.
            if (this._items.length === 0) {
                return;
            }
            // Clear the array of items.
            this._items.length = 0;
            // Refresh the search results.
            this.refresh();
        }
        /**
         * Clear the search results and schedule an update.
         *
         * #### Notes
         * This should be called whenever the search results of the palette
         * should be updated.
         *
         * This is typically called automatically by the palette as needed,
         * but can be called manually if the input text is programatically
         * changed.
         *
         * The rendered results are updated asynchronously.
         */
        refresh() {
            this._results = null;
            if (this.inputNode.value !== '') {
                let clear = this.node.getElementsByClassName('lm-close-icon')[0];
                clear.style.display = 'inherit';
            }
            else {
                let clear = this.node.getElementsByClassName('lm-close-icon')[0];
                clear.style.display = 'none';
            }
            this.update();
        }
        /**
         * Handle the DOM events for the command palette.
         *
         * @param event - The DOM event sent to the command palette.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the command palette's DOM node.
         * It should not be called directly by user code.
         */
        handleEvent(event) {
            switch (event.type) {
                case 'click':
                    this._evtClick(event);
                    break;
                case 'keydown':
                    this._evtKeyDown(event);
                    break;
                case 'input':
                    this.refresh();
                    break;
                case 'focus':
                case 'blur':
                    this._toggleFocused();
                    break;
            }
        }
        /**
         * Create the search results for a query.
         *
         * @param query - The raw text of the search input.
         *
         * @returns The search results, in display order.
         *
         * #### Notes
         * The default implementation of this method returns the results of
         * `CommandPalette.search()` for the palette items.
         *
         * The palette renders every returned result, so the results should
         * not include items which are not visible. Triggering a header result
         * replaces the query with the header category.
         *
         * The results are cached until `refresh()` is called. A subclass
         * which uses external state should call `refresh()` when that state
         * changes.
         *
         * A subclass may reimplement this method as needed.
         */
        search(query) {
            return CommandPalette.search(this.items, query);
        }
        /**
         * Get the index of the result to activate for new search results.
         *
         * @param query - The raw text of the search input.
         *
         * @param results - The search results returned by `search()`.
         *
         * @returns The index of the result to activate, or `-1` for none.
         *
         * #### Notes
         * The default implementation of this method returns the index of the
         * first enabled item when the query is not empty, and `-1` otherwise.
         *
         * The returned index should refer to an enabled item result.
         *
         * A subclass may reimplement this method as needed.
         */
        initialActiveIndex(query, results) {
            return query ? algorithm.ArrayExt.findFirstIndex(results, Private$a.canActivate) : -1;
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            this.node.addEventListener('click', this);
            this.node.addEventListener('keydown', this);
            this.node.addEventListener('input', this);
            this.node.addEventListener('focus', this, true);
            this.node.addEventListener('blur', this, true);
        }
        /**
         * A message handler invoked on an `'after-detach'` message.
         */
        onAfterDetach(msg) {
            this.node.removeEventListener('click', this);
            this.node.removeEventListener('keydown', this);
            this.node.removeEventListener('input', this);
            this.node.removeEventListener('focus', this, true);
            this.node.removeEventListener('blur', this, true);
        }
        /**
         * A message handler invoked on an `'after-show'` message.
         */
        onAfterShow(msg) {
            this.update();
            super.onAfterShow(msg);
        }
        /**
         * A message handler invoked on an `'activate-request'` message.
         */
        onActivateRequest(msg) {
            if (this.isAttached) {
                let input = this.inputNode;
                input.focus();
                input.select();
            }
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            if (!this.isVisible) {
                // Ensure to clear the content if the widget is hidden
                virtualdom.VirtualDOM.render(null, this.contentNode);
                return;
            }
            // Fetch the current query text and content node.
            let query = this.inputNode.value;
            let contentNode = this.contentNode;
            // Ensure the search results are generated.
            let results = this._results;
            if (!results) {
                // Generate and store the new search results.
                results = this._results = this.search(query);
                // Reset the active index.
                this._activeIndex = this.initialActiveIndex(query, results);
            }
            // If there is no query and no results, clear the content.
            if (!query && results.length === 0) {
                virtualdom.VirtualDOM.render(null, contentNode);
                return;
            }
            // If the is a query but no results, render the empty message.
            if (query && results.length === 0) {
                let content = this.renderer.renderEmptyMessage({ query });
                virtualdom.VirtualDOM.render(content, contentNode);
                return;
            }
            // Create the render content for the search results.
            let renderer = this.renderer;
            let activeIndex = this._activeIndex;
            let content = new Array(results.length);
            for (let i = 0, n = results.length; i < n; ++i) {
                let result = results[i];
                if (result.type === 'header') {
                    let indices = result.indices;
                    let category = result.category;
                    content[i] = renderer.renderHeader({ category, indices });
                }
                else {
                    let item = result.item;
                    let indices = result.indices;
                    let active = i === activeIndex;
                    content[i] = renderer.renderItem({ item, indices, active });
                }
            }
            // Render the search result content.
            virtualdom.VirtualDOM.render(content, contentNode);
            // Adjust the scroll position as needed.
            if (activeIndex < 0 || activeIndex >= results.length) {
                contentNode.scrollTop = 0;
            }
            else {
                let element = contentNode.children[activeIndex];
                domutils.ElementExt.scrollIntoViewIfNeeded(contentNode, element);
            }
        }
        /**
         * Handle the `'click'` event for the command palette.
         */
        _evtClick(event) {
            // Bail if the click is not the left button.
            if (event.button !== 0) {
                return;
            }
            // Clear input if the target is clear button
            if (event.target.classList.contains('lm-close-icon')) {
                this.inputNode.value = '';
                this.refresh();
                return;
            }
            // Find the index of the item which was clicked.
            let index = algorithm.ArrayExt.findFirstIndex(this.contentNode.children, node => {
                return node.contains(event.target);
            });
            // Bail if the click was not on an item.
            if (index === -1) {
                return;
            }
            // Kill the event when a content item is clicked.
            event.preventDefault();
            event.stopPropagation();
            // Execute the item if possible.
            this._execute(index);
        }
        /**
         * Handle the `'keydown'` event for the command palette.
         */
        _evtKeyDown(event) {
            if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
                return;
            }
            switch (event.keyCode) {
                case 13: // Enter
                    event.preventDefault();
                    event.stopPropagation();
                    this._execute(this._activeIndex);
                    break;
                case 38: // Up Arrow
                    event.preventDefault();
                    event.stopPropagation();
                    this._activatePreviousItem();
                    break;
                case 40: // Down Arrow
                    event.preventDefault();
                    event.stopPropagation();
                    this._activateNextItem();
                    break;
            }
        }
        /**
         * Activate the next enabled command item.
         */
        _activateNextItem() {
            // Bail if there are no search results.
            if (!this._results || this._results.length === 0) {
                return;
            }
            // Find the next enabled item index.
            let ai = this._activeIndex;
            let n = this._results.length;
            let start = ai < n - 1 ? ai + 1 : 0;
            let stop = start === 0 ? n - 1 : start - 1;
            this._activeIndex = algorithm.ArrayExt.findFirstIndex(this._results, Private$a.canActivate, start, stop);
            // Schedule an update of the items.
            this.update();
        }
        /**
         * Activate the previous enabled command item.
         */
        _activatePreviousItem() {
            // Bail if there are no search results.
            if (!this._results || this._results.length === 0) {
                return;
            }
            // Find the previous enabled item index.
            let ai = this._activeIndex;
            let n = this._results.length;
            let start = ai <= 0 ? n - 1 : ai - 1;
            let stop = start === n - 1 ? 0 : start + 1;
            this._activeIndex = algorithm.ArrayExt.findLastIndex(this._results, Private$a.canActivate, start, stop);
            // Schedule an update of the items.
            this.update();
        }
        /**
         * Execute the command item at the given index, if possible.
         */
        _execute(index) {
            // Bail if there are no search results.
            if (!this._results) {
                return;
            }
            // Bail if the index is out of range.
            let part = this._results[index];
            if (!part) {
                return;
            }
            // Update the search text if the item is a header.
            if (part.type === 'header') {
                let input = this.inputNode;
                input.value = `${part.category.toLowerCase()} `;
                input.focus();
                this.refresh();
                return;
            }
            // Bail if item is not enabled.
            if (!part.item.isEnabled) {
                return;
            }
            // Emit the item triggered signal before the execution.
            this._itemTriggered.emit(part.item);
            // Execute the item.
            this.commands.execute(part.item.command, part.item.args);
            // Clear the query text.
            this.inputNode.value = '';
            // Refresh the search results.
            this.refresh();
        }
        /**
         * Toggle the focused modifier based on the input node focus state.
         */
        _toggleFocused() {
            let focused = document.activeElement === this.inputNode;
            this.toggleClass('lm-mod-focused', focused);
        }
        /**
         * A signal handler for generic command changes.
         */
        _onGenericChange() {
            this.refresh();
        }
    }
    /**
     * The namespace for the `CommandPalette` class statics.
     */
    (function (CommandPalette) {
        /**
         * The default implementation of `IRenderer`.
         */
        class Renderer {
            /**
             * Render the virtual element for a command palette header.
             *
             * @param data - The data to use for rendering the header.
             *
             * @returns A virtual element representing the header.
             */
            renderHeader(data) {
                let content = this.formatHeader(data);
                return virtualdom.h.li({ className: 'lm-CommandPalette-header' }, content);
            }
            /**
             * Render the virtual element for a command palette item.
             *
             * @param data - The data to use for rendering the item.
             *
             * @returns A virtual element representing the item.
             */
            renderItem(data) {
                let className = this.createItemClass(data);
                let dataset = this.createItemDataset(data);
                if (data.item.isToggleable) {
                    return virtualdom.h.li({
                        className,
                        dataset,
                        role: 'menuitemcheckbox',
                        'aria-checked': `${data.item.isToggled}`
                    }, this.renderItemIcon(data), this.renderItemContent(data), this.renderItemShortcut(data));
                }
                return virtualdom.h.li({
                    className,
                    dataset,
                    role: 'menuitem'
                }, this.renderItemIcon(data), this.renderItemContent(data), this.renderItemShortcut(data));
            }
            /**
             * Render the empty results message for a command palette.
             *
             * @param data - The data to use for rendering the message.
             *
             * @returns A virtual element representing the message.
             */
            renderEmptyMessage(data) {
                let content = this.formatEmptyMessage(data);
                return virtualdom.h.li({ className: 'lm-CommandPalette-emptyMessage' }, content);
            }
            /**
             * Render the icon for a command palette item.
             *
             * @param data - The data to use for rendering the icon.
             *
             * @returns A virtual element representing the icon.
             */
            renderItemIcon(data) {
                let className = this.createIconClass(data);
                // If data.item.icon is undefined, it will be ignored.
                return virtualdom.h.div({ className }, data.item.icon, data.item.iconLabel);
            }
            /**
             * Render the content for a command palette item.
             *
             * @param data - The data to use for rendering the content.
             *
             * @returns A virtual element representing the content.
             */
            renderItemContent(data) {
                return virtualdom.h.div({ className: 'lm-CommandPalette-itemContent' }, this.renderItemLabel(data), this.renderItemCaption(data));
            }
            /**
             * Render the label for a command palette item.
             *
             * @param data - The data to use for rendering the label.
             *
             * @returns A virtual element representing the label.
             */
            renderItemLabel(data) {
                let content = this.formatItemLabel(data);
                return virtualdom.h.div({ className: 'lm-CommandPalette-itemLabel' }, content);
            }
            /**
             * Render the caption for a command palette item.
             *
             * @param data - The data to use for rendering the caption.
             *
             * @returns A virtual element representing the caption.
             */
            renderItemCaption(data) {
                let content = this.formatItemCaption(data);
                return virtualdom.h.div({ className: 'lm-CommandPalette-itemCaption' }, content);
            }
            /**
             * Render the shortcut for a command palette item.
             *
             * @param data - The data to use for rendering the shortcut.
             *
             * @returns A virtual element representing the shortcut.
             */
            renderItemShortcut(data) {
                let content = this.formatItemShortcut(data);
                return virtualdom.h.div({ className: 'lm-CommandPalette-itemShortcut' }, content);
            }
            /**
             * Create the class name for the command palette item.
             *
             * @param data - The data to use for the class name.
             *
             * @returns The full class name for the command palette item.
             */
            createItemClass(data) {
                // Set up the initial class name.
                let name = 'lm-CommandPalette-item';
                // Add the boolean state classes.
                if (!data.item.isEnabled) {
                    name += ' lm-mod-disabled';
                }
                if (data.item.isToggled) {
                    name += ' lm-mod-toggled';
                }
                if (data.active) {
                    name += ' lm-mod-active';
                }
                // Add the extra class.
                let extra = data.item.className;
                if (extra) {
                    name += ` ${extra}`;
                }
                // Return the complete class name.
                return name;
            }
            /**
             * Create the dataset for the command palette item.
             *
             * @param data - The data to use for creating the dataset.
             *
             * @returns The dataset for the command palette item.
             */
            createItemDataset(data) {
                return { ...data.item.dataset, command: data.item.command };
            }
            /**
             * Create the class name for the command item icon.
             *
             * @param data - The data to use for the class name.
             *
             * @returns The full class name for the item icon.
             */
            createIconClass(data) {
                let name = 'lm-CommandPalette-itemIcon';
                let extra = data.item.iconClass;
                return extra ? `${name} ${extra}` : name;
            }
            /**
             * Create the render content for the header node.
             *
             * @param data - The data to use for the header content.
             *
             * @returns The content to add to the header node.
             */
            formatHeader(data) {
                if (!data.indices || data.indices.length === 0) {
                    return data.category;
                }
                return algorithm.StringExt.highlight(data.category, data.indices, virtualdom.h.mark);
            }
            /**
             * Create the render content for the empty message node.
             *
             * @param data - The data to use for the empty message content.
             *
             * @returns The content to add to the empty message node.
             */
            formatEmptyMessage(data) {
                return `No commands found that match '${data.query}'`;
            }
            /**
             * Create the render content for the item shortcut node.
             *
             * @param data - The data to use for the shortcut content.
             *
             * @returns The content to add to the shortcut node.
             */
            formatItemShortcut(data) {
                let kb = data.item.keyBinding;
                return kb ? commands.CommandRegistry.formatKeystroke(kb.keys) : null;
            }
            /**
             * Create the render content for the item label node.
             *
             * @param data - The data to use for the label content.
             *
             * @returns The content to add to the label node.
             */
            formatItemLabel(data) {
                if (!data.indices || data.indices.length === 0) {
                    return data.item.label;
                }
                return algorithm.StringExt.highlight(data.item.label, data.indices, virtualdom.h.mark);
            }
            /**
             * Create the render content for the item caption node.
             *
             * @param data - The data to use for the caption content.
             *
             * @returns The content to add to the caption node.
             */
            formatItemCaption(data) {
                return data.item.caption;
            }
        }
        CommandPalette.Renderer = Renderer;
        /**
         * The default `Renderer` instance.
         */
        CommandPalette.defaultRenderer = new Renderer();
        /**
         * Search an array of command items for fuzzy matches.
         *
         * @param items - The command items to search.
         *
         * @param query - The query text to match against the items.
         *
         * @returns The search results for the query.
         *
         * #### Notes
         * Items which are not visible are excluded. Whitespace in the query
         * is ignored. An empty query matches all items, ordered by category,
         * rank, and label. Otherwise, the matched items are ordered by match
         * quality.
         *
         * Each run of items which share a category is preceded by a header
         * result for that category.
         *
         * This function is used by the default implementation of the protected
         * `search()` method of a command palette. A subclass can use it to
         * compose the default results with custom results.
         */
        function search(items, query) {
            return Private$a.search(items, query);
        }
        CommandPalette.search = search;
    })(CommandPalette || (CommandPalette = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$a;
    (function (Private) {
        /**
         * Create the DOM node for a command palette.
         */
        function createNode() {
            let node = document.createElement('div');
            let search = document.createElement('div');
            let wrapper = document.createElement('div');
            let input = document.createElement('input');
            let content = document.createElement('ul');
            let clear = document.createElement('button');
            search.className = 'lm-CommandPalette-search';
            wrapper.className = 'lm-CommandPalette-wrapper';
            input.className = 'lm-CommandPalette-input';
            clear.className = 'lm-close-icon';
            content.className = 'lm-CommandPalette-content';
            content.setAttribute('role', 'menu');
            input.spellcheck = false;
            wrapper.appendChild(input);
            wrapper.appendChild(clear);
            search.appendChild(wrapper);
            node.appendChild(search);
            node.appendChild(content);
            return node;
        }
        Private.createNode = createNode;
        /**
         * Create a new command item from a command registry and options.
         */
        function createItem(commands, options) {
            return new CommandItem(commands, options);
        }
        Private.createItem = createItem;
        /**
         * Search an array of command items for fuzzy matches.
         */
        function search(items, query) {
            // Fuzzy match the items for the query.
            let scores = matchItems(items, query);
            // Sort the items based on their score.
            scores.sort(scoreCmp);
            // Create the results for the search.
            return createResults(scores);
        }
        Private.search = search;
        /**
         * Test whether a result item can be activated.
         */
        function canActivate(result) {
            return result.type === 'item' && result.item.isEnabled;
        }
        Private.canActivate = canActivate;
        /**
         * Normalize a category for a command item.
         */
        function normalizeCategory(category) {
            return category.trim().replace(/\s+/g, ' ');
        }
        /**
         * Normalize the query text for a fuzzy search.
         */
        function normalizeQuery(text) {
            return text.replace(/\s+/g, '').toLowerCase();
        }
        /**
         * Perform a fuzzy match on an array of command items.
         */
        function matchItems(items, query) {
            // Normalize the query text to lower case with no whitespace.
            query = normalizeQuery(query);
            // Create the array to hold the scores.
            let scores = [];
            // Iterate over the items and match against the query.
            for (let i = 0, n = items.length; i < n; ++i) {
                // Ignore items which are not visible.
                let item = items[i];
                if (!item.isVisible) {
                    continue;
                }
                // If the query is empty, all items are matched by default.
                if (!query) {
                    scores.push({
                        matchType: 3 /* MatchType.Default */,
                        categoryIndices: null,
                        labelIndices: null,
                        score: 0,
                        item
                    });
                    continue;
                }
                // Run the fuzzy search for the item and query.
                let score = fuzzySearch(item, query);
                // Ignore the item if it is not a match.
                if (!score) {
                    continue;
                }
                // Penalize disabled items.
                // TODO - push disabled items all the way down in sort cmp?
                if (!item.isEnabled) {
                    score.score += 1000;
                }
                // Add the score to the results.
                scores.push(score);
            }
            // Return the final array of scores.
            return scores;
        }
        /**
         * Perform a fuzzy search on a single command item.
         */
        function fuzzySearch(item, query) {
            // Create the source text to be searched.
            let category = item.category.toLowerCase();
            let label = item.label.toLowerCase();
            let source = `${category} ${label}`;
            // Set up the match score and indices array.
            let score = Infinity;
            let indices = null;
            // The regex for search word boundaries
            let rgx = /\b\w/g;
            // Search the source by word boundary.
            // eslint-disable-next-line no-constant-condition
            while (true) {
                // Find the next word boundary in the source.
                let rgxMatch = rgx.exec(source);
                // Break if there is no more source context.
                if (!rgxMatch) {
                    break;
                }
                // Run the string match on the relevant substring.
                let match = algorithm.StringExt.matchSumOfDeltas(source, query, rgxMatch.index);
                // Break if there is no match.
                if (!match) {
                    break;
                }
                // Update the match if the score is better.
                if (match.score <= score) {
                    score = match.score;
                    indices = match.indices;
                }
            }
            // Bail if there was no match.
            if (!indices || score === Infinity) {
                return null;
            }
            // Compute the pivot index between category and label text.
            let pivot = category.length + 1;
            // Find the slice index to separate matched indices.
            let j = algorithm.ArrayExt.lowerBound(indices, pivot, (a, b) => a - b);
            // Extract the matched category and label indices.
            let categoryIndices = indices.slice(0, j);
            let labelIndices = indices.slice(j);
            // Adjust the label indices for the pivot offset.
            for (let i = 0, n = labelIndices.length; i < n; ++i) {
                labelIndices[i] -= pivot;
            }
            // Handle a pure label match.
            if (categoryIndices.length === 0) {
                return {
                    matchType: 0 /* MatchType.Label */,
                    categoryIndices: null,
                    labelIndices,
                    score,
                    item
                };
            }
            // Handle a pure category match.
            if (labelIndices.length === 0) {
                return {
                    matchType: 1 /* MatchType.Category */,
                    categoryIndices,
                    labelIndices: null,
                    score,
                    item
                };
            }
            // Handle a split match.
            return {
                matchType: 2 /* MatchType.Split */,
                categoryIndices,
                labelIndices,
                score,
                item
            };
        }
        /**
         * A sort comparison function for a match score.
         */
        function scoreCmp(a, b) {
            // First compare based on the match type
            let m1 = a.matchType - b.matchType;
            if (m1 !== 0) {
                return m1;
            }
            // Otherwise, compare based on the match score.
            let d1 = a.score - b.score;
            if (d1 !== 0) {
                return d1;
            }
            // Find the match index based on the match type.
            let i1 = 0;
            let i2 = 0;
            switch (a.matchType) {
                case 0 /* MatchType.Label */:
                    i1 = a.labelIndices[0];
                    i2 = b.labelIndices[0];
                    break;
                case 1 /* MatchType.Category */:
                case 2 /* MatchType.Split */:
                    i1 = a.categoryIndices[0];
                    i2 = b.categoryIndices[0];
                    break;
            }
            // Compare based on the match index.
            if (i1 !== i2) {
                return i1 - i2;
            }
            // Otherwise, compare by category.
            let d2 = a.item.category.localeCompare(b.item.category);
            if (d2 !== 0) {
                return d2;
            }
            // Otherwise, compare by rank.
            let r1 = a.item.rank;
            let r2 = b.item.rank;
            if (r1 !== r2) {
                return r1 < r2 ? -1 : 1; // Infinity safe
            }
            // Finally, compare by label.
            return a.item.label.localeCompare(b.item.label);
        }
        /**
         * Create the results from an array of sorted scores.
         */
        function createResults(scores) {
            // Set up the search results array.
            let results = [];
            // Iterate over each score in the array.
            for (let i = 0, n = scores.length; i < n; ++i) {
                // Extract the current item and indices.
                let { item, categoryIndices, labelIndices } = scores[i];
                // Extract the category for the current item.
                let category = item.category;
                // Is this the same category as the preceding result?
                if (i === 0 || category !== scores[i - 1].item.category) {
                    // Add the header result for the category.
                    results.push({ type: 'header', category, indices: categoryIndices });
                }
                // Create the item result for the score.
                results.push({ type: 'item', item, indices: labelIndices });
            }
            // Return the final results.
            return results;
        }
        /**
         * A concrete implementation of `CommandPalette.IItem`.
         */
        class CommandItem {
            /**
             * Construct a new command item.
             */
            constructor(commands, options) {
                this._commands = commands;
                this.category = normalizeCategory(options.category);
                this.command = options.command;
                this.args = options.args || coreutils.JSONExt.emptyObject;
                this.rank = options.rank !== undefined ? options.rank : Infinity;
            }
            /**
             * The display label for the command item.
             */
            get label() {
                return this._commands.label(this.command, this.args);
            }
            /**
             * The icon renderer for the command item.
             */
            get icon() {
                return this._commands.icon(this.command, this.args);
            }
            /**
             * The icon class for the command item.
             */
            get iconClass() {
                return this._commands.iconClass(this.command, this.args);
            }
            /**
             * The icon label for the command item.
             */
            get iconLabel() {
                return this._commands.iconLabel(this.command, this.args);
            }
            /**
             * The display caption for the command item.
             */
            get caption() {
                return this._commands.caption(this.command, this.args);
            }
            /**
             * The extra class name for the command item.
             */
            get className() {
                return this._commands.className(this.command, this.args);
            }
            /**
             * The dataset for the command item.
             */
            get dataset() {
                return this._commands.dataset(this.command, this.args);
            }
            /**
             * Whether the command item is enabled.
             */
            get isEnabled() {
                return this._commands.isEnabled(this.command, this.args);
            }
            /**
             * Whether the command item is toggled.
             */
            get isToggled() {
                return this._commands.isToggled(this.command, this.args);
            }
            /**
             * Whether the command item is toggleable.
             */
            get isToggleable() {
                return this._commands.isToggleable(this.command, this.args);
            }
            /**
             * Whether the command item is visible.
             */
            get isVisible() {
                return this._commands.isVisible(this.command, this.args);
            }
            /**
             * The key binding for the command item.
             */
            get keyBinding() {
                let { command, args } = this;
                return (algorithm.ArrayExt.findLastValue(this._commands.keyBindings, kb => {
                    return kb.command === command && coreutils.JSONExt.deepEqual(kb.args, args);
                }) || null);
            }
        }
    })(Private$a || (Private$a = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A widget which displays items as a canonical menu.
     */
    class Menu extends Widget {
        /**
         * Construct a new menu.
         *
         * @param options - The options for initializing the menu.
         */
        constructor(options) {
            super({ node: Private$9.createNode() });
            this._childIndex = -1;
            this._activeIndex = -1;
            this._openTimerID = 0;
            this._closeTimerID = 0;
            this._pendingTouchActivation = false;
            this._items = [];
            this._childMenu = null;
            this._parentMenu = null;
            this._aboutToClose = new signaling.Signal(this);
            this._menuRequested = new signaling.Signal(this);
            this.addClass('lm-Menu');
            this.setFlag(Widget.Flag.DisallowLayout);
            this.commands = options.commands;
            this.renderer = options.renderer || Menu.defaultRenderer;
        }
        /**
         * Dispose of the resources held by the menu.
         */
        dispose() {
            this.close();
            this._items.length = 0;
            super.dispose();
        }
        /**
         * A signal emitted just before the menu is closed.
         *
         * #### Notes
         * This signal is emitted when the menu receives a `'close-request'`
         * message, just before it removes itself from the DOM.
         *
         * This signal is not emitted if the menu is already detached from
         * the DOM when it receives the `'close-request'` message.
         */
        get aboutToClose() {
            return this._aboutToClose;
        }
        /**
         * A signal emitted when a new menu is requested by the user.
         *
         * #### Notes
         * This signal is emitted whenever the user presses the right or left
         * arrow keys, and a submenu cannot be opened or closed in response.
         *
         * This signal is useful when implementing menu bars in order to open
         * the next or previous menu in response to a user key press.
         *
         * This signal is only emitted for the root menu in a hierarchy.
         */
        get menuRequested() {
            return this._menuRequested;
        }
        /**
         * The parent menu of the menu.
         *
         * #### Notes
         * This is `null` unless the menu is an open submenu.
         */
        get parentMenu() {
            return this._parentMenu;
        }
        /**
         * The child menu of the menu.
         *
         * #### Notes
         * This is `null` unless the menu has an open submenu.
         */
        get childMenu() {
            return this._childMenu;
        }
        /**
         * The root menu of the menu hierarchy.
         */
        get rootMenu() {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            let menu = this;
            while (menu._parentMenu) {
                menu = menu._parentMenu;
            }
            return menu;
        }
        /**
         * The leaf menu of the menu hierarchy.
         */
        get leafMenu() {
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            let menu = this;
            while (menu._childMenu) {
                menu = menu._childMenu;
            }
            return menu;
        }
        /**
         * The menu content node.
         *
         * #### Notes
         * This is the node which holds the menu item nodes.
         *
         * Modifying this node directly can lead to undefined behavior.
         */
        get contentNode() {
            return this.node.getElementsByClassName('lm-Menu-content')[0];
        }
        /**
         * Get the currently active menu item.
         */
        get activeItem() {
            return this._items[this._activeIndex] || null;
        }
        /**
         * Set the currently active menu item.
         *
         * #### Notes
         * If the item cannot be activated, the item will be set to `null`.
         */
        set activeItem(value) {
            this.activeIndex = value ? this._items.indexOf(value) : -1;
        }
        /**
         * Get the index of the currently active menu item.
         *
         * #### Notes
         * This will be `-1` if no menu item is active.
         */
        get activeIndex() {
            return this._activeIndex;
        }
        /**
         * Set the index of the currently active menu item.
         *
         * #### Notes
         * If the item cannot be activated, the index will be set to `-1`.
         */
        set activeIndex(value) {
            // Adjust the value for an out of range index.
            if (value < 0 || value >= this._items.length) {
                value = -1;
            }
            // Ensure the item can be activated.
            if (value !== -1 && !Private$9.canActivate(this._items[value])) {
                value = -1;
            }
            // Bail if the index will not change.
            if (this._activeIndex === value) {
                return;
            }
            // Update the active index.
            this._activeIndex = value;
            // Make active element in focus
            if (this._activeIndex >= 0 &&
                this.contentNode.childNodes[this._activeIndex]) {
                this.contentNode.childNodes[this._activeIndex].focus();
            }
            // schedule an update of the items.
            this.update();
        }
        /**
         * A read-only array of the menu items in the menu.
         */
        get items() {
            return this._items;
        }
        /**
         * Activate the next selectable item in the menu.
         *
         * #### Notes
         * If no item is selectable, the index will be set to `-1`.
         */
        activateNextItem() {
            let n = this._items.length;
            let ai = this._activeIndex;
            let start = ai < n - 1 ? ai + 1 : 0;
            let stop = start === 0 ? n - 1 : start - 1;
            this.activeIndex = algorithm.ArrayExt.findFirstIndex(this._items, Private$9.canActivate, start, stop);
        }
        /**
         * Activate the previous selectable item in the menu.
         *
         * #### Notes
         * If no item is selectable, the index will be set to `-1`.
         */
        activatePreviousItem() {
            let n = this._items.length;
            let ai = this._activeIndex;
            let start = ai <= 0 ? n - 1 : ai - 1;
            let stop = start === n - 1 ? 0 : start + 1;
            this.activeIndex = algorithm.ArrayExt.findLastIndex(this._items, Private$9.canActivate, start, stop);
        }
        /**
         * Trigger the active menu item.
         *
         * #### Notes
         * If the active item is a submenu, it will be opened and the first
         * item will be activated.
         *
         * If the active item is a command, the command will be executed.
         *
         * If the menu is not attached, this is a no-op.
         *
         * If there is no active item, this is a no-op.
         */
        triggerActiveItem() {
            // Bail if the menu is not attached.
            if (!this.isAttached) {
                return;
            }
            // Bail if there is no active item.
            let item = this.activeItem;
            if (!item) {
                return;
            }
            // Cancel the pending timers.
            this._cancelOpenTimer();
            this._cancelCloseTimer();
            // If the item is a submenu, open it.
            if (item.type === 'submenu') {
                this._openChildMenu(true);
                return;
            }
            // Close the root menu before executing the command.
            this.rootMenu.close();
            // Execute the command for the item.
            let { command, args } = item;
            if (this.commands.isEnabled(command, args)) {
                this.commands.execute(command, args);
            }
            else {
                console.log(`Command '${command}' is disabled.`);
            }
        }
        /**
         * Add a menu item to the end of the menu.
         *
         * @param options - The options for creating the menu item.
         *
         * @returns The menu item added to the menu.
         */
        addItem(options) {
            return this.insertItem(this._items.length, options);
        }
        /**
         * Insert a menu item into the menu at the specified index.
         *
         * @param index - The index at which to insert the item.
         *
         * @param options - The options for creating the menu item.
         *
         * @returns The menu item added to the menu.
         *
         * #### Notes
         * The index will be clamped to the bounds of the items.
         */
        insertItem(index, options) {
            // Close the menu if it's attached.
            if (this.isAttached) {
                this.close();
            }
            // Reset the active index.
            this.activeIndex = -1;
            // Clamp the insert index to the array bounds.
            let i = Math.max(0, Math.min(index, this._items.length));
            // Create the item for the options.
            let item = Private$9.createItem(this, options);
            // Insert the item into the array.
            algorithm.ArrayExt.insert(this._items, i, item);
            // Schedule an update of the items.
            this.update();
            // Return the item added to the menu.
            return item;
        }
        /**
         * Remove an item from the menu.
         *
         * @param item - The item to remove from the menu.
         *
         * #### Notes
         * This is a no-op if the item is not in the menu.
         */
        removeItem(item) {
            let index = this._items.indexOf(item);
            if (index === -1) {
                return;
            }
            this.removeItemAt(index);
        }
        /**
         * Remove the item at a given index from the menu.
         *
         * @param index - The index of the item to remove.
         *
         * #### Notes
         * This is a no-op if the index is out of range.
         */
        removeItemAt(index) {
            // Close the menu if it's attached.
            if (this.isAttached) {
                this.close();
            }
            // Reset the active index.
            this.activeIndex = -1;
            // Remove the item from the array.
            let item = algorithm.ArrayExt.removeAt(this._items, index);
            // Bail if the index is out of range.
            if (!item) {
                return;
            }
            // Schedule an update of the items.
            this.update();
        }
        /**
         * Remove all menu items from the menu.
         */
        clearItems() {
            // Close the menu if it's attached.
            if (this.isAttached) {
                this.close();
            }
            // Reset the active index.
            this.activeIndex = -1;
            // Bail if there is nothing to remove.
            if (this._items.length === 0) {
                return;
            }
            // Clear the items.
            this._items.length = 0;
            // Schedule an update of the items.
            this.update();
        }
        /**
         * Open the menu at the specified location.
         *
         * @param x - The client X coordinate of the menu location.
         *
         * @param y - The client Y coordinate of the menu location.
         *
         * @param options - The additional options for opening the menu.
         *
         * #### Notes
         * The menu will be opened at the given location unless it will not
         * fully fit on the screen. If it will not fit, it will be adjusted
         * to fit naturally on the screen.
         *
         * The menu will be attached under the `host` element in the DOM
         * (or `document.body` if `host` is `null`) and before the `ref`
         * element (or as the last child of `host` if `ref` is `null`).
         * The menu may be displayed outside of the `host` element
         * following the rules of CSS absolute positioning.
         *
         * This is a no-op if the menu is already attached to the DOM.
         */
        open(x, y, options = {}) {
            var _a, _b, _c;
            // Bail early if the menu is already attached.
            if (this.isAttached) {
                return;
            }
            // Extract the menu options.
            let forceX = options.forceX || false;
            let forceY = options.forceY || false;
            const host = (_a = options.host) !== null && _a !== void 0 ? _a : null;
            const ref = (_b = options.ref) !== null && _b !== void 0 ? _b : null;
            const horizontalAlignment = (_c = options.horizontalAlignment) !== null && _c !== void 0 ? _c : (document.documentElement.dir === 'rtl' ? 'right' : 'left');
            // Open the menu as a root menu.
            Private$9.openRootMenu(this, x, y, forceX, forceY, horizontalAlignment, host, ref);
            // Activate the menu to accept keyboard input.
            this.activate();
        }
        /**
         * Handle the DOM events for the menu.
         *
         * @param event - The DOM event sent to the menu.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the menu's DOM nodes. It should
         * not be called directly by user code.
         */
        handleEvent(event) {
            switch (event.type) {
                case 'keydown':
                    this._evtKeyDown(event);
                    break;
                case 'click':
                    this._evtClick(event);
                    break;
                case 'pointerup':
                    this._evtPointerUp(event);
                    break;
                case 'pointermove':
                    this._evtPointerMove(event);
                    break;
                case 'pointerenter':
                    this._evtPointerEnter(event);
                    break;
                case 'pointerleave':
                    this._evtPointerLeave(event);
                    break;
                case 'pointerdown':
                    this._evtPointerDown(event);
                    break;
                case 'contextmenu':
                    event.preventDefault();
                    event.stopPropagation();
                    break;
            }
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            this.node.addEventListener('keydown', this);
            this.node.addEventListener('click', this);
            this.node.addEventListener('pointerup', this);
            this.node.addEventListener('pointermove', this);
            this.node.addEventListener('pointerenter', this);
            this.node.addEventListener('pointerleave', this);
            this.node.addEventListener('contextmenu', this);
            document.addEventListener('pointerdown', this, true);
        }
        /**
         * A message handler invoked on an `'after-detach'` message.
         */
        onAfterDetach(msg) {
            this.node.removeEventListener('keydown', this);
            this.node.removeEventListener('click', this);
            this.node.removeEventListener('pointerup', this);
            this.node.removeEventListener('pointermove', this);
            this.node.removeEventListener('pointerenter', this);
            this.node.removeEventListener('pointerleave', this);
            this.node.removeEventListener('contextmenu', this);
            document.removeEventListener('pointerdown', this, true);
            this._pendingTouchActivation = false;
        }
        /**
         * A message handler invoked on an `'activate-request'` message.
         */
        onActivateRequest(msg) {
            if (this.isAttached) {
                this.node.focus();
            }
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            let items = this._items;
            let renderer = this.renderer;
            let activeIndex = this._activeIndex;
            let collapsedFlags = Private$9.computeCollapsed(items);
            let content = new Array(items.length);
            for (let i = 0, n = items.length; i < n; ++i) {
                let item = items[i];
                let active = i === activeIndex;
                let collapsed = collapsedFlags[i];
                content[i] = renderer.renderItem({
                    item,
                    active,
                    collapsed,
                    onfocus: () => {
                        this.activeIndex = i;
                    }
                });
            }
            virtualdom.VirtualDOM.render(content, this.contentNode);
        }
        /**
         * A message handler invoked on a `'close-request'` message.
         */
        onCloseRequest(msg) {
            // Cancel the pending timers.
            this._cancelOpenTimer();
            this._cancelCloseTimer();
            // Reset the active index.
            this.activeIndex = -1;
            this._pendingTouchActivation = false;
            // Close any open child menu.
            let childMenu = this._childMenu;
            if (childMenu) {
                this._childIndex = -1;
                this._childMenu = null;
                childMenu._parentMenu = null;
                childMenu.close();
            }
            // Remove this menu from its parent and activate the parent.
            let parentMenu = this._parentMenu;
            if (parentMenu) {
                this._parentMenu = null;
                parentMenu._childIndex = -1;
                parentMenu._childMenu = null;
                parentMenu.activate();
            }
            // Emit the `aboutToClose` signal if the menu is attached.
            if (this.isAttached) {
                this._aboutToClose.emit(undefined);
            }
            // Finish closing the menu.
            super.onCloseRequest(msg);
        }
        /**
         * Handle the `'keydown'` event for the menu.
         *
         * #### Notes
         * This listener is attached to the menu node.
         */
        _evtKeyDown(event) {
            // A menu handles all keydown events.
            event.preventDefault();
            event.stopPropagation();
            // Fetch the key code for the event.
            let kc = event.keyCode;
            // Enter
            if (kc === 13) {
                this.triggerActiveItem();
                return;
            }
            // Escape
            if (kc === 27) {
                this.close();
                return;
            }
            // Left Arrow
            if (kc === 37) {
                if (this._parentMenu) {
                    this.close();
                }
                else {
                    this._menuRequested.emit('previous');
                }
                return;
            }
            // Up Arrow
            if (kc === 38) {
                this.activatePreviousItem();
                return;
            }
            // Right Arrow
            if (kc === 39) {
                let item = this.activeItem;
                if (item && item.type === 'submenu') {
                    this.triggerActiveItem();
                }
                else {
                    this.rootMenu._menuRequested.emit('next');
                }
                return;
            }
            // Down Arrow
            if (kc === 40) {
                this.activateNextItem();
                return;
            }
            // Get the pressed key character.
            let key = keyboard.getKeyboardLayout().keyForKeydownEvent(event);
            // Bail if the key is not valid.
            if (!key) {
                return;
            }
            // Search for the next best matching mnemonic item.
            let start = this._activeIndex + 1;
            let result = Private$9.findMnemonic(this._items, key, start);
            // Handle the requested mnemonic based on the search results.
            // If exactly one mnemonic is matched, that item is triggered.
            // Otherwise, the next mnemonic is activated if available,
            // followed by the auto mnemonic if available.
            if (result.index !== -1 && !result.multiple) {
                this.activeIndex = result.index;
                this.triggerActiveItem();
            }
            else if (result.index !== -1) {
                this.activeIndex = result.index;
            }
            else if (result.auto !== -1) {
                this.activeIndex = result.auto;
            }
        }
        /**
         * Handle the `'pointerup'` event for the menu.
         *
         * #### Notes
         * This listener is attached to the menu node.
         */
        _evtPointerUp(event) {
            if (event.button !== 0) {
                return;
            }
            if (Utils$1.isTouchEvent(event)) {
                this._pendingTouchActivation = this._activateItemFromEvent(event);
                event.stopPropagation();
                return;
            }
            event.preventDefault();
            event.stopPropagation();
            this._activateItemFromEvent(event);
            this.triggerActiveItem();
        }
        /**
         * Handle the `'click'` event for the menu.
         *
         * #### Notes
         * This listener is attached to the menu node.
         */
        _evtClick(event) {
            if (!this._pendingTouchActivation) {
                return;
            }
            this._pendingTouchActivation = false;
            event.preventDefault();
            event.stopPropagation();
            if (!this._activateItemFromEvent(event)) {
                return;
            }
            this.triggerActiveItem();
        }
        /**
         * Handle the `'pointermove'` event for the menu.
         *
         * #### Notes
         * This listener is attached to the menu node.
         */
        _evtPointerMove(event) {
            if (Utils$1.isTouchEvent(event)) {
                return;
            }
            // Hit test the item nodes for the item under the mouse.
            let index = algorithm.ArrayExt.findFirstIndex(this.contentNode.children, node => {
                return domutils.ElementExt.hitTest(node, event.clientX, event.clientY);
            });
            // Bail early if the mouse is already over the active index.
            if (index === this._activeIndex) {
                return;
            }
            // Update and coerce the active index.
            this.activeIndex = index;
            index = this.activeIndex;
            // If the index is the current child index, cancel the timers.
            if (index === this._childIndex) {
                this._cancelOpenTimer();
                this._cancelCloseTimer();
                return;
            }
            // If a child menu is currently open, start the close timer.
            if (this._childIndex !== -1) {
                this._startCloseTimer();
            }
            // Cancel the open timer to give a full delay for opening.
            this._cancelOpenTimer();
            // Bail if the active item is not a valid submenu item.
            let item = this.activeItem;
            if (!item || item.type !== 'submenu' || !item.submenu) {
                return;
            }
            // Start the open timer to open the active item submenu.
            this._startOpenTimer();
        }
        /**
         * Handle the `'pointerenter'` event for the menu.
         *
         * #### Notes
         * This listener is attached to the menu node.
         */
        _evtPointerEnter(event) {
            if (Utils$1.isTouchEvent(event)) {
                return;
            }
            // Synchronize the active ancestor items.
            for (let menu = this._parentMenu; menu; menu = menu._parentMenu) {
                menu._cancelOpenTimer();
                menu._cancelCloseTimer();
                menu.activeIndex = menu._childIndex;
            }
        }
        /**
         * Handle the `'pointerleave'` event for the menu.
         *
         * #### Notes
         * This listener is attached to the menu node.
         */
        _evtPointerLeave(event) {
            if (Utils$1.isTouchEvent(event)) {
                return;
            }
            // Cancel any pending submenu opening.
            this._cancelOpenTimer();
            // If there is no open child menu, just reset the active index.
            if (!this._childMenu) {
                this.activeIndex = -1;
                return;
            }
            // If the mouse is over the child menu, cancel the close timer.
            let { clientX, clientY } = event;
            if (domutils.ElementExt.hitTest(this._childMenu.node, clientX, clientY)) {
                this._cancelCloseTimer();
                return;
            }
            // Otherwise, reset the active index and start the close timer.
            this.activeIndex = -1;
            this._startCloseTimer();
        }
        /**
         * Handle the `'pointerdown'` event for the menu.
         *
         * #### Notes
         * This listener is attached to the document node.
         */
        _evtPointerDown(event) {
            this._pendingTouchActivation = false;
            // Bail if the menu is not a root menu.
            if (this._parentMenu) {
                return;
            }
            // The mouse button which is pressed is irrelevant. If the press
            // is not on a menu, the entire hierarchy is closed and the event
            // is allowed to propagate. This allows other code to act on the
            // event, such as focusing the clicked element.
            if (Private$9.hitTestMenus(this, event.clientX, event.clientY)) {
                if (!Utils$1.isTouchEvent(event)) {
                    event.preventDefault();
                }
                event.stopPropagation();
            }
            else {
                this.close();
            }
        }
        /**
         * Open the child menu at the active index immediately.
         *
         * If a different child menu is already open, it will be closed,
         * even if the active item is not a valid submenu.
         */
        _openChildMenu(activateFirst = false) {
            // If the item is not a valid submenu, close the child menu.
            let item = this.activeItem;
            if (!item || item.type !== 'submenu' || !item.submenu) {
                this._closeChildMenu();
                return;
            }
            // Do nothing if the child menu will not change.
            let submenu = item.submenu;
            if (submenu === this._childMenu) {
                return;
            }
            // Prior to any DOM modifications save window data
            Menu.saveWindowData();
            // Ensure the current child menu is closed.
            this._closeChildMenu();
            // Update the private child state.
            this._childMenu = submenu;
            this._childIndex = this._activeIndex;
            // Set the parent menu reference for the child.
            submenu._parentMenu = this;
            // Ensure the menu is updated and lookup the item node.
            messaging.MessageLoop.sendMessage(this, Widget.Msg.UpdateRequest);
            let itemNode = this.contentNode.children[this._activeIndex];
            // Open the submenu at the active node.
            Private$9.openSubmenu(submenu, itemNode);
            // Activate the first item if desired.
            if (activateFirst) {
                submenu.activeIndex = -1;
                submenu.activateNextItem();
            }
            // Activate the child menu.
            submenu.activate();
        }
        /**
         * Activate the menu item under an event.
         */
        _activateItemFromEvent(event) {
            let index = algorithm.ArrayExt.findFirstIndex(this.contentNode.children, node => {
                return domutils.ElementExt.hitTest(node, event.clientX, event.clientY);
            });
            if (index === -1) {
                return false;
            }
            this.activeIndex = index;
            return true;
        }
        /**
         * Close the child menu immediately.
         *
         * This is a no-op if a child menu is not open.
         */
        _closeChildMenu() {
            if (this._childMenu) {
                this._childMenu.close();
            }
        }
        /**
         * Start the open timer, unless it is already pending.
         */
        _startOpenTimer() {
            if (this._openTimerID === 0) {
                this._openTimerID = window.setTimeout(() => {
                    this._openTimerID = 0;
                    this._openChildMenu();
                }, Private$9.TIMER_DELAY);
            }
        }
        /**
         * Start the close timer, unless it is already pending.
         */
        _startCloseTimer() {
            if (this._closeTimerID === 0) {
                this._closeTimerID = window.setTimeout(() => {
                    this._closeTimerID = 0;
                    this._closeChildMenu();
                }, Private$9.TIMER_DELAY);
            }
        }
        /**
         * Cancel the open timer, if the timer is pending.
         */
        _cancelOpenTimer() {
            if (this._openTimerID !== 0) {
                clearTimeout(this._openTimerID);
                this._openTimerID = 0;
            }
        }
        /**
         * Cancel the close timer, if the timer is pending.
         */
        _cancelCloseTimer() {
            if (this._closeTimerID !== 0) {
                clearTimeout(this._closeTimerID);
                this._closeTimerID = 0;
            }
        }
        /**
         * Save window data used for menu positioning in transient cache.
         *
         * In order to avoid layout trashing it is recommended to invoke this
         * method immediately prior to opening the menu and any DOM modifications
         * (like closing previously visible menu, or adding a class to menu widget).
         *
         * The transient cache will be released upon `open()` call.
         */
        static saveWindowData() {
            Private$9.saveWindowData();
        }
    }
    /**
     * The namespace for the `Menu` class statics.
     */
    (function (Menu) {
        /**
         * The default implementation of `IRenderer`.
         *
         * #### Notes
         * Subclasses are free to reimplement rendering methods as needed.
         */
        class Renderer {
            /**
             * Render the virtual element for a menu item.
             *
             * @param data - The data to use for rendering the item.
             *
             * @returns A virtual element representing the item.
             */
            renderItem(data) {
                let className = this.createItemClass(data);
                let dataset = this.createItemDataset(data);
                let aria = this.createItemARIA(data);
                return virtualdom.h.li({
                    className,
                    dataset,
                    tabindex: '0',
                    onfocus: data.onfocus,
                    ...aria
                }, this.renderIcon(data), this.renderLabel(data), this.renderShortcut(data), this.renderSubmenu(data));
            }
            /**
             * Render the icon element for a menu item.
             *
             * @param data - The data to use for rendering the icon.
             *
             * @returns A virtual element representing the item icon.
             */
            renderIcon(data) {
                let className = this.createIconClass(data);
                // If data.item.icon is undefined, it will be ignored.
                return virtualdom.h.div({ className }, data.item.icon, data.item.iconLabel);
            }
            /**
             * Render the label element for a menu item.
             *
             * @param data - The data to use for rendering the label.
             *
             * @returns A virtual element representing the item label.
             */
            renderLabel(data) {
                let content = this.formatLabel(data);
                return virtualdom.h.div({ className: 'lm-Menu-itemLabel' }, content);
            }
            /**
             * Render the shortcut element for a menu item.
             *
             * @param data - The data to use for rendering the shortcut.
             *
             * @returns A virtual element representing the item shortcut.
             */
            renderShortcut(data) {
                let content = this.formatShortcut(data);
                return virtualdom.h.div({ className: 'lm-Menu-itemShortcut' }, content);
            }
            /**
             * Render the submenu icon element for a menu item.
             *
             * @param data - The data to use for rendering the submenu icon.
             *
             * @returns A virtual element representing the submenu icon.
             */
            renderSubmenu(data) {
                return virtualdom.h.div({ className: 'lm-Menu-itemSubmenuIcon' });
            }
            /**
             * Create the class name for the menu item.
             *
             * @param data - The data to use for the class name.
             *
             * @returns The full class name for the menu item.
             */
            createItemClass(data) {
                // Setup the initial class name.
                let name = 'lm-Menu-item';
                // Add the boolean state classes.
                if (!data.item.isEnabled) {
                    name += ' lm-mod-disabled';
                }
                if (data.item.isToggled) {
                    name += ' lm-mod-toggled';
                }
                if (!data.item.isVisible) {
                    name += ' lm-mod-hidden';
                }
                if (data.active) {
                    name += ' lm-mod-active';
                }
                if (data.collapsed) {
                    name += ' lm-mod-collapsed';
                }
                // Add the extra class.
                let extra = data.item.className;
                if (extra) {
                    name += ` ${extra}`;
                }
                // Return the complete class name.
                return name;
            }
            /**
             * Create the dataset for the menu item.
             *
             * @param data - The data to use for creating the dataset.
             *
             * @returns The dataset for the menu item.
             */
            createItemDataset(data) {
                let result;
                let { type, command, dataset } = data.item;
                if (type === 'command') {
                    result = { ...dataset, type, command };
                }
                else {
                    result = { ...dataset, type };
                }
                return result;
            }
            /**
             * Create the class name for the menu item icon.
             *
             * @param data - The data to use for the class name.
             *
             * @returns The full class name for the item icon.
             */
            createIconClass(data) {
                let name = 'lm-Menu-itemIcon';
                let extra = data.item.iconClass;
                return extra ? `${name} ${extra}` : name;
            }
            /**
             * Create the aria attributes for menu item.
             *
             * @param data - The data to use for the aria attributes.
             *
             * @returns The aria attributes object for the item.
             */
            createItemARIA(data) {
                let aria = {};
                switch (data.item.type) {
                    case 'separator':
                        aria.role = 'presentation';
                        break;
                    case 'submenu':
                        aria['aria-haspopup'] = 'true';
                        if (!data.item.isEnabled) {
                            aria['aria-disabled'] = 'true';
                        }
                        break;
                    default:
                        if (!data.item.isEnabled) {
                            aria['aria-disabled'] = 'true';
                        }
                        if (data.item.isToggleable || data.item.isToggled) {
                            aria.role = 'menuitemcheckbox';
                            aria['aria-checked'] = `${data.item.isToggled}`;
                        }
                        else {
                            aria.role = 'menuitem';
                        }
                }
                return aria;
            }
            /**
             * Create the render content for the label node.
             *
             * @param data - The data to use for the label content.
             *
             * @returns The content to add to the label node.
             */
            formatLabel(data) {
                // Fetch the label text and mnemonic index.
                let { label, mnemonic } = data.item;
                // If the index is out of range, do not modify the label.
                if (mnemonic < 0 || mnemonic >= label.length) {
                    return label;
                }
                // Split the label into parts.
                let prefix = label.slice(0, mnemonic);
                let suffix = label.slice(mnemonic + 1);
                let char = label[mnemonic];
                // Wrap the mnemonic character in a span.
                let span = virtualdom.h.span({ className: 'lm-Menu-itemMnemonic' }, char);
                // Return the content parts.
                return [prefix, span, suffix];
            }
            /**
             * Create the render content for the shortcut node.
             *
             * @param data - The data to use for the shortcut content.
             *
             * @returns The content to add to the shortcut node.
             */
            formatShortcut(data) {
                let kb = data.item.keyBinding;
                return kb ? commands.CommandRegistry.formatKeystroke(kb.keys) : null;
            }
        }
        Menu.Renderer = Renderer;
        /**
         * The default `Renderer` instance.
         */
        Menu.defaultRenderer = new Renderer();
    })(Menu || (Menu = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$9;
    (function (Private) {
        /**
         * The ms delay for opening and closing a submenu.
         */
        Private.TIMER_DELAY = 300;
        /**
         * The horizontal pixel overlap for an open submenu.
         */
        Private.SUBMENU_OVERLAP = 3;
        let transientWindowDataCache = null;
        let transientCacheCounter = 0;
        function getWindowData() {
            // if transient cache is in use, take one from it
            if (transientCacheCounter > 0) {
                transientCacheCounter--;
                return transientWindowDataCache;
            }
            return _getWindowData();
        }
        /**
         * Store window data in transient cache.
         *
         * The transient cache will be released upon `getWindowData()` call.
         * If this function is called multiple times, the cache will be
         * retained until as many calls to `getWindowData()` were made.
         *
         * Note: should be called before any DOM modifications.
         */
        function saveWindowData() {
            transientWindowDataCache = _getWindowData();
            transientCacheCounter++;
        }
        Private.saveWindowData = saveWindowData;
        /**
         * Create the DOM node for a menu.
         */
        function createNode() {
            let node = document.createElement('div');
            let content = document.createElement('ul');
            content.className = 'lm-Menu-content';
            node.appendChild(content);
            content.setAttribute('role', 'menu');
            node.tabIndex = 0;
            return node;
        }
        Private.createNode = createNode;
        /**
         * Test whether a menu item can be activated.
         */
        function canActivate(item) {
            return item.type !== 'separator' && item.isEnabled && item.isVisible;
        }
        Private.canActivate = canActivate;
        /**
         * Create a new menu item for an owner menu.
         */
        function createItem(owner, options) {
            return new MenuItem(owner.commands, options);
        }
        Private.createItem = createItem;
        /**
         * Hit test a menu hierarchy starting at the given root.
         */
        function hitTestMenus(menu, x, y) {
            for (let temp = menu; temp; temp = temp.childMenu) {
                if (domutils.ElementExt.hitTest(temp.node, x, y)) {
                    return true;
                }
            }
            return false;
        }
        Private.hitTestMenus = hitTestMenus;
        /**
         * Compute which extra separator items should be collapsed.
         */
        function computeCollapsed(items) {
            // Allocate the return array and fill it with `false`.
            let result = new Array(items.length);
            algorithm.ArrayExt.fill(result, false);
            // Collapse the leading separators.
            let k1 = 0;
            let n = items.length;
            for (; k1 < n; ++k1) {
                let item = items[k1];
                if (!item.isVisible) {
                    continue;
                }
                if (item.type !== 'separator') {
                    break;
                }
                result[k1] = true;
            }
            // Hide the trailing separators.
            let k2 = n - 1;
            for (; k2 >= 0; --k2) {
                let item = items[k2];
                if (!item.isVisible) {
                    continue;
                }
                if (item.type !== 'separator') {
                    break;
                }
                result[k2] = true;
            }
            // Hide the remaining consecutive separators.
            let hide = false;
            while (++k1 < k2) {
                let item = items[k1];
                if (!item.isVisible) {
                    continue;
                }
                if (item.type !== 'separator') {
                    hide = false;
                }
                else if (hide) {
                    result[k1] = true;
                }
                else {
                    hide = true;
                }
            }
            // Return the resulting flags.
            return result;
        }
        Private.computeCollapsed = computeCollapsed;
        function _getWindowData() {
            return {
                pageXOffset: window.pageXOffset,
                pageYOffset: window.pageYOffset,
                clientWidth: document.documentElement.clientWidth,
                clientHeight: document.documentElement.clientHeight
            };
        }
        /**
         * Open a menu as a root menu at the target location.
         */
        function openRootMenu(menu, x, y, forceX, forceY, horizontalAlignment, host, ref) {
            // Get the current position and size of the main viewport.
            const windowData = getWindowData();
            let px = windowData.pageXOffset;
            let py = windowData.pageYOffset;
            let cw = windowData.clientWidth;
            let ch = windowData.clientHeight;
            // Ensure the menu is updated before attaching and measuring.
            messaging.MessageLoop.sendMessage(menu, Widget.Msg.UpdateRequest);
            // Compute the maximum allowed height for the menu.
            let maxHeight = ch - (forceY ? y : 0);
            // Fetch common variables.
            let node = menu.node;
            let style = node.style;
            // Clear the menu geometry and prepare it for measuring.
            style.opacity = '0';
            style.maxHeight = `${maxHeight}px`;
            // Attach the menu to the document.
            Widget.attach(menu, host || document.body, ref);
            // Measure the size of the menu.
            let { width, height } = node.getBoundingClientRect();
            // align the menu to the right of the target if requested or language is RTL
            if (horizontalAlignment === 'right') {
                x -= width;
            }
            // Adjust the X position of the menu to fit on-screen.
            if (!forceX && x + width > px + cw) {
                x = px + cw - width;
            }
            // Adjust the Y position of the menu to fit on-screen.
            if (!forceY && y + height > py + ch) {
                if (y > py + ch) {
                    y = py + ch - height;
                }
                else {
                    y = y - height;
                }
            }
            // Update the position of the menu to the computed position.
            style.transform = `translate(${Math.max(0, x)}px, ${Math.max(0, y)}px`;
            // Finally, make the menu visible on the screen.
            style.opacity = '1';
        }
        Private.openRootMenu = openRootMenu;
        /**
         * Open a menu as a submenu using an item node for positioning.
         */
        function openSubmenu(submenu, itemNode) {
            // Get the current position and size of the main viewport.
            const windowData = getWindowData();
            let px = windowData.pageXOffset;
            let py = windowData.pageYOffset;
            let cw = windowData.clientWidth;
            let ch = windowData.clientHeight;
            // Ensure the menu is updated before opening.
            messaging.MessageLoop.sendMessage(submenu, Widget.Msg.UpdateRequest);
            // Compute the maximum allowed height for the menu.
            let maxHeight = ch;
            // Fetch common variables.
            let node = submenu.node;
            let style = node.style;
            // Clear the menu geometry and prepare it for measuring.
            style.opacity = '0';
            style.maxHeight = `${maxHeight}px`;
            // Attach the menu to the document.
            Widget.attach(submenu, document.body);
            // Measure the size of the menu.
            let { width, height } = node.getBoundingClientRect();
            // Compute the box sizing for the menu.
            let box = domutils.ElementExt.boxSizing(submenu.node);
            // Get the bounding rect for the target item node.
            let itemRect = itemNode.getBoundingClientRect();
            // Compute the target X position.
            let x = itemRect.right - Private.SUBMENU_OVERLAP;
            // Adjust the X position to fit on the screen.
            if (x + width > px + cw) {
                x = itemRect.left + Private.SUBMENU_OVERLAP - width;
            }
            // Compute the target Y position.
            let y = itemRect.top - box.borderTop - box.paddingTop;
            // Adjust the Y position to fit on the screen.
            if (y + height > py + ch) {
                y = itemRect.bottom + box.borderBottom + box.paddingBottom - height;
            }
            // Update the position of the menu to the computed position.
            style.transform = `translate(${Math.max(0, x)}px, ${Math.max(0, y)}px`;
            // Finally, make the menu visible on the screen.
            style.opacity = '1';
        }
        Private.openSubmenu = openSubmenu;
        /**
         * Find the best matching mnemonic item.
         *
         * The search starts at the given index and wraps around.
         */
        function findMnemonic(items, key, start) {
            // Setup the result variables.
            let index = -1;
            let auto = -1;
            let multiple = false;
            // Normalize the key to upper case.
            let upperKey = key.toUpperCase();
            // Search the items from the given start index.
            for (let i = 0, n = items.length; i < n; ++i) {
                // Compute the wrapped index.
                let k = (i + start) % n;
                // Lookup the item
                let item = items[k];
                // Ignore items which cannot be activated.
                if (!canActivate(item)) {
                    continue;
                }
                // Ignore items with an empty label.
                let label = item.label;
                if (label.length === 0) {
                    continue;
                }
                // Lookup the mnemonic index for the label.
                let mn = item.mnemonic;
                // Handle a valid mnemonic index.
                if (mn >= 0 && mn < label.length) {
                    if (label[mn].toUpperCase() === upperKey) {
                        if (index === -1) {
                            index = k;
                        }
                        else {
                            multiple = true;
                        }
                    }
                    continue;
                }
                // Finally, handle the auto index if possible.
                if (auto === -1 && label[0].toUpperCase() === upperKey) {
                    auto = k;
                }
            }
            // Return the search results.
            return { index, multiple, auto };
        }
        Private.findMnemonic = findMnemonic;
        /**
         * A concrete implementation of `Menu.IItem`.
         */
        class MenuItem {
            /**
             * Construct a new menu item.
             */
            constructor(commands, options) {
                this._commands = commands;
                this.type = options.type || 'command';
                this.command = options.command || '';
                this.args = options.args || coreutils.JSONExt.emptyObject;
                this.submenu = options.submenu || null;
            }
            /**
             * The display label for the menu item.
             */
            get label() {
                if (this.type === 'command') {
                    return this._commands.label(this.command, this.args);
                }
                if (this.type === 'submenu' && this.submenu) {
                    return this.submenu.title.label;
                }
                return '';
            }
            /**
             * The mnemonic index for the menu item.
             */
            get mnemonic() {
                if (this.type === 'command') {
                    return this._commands.mnemonic(this.command, this.args);
                }
                if (this.type === 'submenu' && this.submenu) {
                    return this.submenu.title.mnemonic;
                }
                return -1;
            }
            /**
             * The icon renderer for the menu item.
             */
            get icon() {
                if (this.type === 'command') {
                    return this._commands.icon(this.command, this.args);
                }
                if (this.type === 'submenu' && this.submenu) {
                    return this.submenu.title.icon;
                }
                return undefined;
            }
            /**
             * The icon class for the menu item.
             */
            get iconClass() {
                if (this.type === 'command') {
                    return this._commands.iconClass(this.command, this.args);
                }
                if (this.type === 'submenu' && this.submenu) {
                    return this.submenu.title.iconClass;
                }
                return '';
            }
            /**
             * The icon label for the menu item.
             */
            get iconLabel() {
                if (this.type === 'command') {
                    return this._commands.iconLabel(this.command, this.args);
                }
                if (this.type === 'submenu' && this.submenu) {
                    return this.submenu.title.iconLabel;
                }
                return '';
            }
            /**
             * The display caption for the menu item.
             */
            get caption() {
                if (this.type === 'command') {
                    return this._commands.caption(this.command, this.args);
                }
                if (this.type === 'submenu' && this.submenu) {
                    return this.submenu.title.caption;
                }
                return '';
            }
            /**
             * The extra class name for the menu item.
             */
            get className() {
                if (this.type === 'command') {
                    return this._commands.className(this.command, this.args);
                }
                if (this.type === 'submenu' && this.submenu) {
                    return this.submenu.title.className;
                }
                return '';
            }
            /**
             * The dataset for the menu item.
             */
            get dataset() {
                if (this.type === 'command') {
                    return this._commands.dataset(this.command, this.args);
                }
                if (this.type === 'submenu' && this.submenu) {
                    return this.submenu.title.dataset;
                }
                return {};
            }
            /**
             * Whether the menu item is enabled.
             */
            get isEnabled() {
                if (this.type === 'command') {
                    return this._commands.isEnabled(this.command, this.args);
                }
                if (this.type === 'submenu') {
                    return this.submenu !== null;
                }
                return true;
            }
            /**
             * Whether the menu item is toggled.
             */
            get isToggled() {
                if (this.type === 'command') {
                    return this._commands.isToggled(this.command, this.args);
                }
                return false;
            }
            /**
             * Whether the menu item is toggleable.
             */
            get isToggleable() {
                if (this.type === 'command') {
                    return this._commands.isToggleable(this.command, this.args);
                }
                return false;
            }
            /**
             * Whether the menu item is visible.
             */
            get isVisible() {
                if (this.type === 'command') {
                    return this._commands.isVisible(this.command, this.args);
                }
                if (this.type === 'submenu') {
                    return this.submenu !== null;
                }
                return true;
            }
            /**
             * The key binding for the menu item.
             */
            get keyBinding() {
                if (this.type === 'command') {
                    let { command, args } = this;
                    return (algorithm.ArrayExt.findLastValue(this._commands.keyBindings, kb => {
                        return kb.command === command && coreutils.JSONExt.deepEqual(kb.args, args);
                    }) || null);
                }
                return null;
            }
        }
    })(Private$9 || (Private$9 = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * An object which implements a universal context menu.
     *
     * #### Notes
     * The items shown in the context menu are determined by CSS selector
     * matching against the DOM hierarchy at the site of the mouse click.
     * This is similar in concept to how keyboard shortcuts are matched
     * in the command registry.
     */
    class ContextMenu {
        /**
         * Construct a new context menu.
         *
         * @param options - The options for initializing the menu.
         */
        constructor(options) {
            this._groupByTarget = true;
            this._idTick = 0;
            this._items = [];
            this._sortBySelector = true;
            const { groupByTarget, sortBySelector, ...others } = options;
            this.menu = new Menu(others);
            this._groupByTarget = groupByTarget !== false;
            this._sortBySelector = sortBySelector !== false;
        }
        /**
         * Add an item to the context menu.
         *
         * @param options - The options for creating the item.
         *
         * @returns A disposable which will remove the item from the menu.
         */
        addItem(options) {
            // Create an item from the given options.
            let item = Private$8.createItem(options, this._idTick++);
            // Add the item to the internal array.
            this._items.push(item);
            // Return a disposable which will remove the item.
            return new disposable.DisposableDelegate(() => {
                algorithm.ArrayExt.removeFirstOf(this._items, item);
            });
        }
        /**
         * Open the context menu in response to a `'contextmenu'` event.
         *
         * @param event - The `'contextmenu'` event of interest.
         *
         * @returns `true` if the menu was opened, or `false` if no items
         *   matched the event and the menu was not opened.
         *
         * #### Notes
         * This method will populate the context menu with items which match
         * the propagation path of the event, then open the menu at the mouse
         * position indicated by the event.
         */
        open(event) {
            // Prior to any DOM modifications update the window data.
            Menu.saveWindowData();
            // Clear the current contents of the context menu.
            this.menu.clearItems();
            // Bail early if there are no items to match.
            if (this._items.length === 0) {
                return false;
            }
            // Find the matching items for the event.
            let items = Private$8.matchItems(this._items, event, this._groupByTarget, this._sortBySelector);
            // Bail if there are no matching items.
            if (!items || items.length === 0) {
                return false;
            }
            // Add the filtered items to the menu.
            for (const item of items) {
                this.menu.addItem(item);
            }
            // Open the context menu at the current mouse position.
            this.menu.open(event.clientX, event.clientY);
            // Indicate success.
            return true;
        }
    }
    /**
     * The namespace for the module implementation details.
     */
    var Private$8;
    (function (Private) {
        /**
         * Create a normalized context menu item from an options object.
         */
        function createItem(options, id) {
            let selector = validateSelector(options.selector);
            let rank = options.rank !== undefined ? options.rank : Infinity;
            return { ...options, selector, rank, id };
        }
        Private.createItem = createItem;
        /**
         * Find the items which match a context menu event.
         *
         * The results are sorted by DOM level, specificity, and rank.
         */
        function matchItems(items, event, groupByTarget, sortBySelector) {
            // Look up the target of the event.
            let target = event.target;
            // Bail if there is no target.
            if (!target) {
                return null;
            }
            // Look up the current target of the event.
            let currentTarget = event.currentTarget;
            // Bail if there is no current target.
            if (!currentTarget) {
                return null;
            }
            // There are some third party libraries that cause the `target` to
            // be detached from the DOM before lumino can process the event.
            // If that happens, search for a new target node by point. If that
            // node is still dangling, bail.
            if (!currentTarget.contains(target)) {
                target = document.elementFromPoint(event.clientX, event.clientY);
                if (!target || !currentTarget.contains(target)) {
                    return null;
                }
            }
            // Set up the result array.
            let result = [];
            // Copy the items array to allow in-place modification.
            let availableItems = items.slice();
            // Walk up the DOM hierarchy searching for matches.
            while (target !== null) {
                // Set up the match array for this DOM level.
                let matches = [];
                // Search the remaining items for matches.
                for (let i = 0, n = availableItems.length; i < n; ++i) {
                    // Fetch the item.
                    let item = availableItems[i];
                    // Skip items which are already consumed.
                    if (!item) {
                        continue;
                    }
                    // Skip items which do not match the element.
                    if (!domutils.Selector.matches(target, item.selector)) {
                        continue;
                    }
                    // Add the matched item to the result for this DOM level.
                    matches.push(item);
                    // Mark the item as consumed.
                    availableItems[i] = null;
                }
                // Sort the matches for this level and add them to the results.
                if (matches.length !== 0) {
                    if (groupByTarget) {
                        matches.sort(sortBySelector ? itemCmp : itemCmpRank);
                    }
                    result.push(...matches);
                }
                // Stop searching at the limits of the DOM range.
                if (target === currentTarget) {
                    break;
                }
                // Step to the parent DOM level.
                target = target.parentElement;
            }
            if (!groupByTarget) {
                result.sort(sortBySelector ? itemCmp : itemCmpRank);
            }
            // Return the matched and sorted results.
            return result;
        }
        Private.matchItems = matchItems;
        /**
         * Validate the selector for a menu item.
         *
         * This returns the validated selector, or throws if the selector is
         * invalid or contains commas.
         */
        function validateSelector(selector) {
            if (selector.indexOf(',') !== -1) {
                throw new Error(`Selector cannot contain commas: ${selector}`);
            }
            if (!domutils.Selector.isValid(selector)) {
                throw new Error(`Invalid selector: ${selector}`);
            }
            return selector;
        }
        /**
         * A sort comparison function for a context menu item by ranks.
         */
        function itemCmpRank(a, b) {
            // Sort based on rank.
            let r1 = a.rank;
            let r2 = b.rank;
            if (r1 !== r2) {
                return r1 < r2 ? -1 : 1; // Infinity-safe
            }
            // When all else fails, sort by item id.
            return a.id - b.id;
        }
        /**
         * A sort comparison function for a context menu item by selectors and ranks.
         */
        function itemCmp(a, b) {
            // Sort first based on selector specificity.
            let s1 = domutils.Selector.calculateSpecificity(a.selector);
            let s2 = domutils.Selector.calculateSpecificity(b.selector);
            if (s1 !== s2) {
                return s2 - s1;
            }
            // If specificities are equal
            return itemCmpRank(a, b);
        }
    })(Private$8 || (Private$8 = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    const ARROW_KEYS = [
        'ArrowLeft',
        'ArrowUp',
        'ArrowRight',
        'ArrowDown',
        'Home',
        'End'
    ];
    /**
     * A widget which displays titles as a single row or column of tabs.
     *
     * #### Notes
     * If CSS transforms are used to rotate nodes for vertically oriented
     * text, then tab dragging will not work correctly. The `tabsMovable`
     * property should be set to `false` when rotating nodes from CSS.
     */
    class TabBar extends Widget {
        /**
         * Construct a new tab bar.
         *
         * @param options - The options for initializing the tab bar.
         */
        constructor(options = {}) {
            super({ node: Private$7.createNode() });
            this._currentIndex = -1;
            this._titles = [];
            this._titlesEditable = false;
            this._previousTitle = null;
            this._dragData = null;
            this._tabSizeFrozen = false;
            this._frozenTabWidths = null;
            this._unfreezeRunID = 0;
            this._unfreezeFallbackTimerID = 0;
            this._unfreezeTransitionListener = null;
            this._pendingTouchCloseRequest = null;
            this._addButtonEnabled = false;
            this._tabMoved = new signaling.Signal(this);
            this._currentChanged = new signaling.Signal(this);
            this._addRequested = new signaling.Signal(this);
            this._tabCloseRequested = new signaling.Signal(this);
            this._tabDetachRequested = new signaling.Signal(this);
            this._tabActivateRequested = new signaling.Signal(this);
            this.addClass('lm-TabBar');
            this.contentNode.setAttribute('role', 'tablist');
            this.setFlag(Widget.Flag.DisallowLayout);
            this._document = options.document || document;
            this.tabsMovable = options.tabsMovable || false;
            this.titlesEditable = options.titlesEditable || false;
            this.allowDeselect = options.allowDeselect || false;
            this.addButtonEnabled = options.addButtonEnabled || false;
            this.insertBehavior = options.insertBehavior || 'select-tab-if-needed';
            this.name = options.name || '';
            this.orientation = options.orientation || 'horizontal';
            this.removeBehavior = options.removeBehavior || 'select-tab-after';
            this.renderer = options.renderer || TabBar.defaultRenderer;
        }
        /**
         * Dispose of the resources held by the widget.
         */
        dispose() {
            this._releaseMouse();
            this._titles.length = 0;
            this._previousTitle = null;
            super.dispose();
        }
        /**
         * A signal emitted when the current tab is changed.
         *
         * #### Notes
         * This signal is emitted when the currently selected tab is changed
         * either through user or programmatic interaction.
         *
         * Notably, this signal is not emitted when the index of the current
         * tab changes due to tabs being inserted, removed, or moved. It is
         * only emitted when the actual current tab node is changed.
         */
        get currentChanged() {
            return this._currentChanged;
        }
        /**
         * A signal emitted when a tab is moved by the user.
         *
         * #### Notes
         * This signal is emitted when a tab is moved by user interaction.
         *
         * This signal is not emitted when a tab is moved programmatically.
         */
        get tabMoved() {
            return this._tabMoved;
        }
        /**
         * A signal emitted when a tab is clicked by the user.
         *
         * #### Notes
         * If the clicked tab is not the current tab, the clicked tab will be
         * made current and the `currentChanged` signal will be emitted first.
         *
         * This signal is emitted even if the clicked tab is the current tab.
         */
        get tabActivateRequested() {
            return this._tabActivateRequested;
        }
        /**
         * A signal emitted when the tab bar add button is clicked.
         */
        get addRequested() {
            return this._addRequested;
        }
        /**
         * A signal emitted when a tab close icon is clicked.
         *
         * #### Notes
         * This signal is not emitted unless the tab title is `closable`.
         */
        get tabCloseRequested() {
            return this._tabCloseRequested;
        }
        /**
         * A signal emitted when a tab is dragged beyond the detach threshold.
         *
         * #### Notes
         * This signal is emitted when the user drags a tab with the mouse,
         * and mouse is dragged beyond the detach threshold.
         *
         * The consumer of the signal should call `releaseMouse` and remove
         * the tab in order to complete the detach.
         *
         * This signal is only emitted once per drag cycle.
         */
        get tabDetachRequested() {
            return this._tabDetachRequested;
        }
        /**
         * The document to use with the tab bar.
         *
         * The default is the global `document` instance.
         */
        get document() {
            return this._document;
        }
        /**
         * Whether the titles can be user-edited.
         *
         */
        get titlesEditable() {
            return this._titlesEditable;
        }
        /**
         * Set whether titles can be user edited.
         *
         */
        set titlesEditable(value) {
            this._titlesEditable = value;
        }
        /**
         * Get the currently selected title.
         *
         * #### Notes
         * This will be `null` if no tab is selected.
         */
        get currentTitle() {
            return this._titles[this._currentIndex] || null;
        }
        /**
         * Set the currently selected title.
         *
         * #### Notes
         * If the title does not exist, the title will be set to `null`.
         */
        set currentTitle(value) {
            this.currentIndex = value ? this._titles.indexOf(value) : -1;
        }
        /**
         * Get the index of the currently selected tab.
         *
         * #### Notes
         * This will be `-1` if no tab is selected.
         */
        get currentIndex() {
            return this._currentIndex;
        }
        /**
         * Set the index of the currently selected tab.
         *
         * #### Notes
         * If the value is out of range, the index will be set to `-1`.
         */
        set currentIndex(value) {
            // Adjust for an out of range index.
            if (value < 0 || value >= this._titles.length) {
                value = -1;
            }
            // Bail early if the index will not change.
            if (this._currentIndex === value) {
                return;
            }
            // Look up the previous index and title.
            let pi = this._currentIndex;
            let pt = this._titles[pi] || null;
            // Look up the current index and title.
            let ci = value;
            let ct = this._titles[ci] || null;
            // Update the current index and previous title.
            this._currentIndex = ci;
            this._previousTitle = pt;
            // Schedule an update of the tabs.
            this.update();
            // Emit the current changed signal.
            this._currentChanged.emit({
                previousIndex: pi,
                previousTitle: pt,
                currentIndex: ci,
                currentTitle: ct
            });
        }
        /**
         * Get the name of the tab bar.
         */
        get name() {
            return this._name;
        }
        /**
         * Set the name of the tab bar.
         */
        set name(value) {
            this._name = value;
            if (value) {
                this.contentNode.setAttribute('aria-label', value);
            }
            else {
                this.contentNode.removeAttribute('aria-label');
            }
        }
        /**
         * Get the orientation of the tab bar.
         *
         * #### Notes
         * This controls whether the tabs are arranged in a row or column.
         */
        get orientation() {
            return this._orientation;
        }
        /**
         * Set the orientation of the tab bar.
         *
         * #### Notes
         * This controls whether the tabs are arranged in a row or column.
         */
        set orientation(value) {
            // Do nothing if the orientation does not change.
            if (this._orientation === value) {
                return;
            }
            // Release the mouse before making any changes.
            this._releaseMouse();
            // Toggle the orientation values.
            this._orientation = value;
            this.dataset['orientation'] = value;
            this.contentNode.setAttribute('aria-orientation', value);
        }
        /**
         * Whether the add button is enabled.
         */
        get addButtonEnabled() {
            return this._addButtonEnabled;
        }
        /**
         * Set whether the add button is enabled.
         */
        set addButtonEnabled(value) {
            // Do nothing if the value does not change.
            if (this._addButtonEnabled === value) {
                return;
            }
            this._addButtonEnabled = value;
            if (value) {
                this.addButtonNode.classList.remove('lm-mod-hidden');
            }
            else {
                this.addButtonNode.classList.add('lm-mod-hidden');
            }
        }
        /**
         * A read-only array of the titles in the tab bar.
         */
        get titles() {
            return this._titles;
        }
        /**
         * The tab bar content node.
         *
         * #### Notes
         * This is the node which holds the tab nodes.
         *
         * Modifying this node directly can lead to undefined behavior.
         */
        get contentNode() {
            return this.node.getElementsByClassName('lm-TabBar-content')[0];
        }
        /**
         * The tab bar add button node.
         *
         * #### Notes
         * This is the node which holds the add button.
         *
         * Modifying this node directly can lead to undefined behavior.
         */
        get addButtonNode() {
            return this.node.getElementsByClassName('lm-TabBar-addButton')[0];
        }
        /**
         * Add a tab to the end of the tab bar.
         *
         * @param value - The title which holds the data for the tab,
         *   or an options object to convert to a title.
         *
         * @returns The title object added to the tab bar.
         *
         * #### Notes
         * If the title is already added to the tab bar, it will be moved.
         */
        addTab(value) {
            return this.insertTab(this._titles.length, value);
        }
        /**
         * Insert a tab into the tab bar at the specified index.
         *
         * @param index - The index at which to insert the tab.
         *
         * @param value - The title which holds the data for the tab,
         *   or an options object to convert to a title.
         *
         * @returns The title object added to the tab bar.
         *
         * #### Notes
         * The index will be clamped to the bounds of the tabs.
         *
         * If the title is already added to the tab bar, it will be moved.
         */
        insertTab(index, value) {
            // Release the mouse before making any changes.
            this._releaseMouse();
            // Coerce the value to a title.
            let title = Private$7.asTitle(value);
            // Look up the index of the title.
            let i = this._titles.indexOf(title);
            // Clamp the insert index to the array bounds.
            let j = Math.max(0, Math.min(index, this._titles.length));
            // If the title is not in the array, insert it.
            if (i === -1) {
                // Insert the title into the array.
                algorithm.ArrayExt.insert(this._titles, j, title);
                // Connect to the title changed signal.
                title.changed.connect(this._onTitleChanged, this);
                // Schedule an update of the tabs.
                this.update();
                // Adjust the current index for the insert.
                this._adjustCurrentForInsert(j, title);
                // Return the title added to the tab bar.
                return title;
            }
            // Otherwise, the title exists in the array and should be moved.
            // Adjust the index if the location is at the end of the array.
            if (j === this._titles.length) {
                j--;
            }
            // Bail if there is no effective move.
            if (i === j) {
                return title;
            }
            // Move the title to the new location.
            algorithm.ArrayExt.move(this._titles, i, j);
            // Schedule an update of the tabs.
            this.update();
            // Adjust the current index for the move.
            this._adjustCurrentForMove(i, j);
            // Return the title added to the tab bar.
            return title;
        }
        /**
         * Remove a tab from the tab bar.
         *
         * @param title - The title for the tab to remove.
         *
         * #### Notes
         * This is a no-op if the title is not in the tab bar.
         */
        removeTab(title) {
            this.removeTabAt(this._titles.indexOf(title));
        }
        /**
         * Remove the tab at a given index from the tab bar.
         *
         * @param index - The index of the tab to remove.
         *
         * #### Notes
         * This is a no-op if the index is out of range.
         */
        removeTabAt(index) {
            let indexIsValid = index >= 0 && index < this._titles.length;
            if (this._tabSizeFrozen) {
                if (indexIsValid) {
                    // Capture the widths of all tabs that will remain.
                    let tabs = this.contentNode.children;
                    this._frozenTabWidths = [];
                    for (let i = 0, n = tabs.length; i < n; ++i) {
                        if (i !== index) {
                            this._frozenTabWidths.push(tabs[i].offsetWidth);
                        }
                    }
                }
                else {
                    this._frozenTabWidths = null;
                }
            }
            // Release the mouse before making any changes.
            this._releaseMouse();
            // Remove the title from the array.
            let title = algorithm.ArrayExt.removeAt(this._titles, index);
            // Bail if the index is out of range.
            if (!title) {
                return;
            }
            // Disconnect from the title changed signal.
            title.changed.disconnect(this._onTitleChanged, this);
            // Clear the previous title if it's being removed.
            if (title === this._previousTitle) {
                this._previousTitle = null;
            }
            // Schedule an update of the tabs.
            this.update();
            // Adjust the current index for the remove.
            this._adjustCurrentForRemove(index, title);
        }
        /**
         * Remove all tabs from the tab bar.
         */
        clearTabs() {
            // Bail if there is nothing to remove.
            if (this._titles.length === 0) {
                return;
            }
            // Release the mouse before making any changes.
            this._releaseMouse();
            // Disconnect from the title changed signals.
            for (let title of this._titles) {
                title.changed.disconnect(this._onTitleChanged, this);
            }
            // Get the current index and title.
            let pi = this.currentIndex;
            let pt = this.currentTitle;
            // Reset the current index and previous title.
            this._currentIndex = -1;
            this._previousTitle = null;
            // Clear the title array.
            this._titles.length = 0;
            // Schedule an update of the tabs.
            this.update();
            // If no tab was selected, there's nothing else to do.
            if (pi === -1) {
                return;
            }
            // Emit the current changed signal.
            this._currentChanged.emit({
                previousIndex: pi,
                previousTitle: pt,
                currentIndex: -1,
                currentTitle: null
            });
        }
        /**
         * Release the mouse and restore the non-dragged tab positions.
         *
         * #### Notes
         * This will cause the tab bar to stop handling mouse events and to
         * restore the tabs to their non-dragged positions.
         */
        releaseMouse() {
            this._releaseMouse();
        }
        /**
         * Handle the DOM events for the tab bar.
         *
         * @param event - The DOM event sent to the tab bar.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the tab bar's DOM node.
         *
         * This should not be called directly by user code.
         */
        handleEvent(event) {
            switch (event.type) {
                case 'pointerdown':
                    this._evtPointerDown(event);
                    break;
                case 'pointermove':
                    this._evtPointerMove(event);
                    break;
                case 'pointerup':
                    this._evtPointerUp(event);
                    break;
                case 'pointerleave':
                    this._evtPointerLeave(event);
                    break;
                case 'click':
                    this._evtClick(event);
                    break;
                case 'dblclick':
                    this._evtDblClick(event);
                    break;
                case 'keydown':
                    event.eventPhase === Event.CAPTURING_PHASE
                        ? this._evtKeyDownCapturing(event)
                        : this._evtKeyDown(event);
                    break;
                case 'contextmenu':
                    event.preventDefault();
                    event.stopPropagation();
                    break;
            }
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            this.node.addEventListener('pointerdown', this);
            this.node.addEventListener('pointerleave', this);
            this.node.addEventListener('click', this);
            this.node.addEventListener('dblclick', this);
            this.node.addEventListener('keydown', this);
        }
        /**
         * A message handler invoked on an `'after-detach'` message.
         */
        onAfterDetach(msg) {
            this.node.removeEventListener('pointerdown', this);
            this.node.removeEventListener('pointerleave', this);
            this.node.removeEventListener('click', this);
            this.node.removeEventListener('dblclick', this);
            this.node.removeEventListener('keydown', this);
            this._clearUnfreezeTransitionState();
            this._clearTabSizeFreezeState();
            this._pendingTouchCloseRequest = null;
            this._releaseMouse();
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            var _a;
            let titles = this._titles;
            let renderer = this.renderer;
            let currentTitle = this.currentTitle;
            let content = new Array(titles.length);
            // Keep the tabindex="0" attribute to the tab which handled it before the update.
            // If the add button handles it, no need to do anything. If no element of the tab
            // bar handles it, set it on the current or the first tab to ensure one element
            // handles it after update.
            const tabHandlingTabindex = (_a = this._getCurrentTabindex()) !== null && _a !== void 0 ? _a : (this._currentIndex > -1 ? this._currentIndex : 0);
            for (let i = 0, n = titles.length; i < n; ++i) {
                let title = titles[i];
                let current = title === currentTitle;
                let zIndex = current ? n : n - i - 1;
                let tabIndex = tabHandlingTabindex === i ? 0 : -1;
                content[i] = renderer.renderTab({ title, current, zIndex, tabIndex });
            }
            virtualdom.VirtualDOM.render(content, this.contentNode);
            if (this._tabSizeFrozen) {
                let tabs = this.contentNode.children;
                if (this._frozenTabWidths &&
                    this._frozenTabWidths.length === tabs.length) {
                    for (let i = 0, n = tabs.length; i < n; ++i) {
                        this._setFrozenTabSize(tabs[i], this._frozenTabWidths[i]);
                    }
                    this._frozenTabWidths = null;
                }
                else {
                    this._frozenTabWidths = null;
                    for (let i = 0, n = tabs.length; i < n; ++i) {
                        let tab = tabs[i];
                        this._setFrozenTabSize(tab, tab.offsetWidth);
                    }
                }
            }
        }
        /**
         * Get the index of the tab which handles tabindex="0".
         * If the add button handles tabindex="0", -1 is returned.
         * If none of the previous handles tabindex="0", null is returned.
         */
        _getCurrentTabindex() {
            let index = null;
            const elemTabindex = this.contentNode.querySelector('li[tabindex="0"]');
            if (elemTabindex) {
                index = [...this.contentNode.children].indexOf(elemTabindex);
            }
            else if (this._addButtonEnabled &&
                this.addButtonNode.getAttribute('tabindex') === '0') {
                index = -1;
            }
            return index;
        }
        /**
         * Handle the `'dblclick'` event for the tab bar.
         */
        _evtDblClick(event) {
            // Do nothing if titles are not editable
            if (!this.titlesEditable) {
                return;
            }
            let tabs = this.contentNode.children;
            // Find the index of the targeted tab.
            let index = algorithm.ArrayExt.findFirstIndex(tabs, tab => {
                return domutils.ElementExt.hitTest(tab, event.clientX, event.clientY);
            });
            // Do nothing if the press is not on a tab.
            if (index === -1) {
                return;
            }
            let title = this.titles[index];
            let label = tabs[index].querySelector('.lm-TabBar-tabLabel');
            if (label && label.contains(event.target)) {
                let value = title.label || '';
                // Clear the label element
                let oldValue = label.innerHTML;
                label.innerHTML = '';
                let input = document.createElement('input');
                input.classList.add('lm-TabBar-tabInput');
                input.value = value;
                label.appendChild(input);
                let onblur = () => {
                    input.removeEventListener('blur', onblur);
                    label.innerHTML = oldValue;
                    this.node.addEventListener('keydown', this);
                };
                input.addEventListener('dblclick', (event) => event.stopPropagation());
                input.addEventListener('blur', onblur);
                input.addEventListener('keydown', (event) => {
                    if (event.key === 'Enter') {
                        if (input.value !== '') {
                            title.label = title.caption = input.value;
                        }
                        onblur();
                    }
                    else if (event.key === 'Escape') {
                        onblur();
                    }
                });
                this.node.removeEventListener('keydown', this);
                input.select();
                input.focus();
                if (label.children.length > 0) {
                    label.children[0].focus();
                }
            }
        }
        /**
         * Handle the `'keydown'` event for the tab bar at capturing phase.
         */
        _evtKeyDownCapturing(event) {
            if (event.eventPhase !== Event.CAPTURING_PHASE) {
                return;
            }
            // Stop all input events during drag.
            event.preventDefault();
            event.stopPropagation();
            // Release the mouse if `Escape` is pressed.
            if (event.key === 'Escape') {
                this._releaseMouse();
            }
        }
        /**
         * Handle the `'keydown'` event for the tab bar at target phase.
         */
        _evtKeyDown(event) {
            var _a, _b, _c;
            // Allow for navigation using tab key
            if (event.key === 'Tab' || event.eventPhase === Event.CAPTURING_PHASE) {
                return;
            }
            // Check if Enter or Spacebar key has been pressed and open that tab
            if (event.key === 'Enter' ||
                event.key === 'Spacebar' ||
                event.key === ' ') {
                // Get focus element that is in focus by the tab key
                const focusedElement = document.activeElement;
                // Test first if the focus is on the add button node
                if (this.addButtonEnabled &&
                    this.addButtonNode.contains(focusedElement)) {
                    event.preventDefault();
                    event.stopPropagation();
                    this._addRequested.emit();
                }
                else {
                    const index = algorithm.ArrayExt.findFirstIndex(this.contentNode.children, tab => tab.contains(focusedElement));
                    if (index >= 0) {
                        event.preventDefault();
                        event.stopPropagation();
                        this.currentIndex = index;
                    }
                }
                // Handle the arrow keys to switch tabs.
            }
            else if (ARROW_KEYS.includes(event.key)) {
                // Create a list of all focusable elements in the tab bar.
                const focusable = [...this.contentNode.children];
                if (this.addButtonEnabled) {
                    focusable.push(this.addButtonNode);
                }
                // If the tab bar contains only one element, nothing to do.
                if (focusable.length <= 1) {
                    return;
                }
                event.preventDefault();
                event.stopPropagation();
                // Get the current focused element.
                let focusedIndex = focusable.indexOf(document.activeElement);
                if (focusedIndex === -1) {
                    focusedIndex = this._currentIndex;
                }
                // Find the next element to focus on.
                let nextFocused;
                if ((event.key === 'ArrowRight' && this._orientation === 'horizontal') ||
                    (event.key === 'ArrowDown' && this._orientation === 'vertical')) {
                    nextFocused = (_a = focusable[focusedIndex + 1]) !== null && _a !== void 0 ? _a : focusable[0];
                }
                else if ((event.key === 'ArrowLeft' && this._orientation === 'horizontal') ||
                    (event.key === 'ArrowUp' && this._orientation === 'vertical')) {
                    nextFocused =
                        (_b = focusable[focusedIndex - 1]) !== null && _b !== void 0 ? _b : focusable[focusable.length - 1];
                }
                else if (event.key === 'Home') {
                    nextFocused = focusable[0];
                }
                else if (event.key === 'End') {
                    nextFocused = focusable[focusable.length - 1];
                }
                // Change the focused element and the tabindex value.
                if (nextFocused) {
                    (_c = focusable[focusedIndex]) === null || _c === void 0 ? void 0 : _c.setAttribute('tabindex', '-1');
                    nextFocused === null || nextFocused === void 0 ? void 0 : nextFocused.setAttribute('tabindex', '0');
                    nextFocused.focus();
                }
            }
        }
        /**
         * Handle the `'pointerdown'` event for the tab bar.
         */
        _evtPointerDown(event) {
            this._pendingTouchCloseRequest = null;
            // Do nothing if it's not a left or middle mouse press.
            if (event.button !== 0 && event.button !== 1) {
                return;
            }
            // Do nothing if a drag is in progress.
            if (this._dragData) {
                return;
            }
            // Do nothing if a title editable input was clicked.
            if (event.target.classList.contains('lm-TabBar-tabInput')) {
                return;
            }
            // Check if the add button was clicked.
            let addButtonClicked = this.addButtonEnabled &&
                this.addButtonNode.contains(event.target);
            // Lookup the tab nodes.
            let tabs = this.contentNode.children;
            // Find the index of the pressed tab.
            let index = algorithm.ArrayExt.findFirstIndex(tabs, tab => {
                return domutils.ElementExt.hitTest(tab, event.clientX, event.clientY);
            });
            // Do nothing if the press is not on a tab or the add button.
            if (index === -1 && !addButtonClicked) {
                return;
            }
            // Check if the close icon was pressed.
            let icon = index !== -1
                ? tabs[index].querySelector(this.renderer.closeIconSelector)
                : null;
            let closeIconPressed = !!icon && icon.contains(event.target);
            // Pressing on a tab stops the event propagation.
            if (!closeIconPressed || !Utils$1.isTouchEvent(event)) {
                event.preventDefault();
            }
            event.stopPropagation();
            // Initialize the non-measured parts of the drag data.
            this._dragData = {
                tab: tabs[index],
                index: index,
                pressX: event.clientX,
                pressY: event.clientY,
                tabPos: -1,
                tabSize: -1,
                tabPressPos: -1,
                targetIndex: -1,
                tabLayout: null,
                contentRect: null,
                override: null,
                dragActive: false,
                dragAborted: false,
                detachRequested: false
            };
            // Add the document pointer up listener.
            this.document.addEventListener('pointerup', this, true);
            // Do nothing else if the middle button or add button is clicked.
            if (event.button === 1 || addButtonClicked) {
                return;
            }
            // Do nothing else if the close icon is clicked.
            if (closeIconPressed) {
                return;
            }
            // Add the extra listeners if the tabs are movable.
            if (this.tabsMovable) {
                this.document.addEventListener('pointermove', this, true);
                this.document.addEventListener('keydown', this, true);
                this.document.addEventListener('contextmenu', this, true);
            }
            // Update the current index as appropriate.
            if (this.allowDeselect && this.currentIndex === index) {
                this.currentIndex = -1;
            }
            else {
                this.currentIndex = index;
            }
            // Do nothing else if there is no current tab.
            if (this.currentIndex === -1) {
                return;
            }
            // Emit the tab activate request signal.
            this._tabActivateRequested.emit({
                index: this.currentIndex,
                title: this.currentTitle
            });
        }
        /**
         * Handle the `'pointermove'` event for the tab bar.
         */
        _evtPointerMove(event) {
            // Do nothing if no drag is in progress.
            let data = this._dragData;
            if (!data) {
                return;
            }
            // Suppress the event during a drag.
            event.preventDefault();
            event.stopPropagation();
            // Lookup the tab nodes.
            let tabs = this.contentNode.children;
            // Bail early if the drag threshold has not been met.
            if (!data.dragActive && !Private$7.dragExceeded(data, event)) {
                return;
            }
            // Activate the drag if necessary.
            if (!data.dragActive) {
                // Fill in the rest of the drag data measurements.
                let tabRect = data.tab.getBoundingClientRect();
                if (this._orientation === 'horizontal') {
                    data.tabPos = data.tab.offsetLeft;
                    data.tabSize = tabRect.width;
                    data.tabPressPos = data.pressX - tabRect.left;
                }
                else {
                    data.tabPos = data.tab.offsetTop;
                    data.tabSize = tabRect.height;
                    data.tabPressPos = data.pressY - tabRect.top;
                }
                data.tabPressOffset = {
                    x: data.pressX - tabRect.left,
                    y: data.pressY - tabRect.top
                };
                data.tabLayout = Private$7.snapTabLayout(tabs, this._orientation);
                data.contentRect = this.contentNode.getBoundingClientRect();
                data.override = dragdrop.Drag.overrideCursor('default');
                // Add the dragging style classes.
                data.tab.classList.add('lm-mod-dragging');
                this.addClass('lm-mod-dragging');
                // Mark the drag as active.
                data.dragActive = true;
            }
            // Emit the detach requested signal if the threshold is exceeded.
            if (!data.detachRequested && Private$7.detachExceeded(data, event)) {
                // Only emit the signal once per drag cycle.
                data.detachRequested = true;
                // Setup the arguments for the signal.
                let index = data.index;
                let clientX = event.clientX;
                let clientY = event.clientY;
                let tab = tabs[index];
                let title = this._titles[index];
                // Emit the tab detach requested signal.
                this._tabDetachRequested.emit({
                    index,
                    title,
                    tab,
                    clientX,
                    clientY,
                    offset: data.tabPressOffset
                });
                // Bail if the signal handler aborted the drag.
                if (data.dragAborted) {
                    return;
                }
            }
            // Update the positions of the tabs.
            Private$7.layoutTabs(tabs, data, event, this._orientation);
        }
        /**
         * Handle the `'pointerup'` event for the document.
         */
        _evtPointerUp(event) {
            // Do nothing if it's not a left or middle mouse release.
            if (event.button !== 0 && event.button !== 1) {
                return;
            }
            // Do nothing if no drag is in progress.
            const data = this._dragData;
            if (!data) {
                return;
            }
            const isTouch = Utils$1.isTouchEvent(event);
            // Stop the event propagation.
            if (!isTouch) {
                event.preventDefault();
            }
            event.stopPropagation();
            // Remove the extra mouse event listeners.
            this.document.removeEventListener('pointermove', this, true);
            this.document.removeEventListener('pointerup', this, true);
            this.document.removeEventListener('keydown', this, true);
            this.document.removeEventListener('contextmenu', this, true);
            // Handle a release when the drag is not active.
            if (!data.dragActive) {
                // Clear the drag data.
                this._dragData = null;
                // Handle clicking the add button.
                let addButtonClicked = this.addButtonEnabled &&
                    this.addButtonNode.contains(event.target);
                if (addButtonClicked) {
                    this._addRequested.emit(undefined);
                    return;
                }
                // Lookup the tab nodes.
                let tabs = this.contentNode.children;
                // Find the index of the released tab.
                let index = algorithm.ArrayExt.findFirstIndex(tabs, tab => {
                    return domutils.ElementExt.hitTest(tab, event.clientX, event.clientY);
                });
                // Do nothing if the release is not on the original pressed tab.
                if (index !== data.index) {
                    return;
                }
                // Ignore the release if the title is not closable.
                let title = this._titles[index];
                if (!title.closable) {
                    return;
                }
                // Emit the close requested signal if the middle button is released.
                if (event.button === 1) {
                    this._tabCloseRequested.emit({ index, title });
                    return;
                }
                // Emit the close requested signal if the close icon was released.
                let icon = tabs[index].querySelector(this.renderer.closeIconSelector);
                if (icon && icon.contains(event.target)) {
                    this._tabSizeFrozen = true;
                    if (isTouch) {
                        this._pendingTouchCloseRequest = { index, title };
                        return;
                    }
                    this._tabCloseRequested.emit({ index, title });
                    return;
                }
                // Otherwise, there is nothing left to do.
                return;
            }
            // Do nothing if the left button is not released.
            if (event.button !== 0) {
                return;
            }
            // Position the tab at its final resting position.
            Private$7.finalizeTabPosition(data, this._orientation);
            // Remove the dragging class from the tab so it can be transitioned.
            data.tab.classList.remove('lm-mod-dragging');
            // Parse the transition duration for releasing the tab.
            let duration = Private$7.parseTransitionDuration(data.tab);
            // Complete the release on a timer to allow the tab to transition.
            setTimeout(() => {
                // Do nothing if the drag has been aborted.
                if (data.dragAborted) {
                    return;
                }
                // Clear the drag data reference.
                this._dragData = null;
                // Reset the positions of the tabs.
                Private$7.resetTabPositions(this.contentNode.children, this._orientation);
                // Clear the cursor grab.
                data.override.dispose();
                // Remove the remaining dragging style.
                this.removeClass('lm-mod-dragging');
                // If the tab was not moved, there is nothing else to do.
                let i = data.index;
                let j = data.targetIndex;
                if (j === -1 || i === j) {
                    return;
                }
                // Move the title to the new locations.
                algorithm.ArrayExt.move(this._titles, i, j);
                // Adjust the current index for the move.
                this._adjustCurrentForMove(i, j);
                // Emit the tab moved signal.
                this._tabMoved.emit({
                    fromIndex: i,
                    toIndex: j,
                    title: this._titles[j]
                });
                // Update the tabs immediately to prevent flicker.
                messaging.MessageLoop.sendMessage(this, Widget.Msg.UpdateRequest);
            }, duration);
        }
        /**
         * Release the mouse and restore the non-dragged tab positions.
         */
        _releaseMouse() {
            // Do nothing if no drag is in progress.
            let data = this._dragData;
            if (!data) {
                return;
            }
            // Clear the drag data reference.
            this._dragData = null;
            // Remove the extra document event listeners.
            this.document.removeEventListener('pointermove', this, true);
            this.document.removeEventListener('pointerup', this, true);
            this.document.removeEventListener('keydown', this, true);
            this.document.removeEventListener('contextmenu', this, true);
            // Indicate the drag has been aborted. This allows the mouse
            // event handlers to return early when the drag is canceled.
            data.dragAborted = true;
            // If the drag is not active, there's nothing more to do.
            if (!data.dragActive) {
                return;
            }
            // Reset the tabs to their non-dragged positions.
            Private$7.resetTabPositions(this.contentNode.children, this._orientation);
            // Clear the cursor override.
            data.override.dispose();
            // Clear the dragging style classes.
            data.tab.classList.remove('lm-mod-dragging');
            this.removeClass('lm-mod-dragging');
        }
        /**
         * Adjust the current index for a tab insert operation.
         *
         * This method accounts for the tab bar's insertion behavior when
         * adjusting the current index and emitting the changed signal.
         */
        _adjustCurrentForInsert(i, title) {
            // Lookup commonly used variables.
            let ct = this.currentTitle;
            let ci = this._currentIndex;
            let bh = this.insertBehavior;
            // TODO: do we need to do an update to update the aria-selected attribute?
            // Handle the behavior where the new tab is always selected,
            // or the behavior where the new tab is selected if needed.
            if (bh === 'select-tab' || (bh === 'select-tab-if-needed' && ci === -1)) {
                this._currentIndex = i;
                this._previousTitle = ct;
                this._currentChanged.emit({
                    previousIndex: ci,
                    previousTitle: ct,
                    currentIndex: i,
                    currentTitle: title
                });
                return;
            }
            // Otherwise, silently adjust the current index if needed.
            if (ci >= i) {
                this._currentIndex++;
            }
        }
        /**
         * Handle the `'click'` event for the tab bar.
         */
        _evtClick(event) {
            const request = this._pendingTouchCloseRequest;
            if (!request) {
                return;
            }
            this._pendingTouchCloseRequest = null;
            event.preventDefault();
            event.stopPropagation();
            const tab = this.contentNode.children[request.index];
            const icon = tab === null || tab === void 0 ? void 0 : tab.querySelector(this.renderer.closeIconSelector);
            if (this._titles[request.index] !== request.title ||
                !icon ||
                !icon.contains(event.target)) {
                this._clearTabSizeFreezeState();
                return;
            }
            this._tabCloseRequested.emit(request);
        }
        /**
         * Adjust the current index for a tab move operation.
         *
         * This method will not cause the actual current tab to change.
         * It silently adjusts the index to account for the given move.
         */
        _adjustCurrentForMove(i, j) {
            if (this._currentIndex === i) {
                this._currentIndex = j;
            }
            else if (this._currentIndex < i && this._currentIndex >= j) {
                this._currentIndex++;
            }
            else if (this._currentIndex > i && this._currentIndex <= j) {
                this._currentIndex--;
            }
        }
        /**
         * Adjust the current index for a tab remove operation.
         *
         * This method accounts for the tab bar's remove behavior when
         * adjusting the current index and emitting the changed signal.
         */
        _adjustCurrentForRemove(i, title) {
            // Lookup commonly used variables.
            let ci = this._currentIndex;
            let bh = this.removeBehavior;
            // Silently adjust the index if the current tab is not removed.
            if (ci !== i) {
                if (ci > i) {
                    this._currentIndex--;
                }
                return;
            }
            // TODO: do we need to do an update to adjust the aria-selected value?
            // No tab gets selected if the tab bar is empty.
            if (this._titles.length === 0) {
                this._currentIndex = -1;
                this._currentChanged.emit({
                    previousIndex: i,
                    previousTitle: title,
                    currentIndex: -1,
                    currentTitle: null
                });
                return;
            }
            // Handle behavior where the next sibling tab is selected.
            if (bh === 'select-tab-after') {
                this._currentIndex = Math.min(i, this._titles.length - 1);
                this._currentChanged.emit({
                    previousIndex: i,
                    previousTitle: title,
                    currentIndex: this._currentIndex,
                    currentTitle: this.currentTitle
                });
                return;
            }
            // Handle behavior where the previous sibling tab is selected.
            if (bh === 'select-tab-before') {
                this._currentIndex = Math.max(0, i - 1);
                this._currentChanged.emit({
                    previousIndex: i,
                    previousTitle: title,
                    currentIndex: this._currentIndex,
                    currentTitle: this.currentTitle
                });
                return;
            }
            // Handle behavior where the previous history tab is selected.
            if (bh === 'select-previous-tab') {
                if (this._previousTitle) {
                    this._currentIndex = this._titles.indexOf(this._previousTitle);
                    this._previousTitle = null;
                }
                else {
                    this._currentIndex = Math.min(i, this._titles.length - 1);
                }
                this._currentChanged.emit({
                    previousIndex: i,
                    previousTitle: title,
                    currentIndex: this._currentIndex,
                    currentTitle: this.currentTitle
                });
                return;
            }
            // Otherwise, no tab gets selected.
            this._currentIndex = -1;
            this._currentChanged.emit({
                previousIndex: i,
                previousTitle: title,
                currentIndex: -1,
                currentTitle: null
            });
        }
        /**
         * Handle the `changed` signal of a title object.
         */
        _onTitleChanged(sender) {
            this.update();
        }
        /**
         * Handle the `'pointerleave'` event for the tab bar.
         *
         * This restores the natural tab sizing after tabs were frozen
         * due to a close-icon click.
         */
        _evtPointerLeave(event) {
            // Do nothing if tab sizes are not currently frozen.
            if (!this._tabSizeFrozen) {
                return;
            }
            // Unfreeze tab sizes.
            this._tabSizeFrozen = false;
            this._frozenTabWidths = null;
            // Start a new unfreeze run and clear stale listeners/timers.
            this._unfreezeRunID++;
            let runID = this._unfreezeRunID;
            this._clearUnfreezeTransitionState();
            // Add the unfreezing class to enable a smooth width transition.
            this.addClass('lm-mod-unfreezing');
            // Clear the inline width on all tabs, triggering the CSS transition.
            let tabs = this.contentNode.children;
            for (let i = 0, n = tabs.length; i < n; ++i) {
                let tab = tabs[i];
                tab.style.width = '';
                tab.style.flexBasis = '';
            }
            if (tabs.length === 0) {
                this.removeClass('lm-mod-unfreezing');
            }
            else {
                let onTransitionDone = (event) => {
                    let propertyName = event.propertyName;
                    if (propertyName === 'width' || propertyName === 'flex-basis') {
                        this._finalizeUnfreezeRun(runID);
                    }
                };
                this._unfreezeTransitionListener = onTransitionDone;
                this.node.addEventListener('transitionend', onTransitionDone);
                this.node.addEventListener('transitioncancel', onTransitionDone);
                this._unfreezeFallbackTimerID = window.setTimeout(() => {
                    this._finalizeUnfreezeRun(runID);
                }, 2000);
            }
            // Schedule an update to re-render the tabs at natural size.
            this.update();
        }
        /**
         * Apply a frozen size to a tab.
         *
         * The width is mirrored to `flex-basis` so frozen sizing works even when
         * themes define tab sizing through flex rules.
         */
        _setFrozenTabSize(tab, width) {
            let size = `${width}px`;
            tab.style.width = size;
            tab.style.flexBasis = size;
        }
        /**
         * Finish an in-flight unfreeze transition run.
         */
        _finalizeUnfreezeRun(runID) {
            if (runID !== this._unfreezeRunID) {
                return;
            }
            this._clearUnfreezeTransitionState();
            this.removeClass('lm-mod-unfreezing');
        }
        /**
         * Remove unfreeze listeners and timers.
         */
        _clearUnfreezeTransitionState() {
            if (this._unfreezeTransitionListener) {
                this.node.removeEventListener('transitionend', this._unfreezeTransitionListener);
                this.node.removeEventListener('transitioncancel', this._unfreezeTransitionListener);
                this._unfreezeTransitionListener = null;
            }
            if (this._unfreezeFallbackTimerID !== 0) {
                clearTimeout(this._unfreezeFallbackTimerID);
                this._unfreezeFallbackTimerID = 0;
            }
        }
        /**
         * Clear transient tab-size freeze state and styles.
         */
        _clearTabSizeFreezeState() {
            this._tabSizeFrozen = false;
            this._frozenTabWidths = null;
            this.removeClass('lm-mod-unfreezing');
            let tabs = this.contentNode.children;
            for (let i = 0, n = tabs.length; i < n; ++i) {
                let tab = tabs[i];
                tab.style.width = '';
                tab.style.flexBasis = '';
            }
        }
    }
    /**
     * The namespace for the `TabBar` class statics.
     */
    (function (TabBar) {
        /**
         * The default implementation of `IRenderer`.
         *
         * #### Notes
         * Subclasses are free to reimplement rendering methods as needed.
         */
        class Renderer {
            constructor() {
                /**
                 * A selector which matches the close icon node in a tab.
                 */
                this.closeIconSelector = '.lm-TabBar-tabCloseIcon';
                this._tabID = 0;
                this._tabKeys = new WeakMap();
                this._uuid = ++Renderer._nInstance;
            }
            /**
             * Render the virtual element for a tab.
             *
             * @param data - The data to use for rendering the tab.
             *
             * @returns A virtual element representing the tab.
             */
            renderTab(data) {
                let title = data.title.caption;
                let key = this.createTabKey(data);
                let id = key;
                let style = this.createTabStyle(data);
                let className = this.createTabClass(data);
                let dataset = this.createTabDataset(data);
                let aria = this.createTabARIA(data);
                if (data.title.closable) {
                    return virtualdom.h.li({ id, key, className, title, style, dataset, ...aria }, this.renderIcon(data), this.renderLabel(data), this.renderCloseIcon(data));
                }
                else {
                    return virtualdom.h.li({ id, key, className, title, style, dataset, ...aria }, this.renderIcon(data), this.renderLabel(data));
                }
            }
            /**
             * Render the icon element for a tab.
             *
             * @param data - The data to use for rendering the tab.
             *
             * @returns A virtual element representing the tab icon.
             */
            renderIcon(data) {
                const { title } = data;
                let className = this.createIconClass(data);
                // If title.icon is undefined, it will be ignored.
                return virtualdom.h.div({ className }, title.icon, title.iconLabel);
            }
            /**
             * Render the label element for a tab.
             *
             * @param data - The data to use for rendering the tab.
             *
             * @returns A virtual element representing the tab label.
             */
            renderLabel(data) {
                return virtualdom.h.div({ className: 'lm-TabBar-tabLabel' }, data.title.label);
            }
            /**
             * Render the close icon element for a tab.
             *
             * @param data - The data to use for rendering the tab.
             *
             * @returns A virtual element representing the tab close icon.
             */
            renderCloseIcon(data) {
                return virtualdom.h.div({ className: 'lm-TabBar-tabCloseIcon' });
            }
            /**
             * Create a unique render key for the tab.
             *
             * @param data - The data to use for the tab.
             *
             * @returns The unique render key for the tab.
             *
             * #### Notes
             * This method caches the key against the tab title the first time
             * the key is generated. This enables efficient rendering of moved
             * tabs and avoids subtle hover style artifacts.
             */
            createTabKey(data) {
                let key = this._tabKeys.get(data.title);
                if (key === undefined) {
                    key = `tab-key-${this._uuid}-${this._tabID++}`;
                    this._tabKeys.set(data.title, key);
                }
                return key;
            }
            /**
             * Create the inline style object for a tab.
             *
             * @param data - The data to use for the tab.
             *
             * @returns The inline style data for the tab.
             */
            createTabStyle(data) {
                return { zIndex: `${data.zIndex}` };
            }
            /**
             * Create the class name for the tab.
             *
             * @param data - The data to use for the tab.
             *
             * @returns The full class name for the tab.
             */
            createTabClass(data) {
                let name = 'lm-TabBar-tab';
                if (data.title.className) {
                    name += ` ${data.title.className}`;
                }
                if (data.title.closable) {
                    name += ' lm-mod-closable';
                }
                if (data.current) {
                    name += ' lm-mod-current';
                }
                return name;
            }
            /**
             * Create the dataset for a tab.
             *
             * @param data - The data to use for the tab.
             *
             * @returns The dataset for the tab.
             */
            createTabDataset(data) {
                return data.title.dataset;
            }
            /**
             * Create the ARIA attributes for a tab.
             *
             * @param data - The data to use for the tab.
             *
             * @returns The ARIA attributes for the tab.
             */
            createTabARIA(data) {
                var _a;
                return {
                    role: 'tab',
                    'aria-selected': data.current.toString(),
                    tabindex: `${(_a = data.tabIndex) !== null && _a !== void 0 ? _a : '-1'}`
                };
            }
            /**
             * Create the class name for the tab icon.
             *
             * @param data - The data to use for the tab.
             *
             * @returns The full class name for the tab icon.
             */
            createIconClass(data) {
                let name = 'lm-TabBar-tabIcon';
                let extra = data.title.iconClass;
                return extra ? `${name} ${extra}` : name;
            }
        }
        Renderer._nInstance = 0;
        TabBar.Renderer = Renderer;
        /**
         * The default `Renderer` instance.
         */
        TabBar.defaultRenderer = new Renderer();
        /**
         * A selector which matches the add button node in the tab bar.
         */
        TabBar.addButtonSelector = '.lm-TabBar-addButton';
    })(TabBar || (TabBar = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$7;
    (function (Private) {
        /**
         * The start drag distance threshold.
         */
        Private.DRAG_THRESHOLD = 5;
        /**
         * The detach distance threshold.
         */
        Private.DETACH_THRESHOLD = 20;
        /**
         * Create the DOM node for a tab bar.
         */
        function createNode() {
            let node = document.createElement('div');
            let content = document.createElement('ul');
            content.setAttribute('role', 'tablist');
            content.className = 'lm-TabBar-content';
            node.appendChild(content);
            let add = document.createElement('div');
            add.className = 'lm-TabBar-addButton lm-mod-hidden';
            add.setAttribute('tabindex', '-1');
            add.setAttribute('role', 'button');
            node.appendChild(add);
            return node;
        }
        Private.createNode = createNode;
        /**
         * Coerce a title or options into a real title.
         */
        function asTitle(value) {
            return value instanceof Title ? value : new Title(value);
        }
        Private.asTitle = asTitle;
        /**
         * Parse the transition duration for a tab node.
         */
        function parseTransitionDuration(tab) {
            let style = window.getComputedStyle(tab);
            return 1000 * (parseFloat(style.transitionDuration) || 0);
        }
        Private.parseTransitionDuration = parseTransitionDuration;
        /**
         * Get a snapshot of the current tab layout values.
         */
        function snapTabLayout(tabs, orientation) {
            let layout = new Array(tabs.length);
            for (let i = 0, n = tabs.length; i < n; ++i) {
                let node = tabs[i];
                let style = window.getComputedStyle(node);
                if (orientation === 'horizontal') {
                    layout[i] = {
                        pos: node.offsetLeft,
                        size: node.offsetWidth,
                        margin: parseFloat(style.marginLeft) || 0
                    };
                }
                else {
                    layout[i] = {
                        pos: node.offsetTop,
                        size: node.offsetHeight,
                        margin: parseFloat(style.marginTop) || 0
                    };
                }
            }
            return layout;
        }
        Private.snapTabLayout = snapTabLayout;
        /**
         * Test if the event exceeds the drag threshold.
         */
        function dragExceeded(data, event) {
            let dx = Math.abs(event.clientX - data.pressX);
            let dy = Math.abs(event.clientY - data.pressY);
            return dx >= Private.DRAG_THRESHOLD || dy >= Private.DRAG_THRESHOLD;
        }
        Private.dragExceeded = dragExceeded;
        /**
         * Test if the event exceeds the drag detach threshold.
         */
        function detachExceeded(data, event) {
            let rect = data.contentRect;
            return (event.clientX < rect.left - Private.DETACH_THRESHOLD ||
                event.clientX >= rect.right + Private.DETACH_THRESHOLD ||
                event.clientY < rect.top - Private.DETACH_THRESHOLD ||
                event.clientY >= rect.bottom + Private.DETACH_THRESHOLD);
        }
        Private.detachExceeded = detachExceeded;
        /**
         * Update the relative tab positions and computed target index.
         */
        function layoutTabs(tabs, data, event, orientation) {
            // Compute the orientation-sensitive values.
            let pressPos;
            let localPos;
            let clientPos;
            let clientSize;
            if (orientation === 'horizontal') {
                pressPos = data.pressX;
                localPos = event.clientX - data.contentRect.left;
                clientPos = event.clientX;
                clientSize = data.contentRect.width;
            }
            else {
                pressPos = data.pressY;
                localPos = event.clientY - data.contentRect.top;
                clientPos = event.clientY;
                clientSize = data.contentRect.height;
            }
            // Compute the target data.
            let targetIndex = data.index;
            let targetPos = localPos - data.tabPressPos;
            let targetEnd = targetPos + data.tabSize;
            // Update the relative tab positions.
            for (let i = 0, n = tabs.length; i < n; ++i) {
                let pxPos;
                let layout = data.tabLayout[i];
                let threshold = layout.pos + (layout.size >> 1);
                if (i < data.index && targetPos < threshold) {
                    pxPos = `${data.tabSize + data.tabLayout[i + 1].margin}px`;
                    targetIndex = Math.min(targetIndex, i);
                }
                else if (i > data.index && targetEnd > threshold) {
                    pxPos = `${-data.tabSize - layout.margin}px`;
                    targetIndex = Math.max(targetIndex, i);
                }
                else if (i === data.index) {
                    let ideal = clientPos - pressPos;
                    let limit = clientSize - (data.tabPos + data.tabSize);
                    pxPos = `${Math.max(-data.tabPos, Math.min(ideal, limit))}px`;
                }
                else {
                    pxPos = '';
                }
                if (orientation === 'horizontal') {
                    tabs[i].style.left = pxPos;
                }
                else {
                    tabs[i].style.top = pxPos;
                }
            }
            // Update the computed target index.
            data.targetIndex = targetIndex;
        }
        Private.layoutTabs = layoutTabs;
        /**
         * Position the drag tab at its final resting relative position.
         */
        function finalizeTabPosition(data, orientation) {
            // Compute the orientation-sensitive client size.
            let clientSize;
            if (orientation === 'horizontal') {
                clientSize = data.contentRect.width;
            }
            else {
                clientSize = data.contentRect.height;
            }
            // Compute the ideal final tab position.
            let ideal;
            if (data.targetIndex === data.index) {
                ideal = 0;
            }
            else if (data.targetIndex > data.index) {
                let tgt = data.tabLayout[data.targetIndex];
                ideal = tgt.pos + tgt.size - data.tabSize - data.tabPos;
            }
            else {
                let tgt = data.tabLayout[data.targetIndex];
                ideal = tgt.pos - data.tabPos;
            }
            // Compute the tab position limit.
            let limit = clientSize - (data.tabPos + data.tabSize);
            let final = Math.max(-data.tabPos, Math.min(ideal, limit));
            // Set the final orientation-sensitive position.
            if (orientation === 'horizontal') {
                data.tab.style.left = `${final}px`;
            }
            else {
                data.tab.style.top = `${final}px`;
            }
        }
        Private.finalizeTabPosition = finalizeTabPosition;
        /**
         * Reset the relative positions of the given tabs.
         */
        function resetTabPositions(tabs, orientation) {
            for (const tab of tabs) {
                if (orientation === 'horizontal') {
                    tab.style.left = '';
                }
                else {
                    tab.style.top = '';
                }
            }
        }
        Private.resetTabPositions = resetTabPositions;
    })(Private$7 || (Private$7 = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A layout which provides a flexible docking arrangement.
     *
     * #### Notes
     * The consumer of this layout is responsible for handling all signals
     * from the generated tab bars and managing the visibility of widgets
     * and tab bars as needed.
     */
    class DockLayout extends Layout {
        /**
         * Construct a new dock layout.
         *
         * @param options - The options for initializing the layout.
         */
        constructor(options) {
            super();
            this._spacing = 4;
            this._dirty = false;
            this._root = null;
            this._box = null;
            this._items = new Map();
            this.renderer = options.renderer;
            if (options.spacing !== undefined) {
                this._spacing = Utils$1.clampDimension(options.spacing);
            }
            this._document = options.document || document;
            this._hiddenMode =
                options.hiddenMode !== undefined
                    ? options.hiddenMode
                    : Widget.HiddenMode.Display;
        }
        /**
         * Dispose of the resources held by the layout.
         *
         * #### Notes
         * This will clear and dispose all widgets in the layout.
         */
        dispose() {
            // Get an iterator over the widgets in the layout.
            let widgets = this[Symbol.iterator]();
            // Dispose of the layout items.
            this._items.forEach(item => {
                item.dispose();
            });
            // Clear the layout state before disposing the widgets.
            this._box = null;
            this._root = null;
            this._items.clear();
            // Dispose of the widgets contained in the old layout root.
            for (const widget of widgets) {
                widget.dispose();
            }
            // Dispose of the base class.
            super.dispose();
        }
        /**
         * The method for hiding child widgets.
         *
         * #### Notes
         * If there is only one child widget, `Display` hiding mode will be used
         * regardless of this setting.
         */
        get hiddenMode() {
            return this._hiddenMode;
        }
        set hiddenMode(v) {
            if (this._hiddenMode === v) {
                return;
            }
            this._hiddenMode = v;
            for (const bar of this.tabBars()) {
                if (bar.titles.length > 1) {
                    for (const title of bar.titles) {
                        title.owner.hiddenMode = this._hiddenMode;
                    }
                }
            }
        }
        /**
         * Get the inter-element spacing for the dock layout.
         */
        get spacing() {
            return this._spacing;
        }
        /**
         * Set the inter-element spacing for the dock layout.
         */
        set spacing(value) {
            value = Utils$1.clampDimension(value);
            if (this._spacing === value) {
                return;
            }
            this._spacing = value;
            if (!this.parent) {
                return;
            }
            this.parent.fit();
        }
        /**
         * Whether the dock layout is empty.
         */
        get isEmpty() {
            return this._root === null;
        }
        /**
         * Create an iterator over all widgets in the layout.
         *
         * @returns A new iterator over the widgets in the layout.
         *
         * #### Notes
         * This iterator includes the generated tab bars.
         */
        [Symbol.iterator]() {
            return this._root ? this._root.iterAllWidgets() : algorithm.empty();
        }
        /**
         * Create an iterator over the user widgets in the layout.
         *
         * @returns A new iterator over the user widgets in the layout.
         *
         * #### Notes
         * This iterator does not include the generated tab bars.
         */
        widgets() {
            return this._root ? this._root.iterUserWidgets() : algorithm.empty();
        }
        /**
         * Create an iterator over the selected widgets in the layout.
         *
         * @returns A new iterator over the selected user widgets.
         *
         * #### Notes
         * This iterator yields the widgets corresponding to the current tab
         * of each tab bar in the layout.
         */
        selectedWidgets() {
            return this._root ? this._root.iterSelectedWidgets() : algorithm.empty();
        }
        /**
         * Create an iterator over the tab bars in the layout.
         *
         * @returns A new iterator over the tab bars in the layout.
         *
         * #### Notes
         * This iterator does not include the user widgets.
         */
        tabBars() {
            return this._root ? this._root.iterTabBars() : algorithm.empty();
        }
        /**
         * Create an iterator over the handles in the layout.
         *
         * @returns A new iterator over the handles in the layout.
         */
        handles() {
            return this._root ? this._root.iterHandles() : algorithm.empty();
        }
        /**
         * Move a handle to the given offset position.
         *
         * @param handle - The handle to move.
         *
         * @param offsetX - The desired offset X position of the handle.
         *
         * @param offsetY - The desired offset Y position of the handle.
         *
         * #### Notes
         * If the given handle is not contained in the layout, this is no-op.
         *
         * The handle will be moved as close as possible to the desired
         * position without violating any of the layout constraints.
         *
         * Only one of the coordinates is used depending on the orientation
         * of the handle. This method accepts both coordinates to make it
         * easy to invoke from a mouse move event without needing to know
         * the handle orientation.
         */
        moveHandle(handle, offsetX, offsetY) {
            // Bail early if there is no root or if the handle is hidden.
            let hidden = handle.classList.contains('lm-mod-hidden');
            if (!this._root || hidden) {
                return;
            }
            // Lookup the split node for the handle.
            let data = this._root.findSplitNode(handle);
            if (!data) {
                return;
            }
            // Compute the desired delta movement for the handle.
            let delta;
            if (data.node.orientation === 'horizontal') {
                delta = offsetX - handle.offsetLeft;
            }
            else {
                delta = offsetY - handle.offsetTop;
            }
            // Bail if there is no handle movement.
            if (delta === 0) {
                return;
            }
            // Prevent sibling resizing unless needed.
            data.node.holdSizes();
            // Adjust the sizers to reflect the handle movement.
            exports.BoxEngine.adjust(data.node.sizers, data.index, delta);
            // Update the layout of the widgets.
            if (this.parent) {
                this.parent.update();
            }
        }
        /**
         * Save the current configuration of the dock layout.
         *
         * @returns A new config object for the current layout state.
         *
         * #### Notes
         * The return value can be provided to the `restoreLayout` method
         * in order to restore the layout to its current configuration.
         */
        saveLayout() {
            // Bail early if there is no root.
            if (!this._root) {
                return { main: null };
            }
            // Hold the current sizes in the layout tree.
            this._root.holdAllSizes();
            // Return the layout config.
            return { main: this._root.createConfig() };
        }
        /**
         * Restore the layout to a previously saved configuration.
         *
         * @param config - The layout configuration to restore.
         *
         * #### Notes
         * Widgets which currently belong to the layout but which are not
         * contained in the config will be unparented.
         */
        restoreLayout(config) {
            // Create the widget set for validating the config.
            let widgetSet = new Set();
            // Normalize the main area config and collect the widgets.
            let mainConfig;
            if (config.main) {
                mainConfig = Private$6.normalizeAreaConfig(config.main, widgetSet);
            }
            else {
                mainConfig = null;
            }
            // Create iterators over the old content.
            let oldWidgets = this.widgets();
            let oldTabBars = this.tabBars();
            let oldHandles = this.handles();
            // Clear the root before removing the old content.
            this._root = null;
            // Unparent the old widgets which are not in the new config.
            for (const widget of oldWidgets) {
                if (!widgetSet.has(widget)) {
                    widget.parent = null;
                }
            }
            // Dispose of the old tab bars.
            for (const tabBar of oldTabBars) {
                tabBar.dispose();
            }
            // Remove the old handles.
            for (const handle of oldHandles) {
                if (handle.parentNode) {
                    handle.parentNode.removeChild(handle);
                }
            }
            // Reparent the new widgets to the current parent.
            for (const widget of widgetSet) {
                widget.parent = this.parent;
            }
            // Create the root node for the new config.
            if (mainConfig) {
                this._root = Private$6.realizeAreaConfig(mainConfig, {
                    // Ignoring optional `document` argument as we must reuse `this._document`
                    createTabBar: (document) => this._createTabBar(),
                    createHandle: () => this._createHandle()
                }, this._document);
            }
            else {
                this._root = null;
            }
            // If there is no parent, there is nothing more to do.
            if (!this.parent) {
                return;
            }
            // Attach the new widgets to the parent.
            widgetSet.forEach(widget => {
                this.attachWidget(widget);
            });
            // Post a fit request to the parent.
            this.parent.fit();
        }
        /**
         * Add a widget to the dock layout.
         *
         * @param widget - The widget to add to the dock layout.
         *
         * @param options - The additional options for adding the widget.
         *
         * #### Notes
         * The widget will be moved if it is already contained in the layout.
         *
         * An error will be thrown if the reference widget is invalid.
         */
        addWidget(widget, options = {}) {
            // Parse the options.
            let ref = options.ref || null;
            let mode = options.mode || 'tab-after';
            // Find the tab node which holds the reference widget.
            let refNode = null;
            if (this._root && ref) {
                refNode = this._root.findTabNode(ref);
            }
            // Throw an error if the reference widget is invalid.
            if (ref && !refNode) {
                throw new Error('Reference widget is not in the layout.');
            }
            // Reparent the widget to the current layout parent.
            widget.parent = this.parent;
            // Insert the widget according to the insert mode.
            switch (mode) {
                case 'tab-after':
                    this._insertTab(widget, ref, refNode, true);
                    break;
                case 'tab-before':
                    this._insertTab(widget, ref, refNode, false);
                    break;
                case 'split-top':
                    this._insertSplit(widget, ref, refNode, 'vertical', false);
                    break;
                case 'split-left':
                    this._insertSplit(widget, ref, refNode, 'horizontal', false);
                    break;
                case 'split-right':
                    this._insertSplit(widget, ref, refNode, 'horizontal', true);
                    break;
                case 'split-bottom':
                    this._insertSplit(widget, ref, refNode, 'vertical', true);
                    break;
                case 'merge-top':
                    this._insertSplit(widget, ref, refNode, 'vertical', false, true);
                    break;
                case 'merge-left':
                    this._insertSplit(widget, ref, refNode, 'horizontal', false, true);
                    break;
                case 'merge-right':
                    this._insertSplit(widget, ref, refNode, 'horizontal', true, true);
                    break;
                case 'merge-bottom':
                    this._insertSplit(widget, ref, refNode, 'vertical', true, true);
                    break;
            }
            // Do nothing else if there is no parent widget.
            if (!this.parent) {
                return;
            }
            // Ensure the widget is attached to the parent widget.
            this.attachWidget(widget);
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * Remove a widget from the layout.
         *
         * @param widget - The widget to remove from the layout.
         *
         * #### Notes
         * A widget is automatically removed from the layout when its `parent`
         * is set to `null`. This method should only be invoked directly when
         * removing a widget from a layout which has yet to be installed on a
         * parent widget.
         *
         * This method does *not* modify the widget's `parent`.
         */
        removeWidget(widget) {
            // Remove the widget from its current layout location.
            this._removeWidget(widget);
            // Do nothing else if there is no parent widget.
            if (!this.parent) {
                return;
            }
            // Detach the widget from the parent widget.
            this.detachWidget(widget);
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * Find the tab area which contains the given client position.
         *
         * @param clientX - The client X position of interest.
         *
         * @param clientY - The client Y position of interest.
         *
         * @returns The geometry of the tab area at the given position, or
         *   `null` if there is no tab area at the given position.
         */
        hitTestTabAreas(clientX, clientY) {
            // Bail early if hit testing cannot produce valid results.
            if (!this._root || !this.parent || !this.parent.isVisible) {
                return null;
            }
            // Ensure the parent box sizing data is computed.
            if (!this._box) {
                this._box = domutils.ElementExt.boxSizing(this.parent.node);
            }
            // Convert from client to local coordinates.
            let rect = this.parent.node.getBoundingClientRect();
            let x = clientX - rect.left - this._box.borderLeft;
            let y = clientY - rect.top - this._box.borderTop;
            // Find the tab layout node at the local position.
            let tabNode = this._root.hitTestTabNodes(x, y);
            // Bail if a tab layout node was not found.
            if (!tabNode) {
                return null;
            }
            // Extract the data from the tab node.
            let { tabBar, top, left, width, height } = tabNode;
            // Compute the right and bottom edges of the tab area.
            let borderWidth = this._box.borderLeft + this._box.borderRight;
            let borderHeight = this._box.borderTop + this._box.borderBottom;
            let right = rect.width - borderWidth - (left + width);
            let bottom = rect.height - borderHeight - (top + height);
            // Return the hit test results.
            return { tabBar, x, y, top, left, right, bottom, width, height };
        }
        /**
         * Perform layout initialization which requires the parent widget.
         */
        init() {
            // Perform superclass initialization.
            super.init();
            // Attach each widget to the parent.
            for (const widget of this) {
                this.attachWidget(widget);
            }
            // Attach each handle to the parent.
            for (const handle of this.handles()) {
                this.parent.node.appendChild(handle);
            }
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * Attach the widget to the layout parent widget.
         *
         * @param widget - The widget to attach to the parent.
         *
         * #### Notes
         * This is a no-op if the widget is already attached.
         */
        attachWidget(widget) {
            // Do nothing if the widget is already attached.
            if (this.parent.node === widget.node.parentNode) {
                return;
            }
            // Create the layout item for the widget.
            this._items.set(widget, new LayoutItem(widget));
            // Send a `'before-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            }
            // Add the widget's node to the parent.
            this.parent.node.appendChild(widget.node);
            // Send an `'after-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
            }
        }
        /**
         * Detach the widget from the layout parent widget.
         *
         * @param widget - The widget to detach from the parent.
         *
         * #### Notes
         * This is a no-op if the widget is not attached.
         */
        detachWidget(widget) {
            // Do nothing if the widget is not attached.
            if (this.parent.node !== widget.node.parentNode) {
                return;
            }
            // Send a `'before-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            }
            // Remove the widget's node from the parent.
            this.parent.node.removeChild(widget.node);
            // Send an `'after-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
            }
            // Delete the layout item for the widget.
            let item = this._items.get(widget);
            if (item) {
                this._items.delete(widget);
                item.dispose();
            }
        }
        /**
         * A message handler invoked on a `'before-show'` message.
         */
        onBeforeShow(msg) {
            super.onBeforeShow(msg);
            this.parent.update();
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            super.onBeforeAttach(msg);
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-shown'` message.
         */
        onChildShown(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-hidden'` message.
         */
        onChildHidden(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'resize'` message.
         */
        onResize(msg) {
            if (this.parent.isVisible) {
                this._update(msg.width, msg.height);
            }
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            if (this.parent.isVisible) {
                this._update(-1, -1);
            }
        }
        /**
         * A message handler invoked on a `'fit-request'` message.
         */
        onFitRequest(msg) {
            if (this.parent.isAttached) {
                this._fit();
            }
        }
        /**
         * Remove the specified widget from the layout structure.
         *
         * #### Notes
         * This is a no-op if the widget is not in the layout tree.
         *
         * This does not detach the widget from the parent node.
         */
        _removeWidget(widget) {
            // Bail early if there is no layout root.
            if (!this._root) {
                return;
            }
            // Find the tab node which contains the given widget.
            let tabNode = this._root.findTabNode(widget);
            // Bail early if the tab node is not found.
            if (!tabNode) {
                return;
            }
            Private$6.removeAria(widget);
            // If there are multiple tabs, just remove the widget's tab.
            if (tabNode.tabBar.titles.length > 1) {
                tabNode.tabBar.removeTab(widget.title);
                if (this._hiddenMode === Widget.HiddenMode.Scale &&
                    tabNode.tabBar.titles.length == 1) {
                    const existingWidget = tabNode.tabBar.titles[0].owner;
                    existingWidget.hiddenMode = Widget.HiddenMode.Display;
                }
                return;
            }
            // Otherwise, the tab node needs to be removed...
            // Dispose the tab bar.
            tabNode.tabBar.dispose();
            // Handle the case where the tab node is the root.
            if (this._root === tabNode) {
                this._root = null;
                return;
            }
            // Otherwise, remove the tab node from its parent...
            // Prevent widget resizing unless needed.
            this._root.holdAllSizes();
            // Clear the parent reference on the tab node.
            let splitNode = tabNode.parent;
            tabNode.parent = null;
            // Remove the tab node from its parent split node.
            let i = algorithm.ArrayExt.removeFirstOf(splitNode.children, tabNode);
            let handle = algorithm.ArrayExt.removeAt(splitNode.handles, i);
            algorithm.ArrayExt.removeAt(splitNode.sizers, i);
            // Remove the handle from its parent DOM node.
            if (handle.parentNode) {
                handle.parentNode.removeChild(handle);
            }
            // If there are multiple children, just update the handles.
            if (splitNode.children.length > 1) {
                splitNode.syncHandles();
                return;
            }
            // Otherwise, the split node also needs to be removed...
            // Clear the parent reference on the split node.
            let maybeParent = splitNode.parent;
            splitNode.parent = null;
            // Lookup the remaining child node and handle.
            let childNode = splitNode.children[0];
            let childHandle = splitNode.handles[0];
            // Clear the split node data.
            splitNode.children.length = 0;
            splitNode.handles.length = 0;
            splitNode.sizers.length = 0;
            // Remove the child handle from its parent node.
            if (childHandle.parentNode) {
                childHandle.parentNode.removeChild(childHandle);
            }
            // Handle the case where the split node is the root.
            if (this._root === splitNode) {
                childNode.parent = null;
                this._root = childNode;
                return;
            }
            // Otherwise, move the child node to the parent node...
            let parentNode = maybeParent;
            // Lookup the index of the split node.
            let j = parentNode.children.indexOf(splitNode);
            // Handle the case where the child node is a tab node.
            if (childNode instanceof Private$6.TabLayoutNode) {
                childNode.parent = parentNode;
                parentNode.children[j] = childNode;
                return;
            }
            // Remove the split data from the parent.
            let splitHandle = algorithm.ArrayExt.removeAt(parentNode.handles, j);
            algorithm.ArrayExt.removeAt(parentNode.children, j);
            algorithm.ArrayExt.removeAt(parentNode.sizers, j);
            // Remove the handle from its parent node.
            if (splitHandle.parentNode) {
                splitHandle.parentNode.removeChild(splitHandle);
            }
            // The child node and the split parent node will have the same
            // orientation. Merge the grand-children with the parent node.
            for (let i = 0, n = childNode.children.length; i < n; ++i) {
                let gChild = childNode.children[i];
                let gHandle = childNode.handles[i];
                let gSizer = childNode.sizers[i];
                algorithm.ArrayExt.insert(parentNode.children, j + i, gChild);
                algorithm.ArrayExt.insert(parentNode.handles, j + i, gHandle);
                algorithm.ArrayExt.insert(parentNode.sizers, j + i, gSizer);
                gChild.parent = parentNode;
            }
            // Clear the child node.
            childNode.children.length = 0;
            childNode.handles.length = 0;
            childNode.sizers.length = 0;
            childNode.parent = null;
            // Sync the handles on the parent node.
            parentNode.syncHandles();
        }
        /**
         * Create the tab layout node to hold the widget.
         */
        _createTabNode(widget) {
            let tabNode = new Private$6.TabLayoutNode(this._createTabBar());
            tabNode.tabBar.addTab(widget.title);
            Private$6.addAria(widget, tabNode.tabBar);
            return tabNode;
        }
        /**
         * Insert a widget next to an existing tab.
         *
         * #### Notes
         * This does not attach the widget to the parent widget.
         */
        _insertTab(widget, ref, refNode, after) {
            // Do nothing if the tab is inserted next to itself.
            if (widget === ref) {
                return;
            }
            // Create the root if it does not exist.
            if (!this._root) {
                let tabNode = new Private$6.TabLayoutNode(this._createTabBar());
                tabNode.tabBar.addTab(widget.title);
                this._root = tabNode;
                Private$6.addAria(widget, tabNode.tabBar);
                return;
            }
            // Use the first tab node as the ref node if needed.
            if (!refNode) {
                refNode = this._root.findFirstTabNode();
            }
            // If the widget is not contained in the ref node, ensure it is
            // removed from the layout and hidden before being added again.
            if (refNode.tabBar.titles.indexOf(widget.title) === -1) {
                this._removeWidget(widget);
                widget.hide();
            }
            // Lookup the target index for inserting the tab.
            let index;
            if (ref) {
                index = refNode.tabBar.titles.indexOf(ref.title);
            }
            else {
                index = refNode.tabBar.currentIndex;
            }
            // Using transform create an additional layer in the pixel pipeline
            // to limit the number of layer, it is set only if there is more than one widget.
            if (this._hiddenMode === Widget.HiddenMode.Scale) {
                if (refNode.tabBar.titles.length === 0) {
                    // Singular tab should use display mode to limit number of layers.
                    widget.hiddenMode = Widget.HiddenMode.Display;
                }
                else if (refNode.tabBar.titles.length == 1) {
                    // If we are adding a second tab, switch the existing tab back to scale.
                    const existingWidget = refNode.tabBar.titles[0].owner;
                    existingWidget.hiddenMode = Widget.HiddenMode.Scale;
                }
                else {
                    // For the third and subsequent tabs no special action is needed.
                    widget.hiddenMode = Widget.HiddenMode.Scale;
                }
            }
            else {
                // For all other modes just propagate the current mode.
                widget.hiddenMode = this._hiddenMode;
            }
            // Insert the widget's tab relative to the target index.
            refNode.tabBar.insertTab(index + (after ? 1 : 0), widget.title);
            Private$6.addAria(widget, refNode.tabBar);
        }
        /**
         * Insert a widget as a new split area.
         *
         * #### Notes
         * This does not attach the widget to the parent widget.
         */
        _insertSplit(widget, ref, refNode, orientation, after, merge = false) {
            // Do nothing if there is no effective split.
            if (widget === ref && refNode && refNode.tabBar.titles.length === 1) {
                return;
            }
            // Ensure the widget is removed from the current layout.
            this._removeWidget(widget);
            // Set the root if it does not exist.
            if (!this._root) {
                this._root = this._createTabNode(widget);
                return;
            }
            // If the ref node parent is null, split the root.
            if (!refNode || !refNode.parent) {
                // Ensure the root is split with the correct orientation.
                let root = this._splitRoot(orientation);
                // Determine the insert index for the new tab node.
                let i = after ? root.children.length : 0;
                // Normalize the split node.
                root.normalizeSizes();
                // Create the sizer for new tab node.
                let sizer = Private$6.createSizer(refNode ? 1 : Private$6.GOLDEN_RATIO);
                // Insert the tab node sized to the golden ratio.
                let tabNode = this._createTabNode(widget);
                algorithm.ArrayExt.insert(root.children, i, tabNode);
                algorithm.ArrayExt.insert(root.sizers, i, sizer);
                algorithm.ArrayExt.insert(root.handles, i, this._createHandle());
                tabNode.parent = root;
                // Re-normalize the split node to maintain the ratios.
                root.normalizeSizes();
                // Finally, synchronize the visibility of the handles.
                root.syncHandles();
                return;
            }
            // Lookup the split node for the ref widget.
            let splitNode = refNode.parent;
            // If the split node already had the correct orientation,
            // the widget can be inserted into the split node directly.
            if (splitNode.orientation === orientation) {
                // Find the index of the ref node.
                let i = splitNode.children.indexOf(refNode);
                // Conditionally reuse a tab layout found in the wanted position.
                if (merge) {
                    let j = i + (after ? 1 : -1);
                    let sibling = splitNode.children[j];
                    if (sibling instanceof Private$6.TabLayoutNode) {
                        this._insertTab(widget, null, sibling, true);
                        ++sibling.tabBar.currentIndex;
                        return;
                    }
                }
                // Normalize the split node.
                splitNode.normalizeSizes();
                // Consume half the space for the insert location.
                let s = (splitNode.sizers[i].sizeHint /= 2);
                // Insert the tab node sized to the other half.
                let j = i + (after ? 1 : 0);
                let tabNode = this._createTabNode(widget);
                algorithm.ArrayExt.insert(splitNode.children, j, tabNode);
                algorithm.ArrayExt.insert(splitNode.sizers, j, Private$6.createSizer(s));
                algorithm.ArrayExt.insert(splitNode.handles, j, this._createHandle());
                tabNode.parent = splitNode;
                // Finally, synchronize the visibility of the handles.
                splitNode.syncHandles();
                return;
            }
            // Remove the ref node from the split node.
            let i = algorithm.ArrayExt.removeFirstOf(splitNode.children, refNode);
            // Create a new normalized split node for the children.
            let childNode = new Private$6.SplitLayoutNode(orientation);
            childNode.normalized = true;
            // Add the ref node sized to half the space.
            childNode.children.push(refNode);
            childNode.sizers.push(Private$6.createSizer(0.5));
            childNode.handles.push(this._createHandle());
            refNode.parent = childNode;
            // Add the tab node sized to the other half.
            let j = after ? 1 : 0;
            let tabNode = this._createTabNode(widget);
            algorithm.ArrayExt.insert(childNode.children, j, tabNode);
            algorithm.ArrayExt.insert(childNode.sizers, j, Private$6.createSizer(0.5));
            algorithm.ArrayExt.insert(childNode.handles, j, this._createHandle());
            tabNode.parent = childNode;
            // Synchronize the visibility of the handles.
            childNode.syncHandles();
            // Finally, add the new child node to the original split node.
            algorithm.ArrayExt.insert(splitNode.children, i, childNode);
            childNode.parent = splitNode;
        }
        /**
         * Ensure the root is a split node with the given orientation.
         */
        _splitRoot(orientation) {
            // Bail early if the root already meets the requirements.
            let oldRoot = this._root;
            if (oldRoot instanceof Private$6.SplitLayoutNode) {
                if (oldRoot.orientation === orientation) {
                    return oldRoot;
                }
            }
            // Create a new root node with the specified orientation.
            let newRoot = (this._root = new Private$6.SplitLayoutNode(orientation));
            // Add the old root to the new root.
            if (oldRoot) {
                newRoot.children.push(oldRoot);
                newRoot.sizers.push(Private$6.createSizer(0));
                newRoot.handles.push(this._createHandle());
                oldRoot.parent = newRoot;
            }
            // Return the new root as a convenience.
            return newRoot;
        }
        /**
         * Fit the layout to the total size required by the widgets.
         */
        _fit() {
            // Set up the computed minimum size.
            let minW = 0;
            let minH = 0;
            // Update the size limits for the layout tree.
            if (this._root) {
                let limits = this._root.fit(this._spacing, this._items);
                minW = limits.minWidth;
                minH = limits.minHeight;
            }
            // Update the box sizing and add it to the computed min size.
            let box = (this._box = domutils.ElementExt.boxSizing(this.parent.node));
            minW += box.horizontalSum;
            minH += box.verticalSum;
            // Update the parent's min size constraints.
            let style = this.parent.node.style;
            style.minWidth = `${minW}px`;
            style.minHeight = `${minH}px`;
            // Set the dirty flag to ensure only a single update occurs.
            this._dirty = true;
            // Notify the ancestor that it should fit immediately. This may
            // cause a resize of the parent, fulfilling the required update.
            if (this.parent.parent) {
                messaging.MessageLoop.sendMessage(this.parent.parent, Widget.Msg.FitRequest);
            }
            // If the dirty flag is still set, the parent was not resized.
            // Trigger the required update on the parent widget immediately.
            if (this._dirty) {
                messaging.MessageLoop.sendMessage(this.parent, Widget.Msg.UpdateRequest);
            }
        }
        /**
         * Update the layout position and size of the widgets.
         *
         * The parent offset dimensions should be `-1` if unknown.
         */
        _update(offsetWidth, offsetHeight) {
            // Clear the dirty flag to indicate the update occurred.
            this._dirty = false;
            // Bail early if there is no root layout node.
            if (!this._root) {
                return;
            }
            // Measure the parent if the offset dimensions are unknown.
            if (offsetWidth < 0) {
                offsetWidth = this.parent.node.offsetWidth;
            }
            if (offsetHeight < 0) {
                offsetHeight = this.parent.node.offsetHeight;
            }
            // Ensure the parent box sizing data is computed.
            if (!this._box) {
                this._box = domutils.ElementExt.boxSizing(this.parent.node);
            }
            // Compute the actual layout bounds adjusted for border and padding.
            let x = this._box.paddingTop;
            let y = this._box.paddingLeft;
            let width = offsetWidth - this._box.horizontalSum;
            let height = offsetHeight - this._box.verticalSum;
            // Update the geometry of the layout tree.
            this._root.update(x, y, width, height, this._spacing, this._items);
        }
        /**
         * Create a new tab bar for use by the dock layout.
         *
         * #### Notes
         * The tab bar will be attached to the parent if it exists.
         */
        _createTabBar() {
            // Create the tab bar using the renderer.
            let tabBar = this.renderer.createTabBar(this._document);
            // Enforce necessary tab bar behavior.
            tabBar.orientation = 'horizontal';
            // Attach the tab bar to the parent if possible.
            if (this.parent) {
                this.attachWidget(tabBar);
            }
            // Return the initialized tab bar.
            return tabBar;
        }
        /**
         * Create a new handle for the dock layout.
         *
         * #### Notes
         * The handle will be attached to the parent if it exists.
         */
        _createHandle() {
            // Create the handle using the renderer.
            let handle = this.renderer.createHandle();
            // Initialize the handle layout behavior.
            let style = handle.style;
            style.position = 'absolute';
            style.contain = 'strict';
            style.top = '0';
            style.left = '0';
            style.width = '0';
            style.height = '0';
            // Attach the handle to the parent if it exists.
            if (this.parent) {
                this.parent.node.appendChild(handle);
            }
            // Return the initialized handle.
            return handle;
        }
    }
    /**
     * The namespace for the module implementation details.
     */
    var Private$6;
    (function (Private) {
        /**
         * A fraction used for sizing root panels; ~= `1 / golden_ratio`.
         */
        Private.GOLDEN_RATIO = 0.618;
        /**
         * Create a box sizer with an initial size hint.
         */
        function createSizer(hint) {
            let sizer = new BoxSizer();
            sizer.sizeHint = hint;
            sizer.size = hint;
            return sizer;
        }
        Private.createSizer = createSizer;
        /**
         * Normalize an area config object and collect the visited widgets.
         */
        function normalizeAreaConfig(config, widgetSet) {
            let result;
            if (config.type === 'tab-area') {
                result = normalizeTabAreaConfig(config, widgetSet);
            }
            else {
                result = normalizeSplitAreaConfig(config, widgetSet);
            }
            return result;
        }
        Private.normalizeAreaConfig = normalizeAreaConfig;
        /**
         * Convert a normalized area config into a layout tree.
         */
        function realizeAreaConfig(config, renderer, document) {
            let node;
            if (config.type === 'tab-area') {
                node = realizeTabAreaConfig(config, renderer, document);
            }
            else {
                node = realizeSplitAreaConfig(config, renderer, document);
            }
            return node;
        }
        Private.realizeAreaConfig = realizeAreaConfig;
        /**
         * A layout node which holds the data for a tabbed area.
         */
        class TabLayoutNode {
            /**
             * Construct a new tab layout node.
             *
             * @param tabBar - The tab bar to use for the layout node.
             */
            constructor(tabBar) {
                /**
                 * The parent of the layout node.
                 */
                this.parent = null;
                this._top = 0;
                this._left = 0;
                this._width = 0;
                this._height = 0;
                let tabSizer = new BoxSizer();
                let widgetSizer = new BoxSizer();
                tabSizer.stretch = 0;
                widgetSizer.stretch = 1;
                this.tabBar = tabBar;
                this.sizers = [tabSizer, widgetSizer];
            }
            /**
             * The most recent value for the `top` edge of the layout box.
             */
            get top() {
                return this._top;
            }
            /**
             * The most recent value for the `left` edge of the layout box.
             */
            get left() {
                return this._left;
            }
            /**
             * The most recent value for the `width` of the layout box.
             */
            get width() {
                return this._width;
            }
            /**
             * The most recent value for the `height` of the layout box.
             */
            get height() {
                return this._height;
            }
            /**
             * Create an iterator for all widgets in the layout tree.
             */
            *iterAllWidgets() {
                yield this.tabBar;
                yield* this.iterUserWidgets();
            }
            /**
             * Create an iterator for the user widgets in the layout tree.
             */
            *iterUserWidgets() {
                for (const title of this.tabBar.titles) {
                    yield title.owner;
                }
            }
            /**
             * Create an iterator for the selected widgets in the layout tree.
             */
            *iterSelectedWidgets() {
                let title = this.tabBar.currentTitle;
                if (title) {
                    yield title.owner;
                }
            }
            /**
             * Create an iterator for the tab bars in the layout tree.
             */
            *iterTabBars() {
                yield this.tabBar;
            }
            /**
             * Create an iterator for the handles in the layout tree.
             */
            // eslint-disable-next-line require-yield
            *iterHandles() {
                return;
            }
            /**
             * Find the tab layout node which contains the given widget.
             */
            findTabNode(widget) {
                return this.tabBar.titles.indexOf(widget.title) !== -1 ? this : null;
            }
            /**
             * Find the split layout node which contains the given handle.
             */
            findSplitNode(handle) {
                return null;
            }
            /**
             * Find the first tab layout node in a layout tree.
             */
            findFirstTabNode() {
                return this;
            }
            /**
             * Find the tab layout node which contains the local point.
             */
            hitTestTabNodes(x, y) {
                if (x < this._left || x >= this._left + this._width) {
                    return null;
                }
                if (y < this._top || y >= this._top + this._height) {
                    return null;
                }
                return this;
            }
            /**
             * Create a configuration object for the layout tree.
             */
            createConfig() {
                let widgets = this.tabBar.titles.map(title => title.owner);
                let currentIndex = this.tabBar.currentIndex;
                return { type: 'tab-area', widgets, currentIndex };
            }
            /**
             * Recursively hold all of the sizes in the layout tree.
             *
             * This ignores the sizers of tab layout nodes.
             */
            holdAllSizes() {
                return;
            }
            /**
             * Fit the layout tree.
             */
            fit(spacing, items) {
                // Set up the limit variables.
                let minWidth = 0;
                let minHeight = 0;
                let maxWidth = Infinity;
                let maxHeight = Infinity;
                // Lookup the tab bar layout item.
                let tabBarItem = items.get(this.tabBar);
                // Lookup the widget layout item.
                let current = this.tabBar.currentTitle;
                let widgetItem = current ? items.get(current.owner) : undefined;
                // Lookup the tab bar and widget sizers.
                let [tabBarSizer, widgetSizer] = this.sizers;
                // Update the tab bar limits.
                if (tabBarItem) {
                    tabBarItem.fit();
                }
                // Update the widget limits.
                if (widgetItem) {
                    widgetItem.fit();
                }
                // Update the results and sizer for the tab bar.
                if (tabBarItem && !tabBarItem.isHidden) {
                    minWidth = Math.max(minWidth, tabBarItem.minWidth);
                    minHeight += tabBarItem.minHeight;
                    tabBarSizer.minSize = tabBarItem.minHeight;
                    tabBarSizer.maxSize = tabBarItem.maxHeight;
                }
                else {
                    tabBarSizer.minSize = 0;
                    tabBarSizer.maxSize = 0;
                }
                // Update the results and sizer for the current widget.
                if (widgetItem && !widgetItem.isHidden) {
                    minWidth = Math.max(minWidth, widgetItem.minWidth);
                    minHeight += widgetItem.minHeight;
                    widgetSizer.minSize = widgetItem.minHeight;
                    widgetSizer.maxSize = Infinity;
                }
                else {
                    widgetSizer.minSize = 0;
                    widgetSizer.maxSize = Infinity;
                }
                // Return the computed size limits for the layout node.
                return { minWidth, minHeight, maxWidth, maxHeight };
            }
            /**
             * Update the layout tree.
             */
            update(left, top, width, height, spacing, items) {
                // Update the layout box values.
                this._top = top;
                this._left = left;
                this._width = width;
                this._height = height;
                // Lookup the tab bar layout item.
                let tabBarItem = items.get(this.tabBar);
                // Lookup the widget layout item.
                let current = this.tabBar.currentTitle;
                let widgetItem = current ? items.get(current.owner) : undefined;
                // Distribute the layout space to the sizers.
                exports.BoxEngine.calc(this.sizers, height);
                // Update the tab bar item using the computed size.
                if (tabBarItem && !tabBarItem.isHidden) {
                    let size = this.sizers[0].size;
                    tabBarItem.update(left, top, width, size);
                    top += size;
                }
                // Layout the widget using the computed size.
                if (widgetItem && !widgetItem.isHidden) {
                    let size = this.sizers[1].size;
                    widgetItem.update(left, top, width, size);
                }
            }
        }
        Private.TabLayoutNode = TabLayoutNode;
        /**
         * A layout node which holds the data for a split area.
         */
        class SplitLayoutNode {
            /**
             * Construct a new split layout node.
             *
             * @param orientation - The orientation of the node.
             */
            constructor(orientation) {
                /**
                 * The parent of the layout node.
                 */
                this.parent = null;
                /**
                 * Whether the sizers have been normalized.
                 */
                this.normalized = false;
                /**
                 * The child nodes for the split node.
                 */
                this.children = [];
                /**
                 * The box sizers for the layout children.
                 */
                this.sizers = [];
                /**
                 * The handles for the layout children.
                 */
                this.handles = [];
                this.orientation = orientation;
            }
            /**
             * Create an iterator for all widgets in the layout tree.
             */
            *iterAllWidgets() {
                for (const child of this.children) {
                    yield* child.iterAllWidgets();
                }
            }
            /**
             * Create an iterator for the user widgets in the layout tree.
             */
            *iterUserWidgets() {
                for (const child of this.children) {
                    yield* child.iterUserWidgets();
                }
            }
            /**
             * Create an iterator for the selected widgets in the layout tree.
             */
            *iterSelectedWidgets() {
                for (const child of this.children) {
                    yield* child.iterSelectedWidgets();
                }
            }
            /**
             * Create an iterator for the tab bars in the layout tree.
             */
            *iterTabBars() {
                for (const child of this.children) {
                    yield* child.iterTabBars();
                }
            }
            /**
             * Create an iterator for the handles in the layout tree.
             */
            *iterHandles() {
                yield* this.handles;
                for (const child of this.children) {
                    yield* child.iterHandles();
                }
            }
            /**
             * Find the tab layout node which contains the given widget.
             */
            findTabNode(widget) {
                for (let i = 0, n = this.children.length; i < n; ++i) {
                    let result = this.children[i].findTabNode(widget);
                    if (result) {
                        return result;
                    }
                }
                return null;
            }
            /**
             * Find the split layout node which contains the given handle.
             */
            findSplitNode(handle) {
                let index = this.handles.indexOf(handle);
                if (index !== -1) {
                    return { index, node: this };
                }
                for (let i = 0, n = this.children.length; i < n; ++i) {
                    let result = this.children[i].findSplitNode(handle);
                    if (result) {
                        return result;
                    }
                }
                return null;
            }
            /**
             * Find the first tab layout node in a layout tree.
             */
            findFirstTabNode() {
                if (this.children.length === 0) {
                    return null;
                }
                return this.children[0].findFirstTabNode();
            }
            /**
             * Find the tab layout node which contains the local point.
             */
            hitTestTabNodes(x, y) {
                for (let i = 0, n = this.children.length; i < n; ++i) {
                    let result = this.children[i].hitTestTabNodes(x, y);
                    if (result) {
                        return result;
                    }
                }
                return null;
            }
            /**
             * Create a configuration object for the layout tree.
             */
            createConfig() {
                let orientation = this.orientation;
                let sizes = this.createNormalizedSizes();
                let children = this.children.map(child => child.createConfig());
                return { type: 'split-area', orientation, children, sizes };
            }
            /**
             * Sync the visibility and orientation of the handles.
             */
            syncHandles() {
                this.handles.forEach((handle, i) => {
                    handle.setAttribute('data-orientation', this.orientation);
                    if (i === this.handles.length - 1) {
                        handle.classList.add('lm-mod-hidden');
                    }
                    else {
                        handle.classList.remove('lm-mod-hidden');
                    }
                });
            }
            /**
             * Hold the current sizes of the box sizers.
             *
             * This sets the size hint of each sizer to its current size.
             */
            holdSizes() {
                for (const sizer of this.sizers) {
                    sizer.sizeHint = sizer.size;
                }
            }
            /**
             * Recursively hold all of the sizes in the layout tree.
             *
             * This ignores the sizers of tab layout nodes.
             */
            holdAllSizes() {
                for (const child of this.children) {
                    child.holdAllSizes();
                }
                this.holdSizes();
            }
            /**
             * Normalize the sizes of the split layout node.
             */
            normalizeSizes() {
                // Bail early if the sizers are empty.
                let n = this.sizers.length;
                if (n === 0) {
                    return;
                }
                // Hold the current sizes of the sizers.
                this.holdSizes();
                // Compute the sum of the sizes.
                let sum = this.sizers.reduce((v, sizer) => v + sizer.sizeHint, 0);
                // Normalize the sizes based on the sum.
                if (sum === 0) {
                    for (const sizer of this.sizers) {
                        sizer.size = sizer.sizeHint = 1 / n;
                    }
                }
                else {
                    for (const sizer of this.sizers) {
                        sizer.size = sizer.sizeHint /= sum;
                    }
                }
                // Mark the sizes as normalized.
                this.normalized = true;
            }
            /**
             * Snap the normalized sizes of the split layout node.
             */
            createNormalizedSizes() {
                // Bail early if the sizers are empty.
                let n = this.sizers.length;
                if (n === 0) {
                    return [];
                }
                // Grab the current sizes of the sizers.
                let sizes = this.sizers.map(sizer => sizer.size);
                // Compute the sum of the sizes.
                let sum = sizes.reduce((v, size) => v + size, 0);
                // Normalize the sizes based on the sum.
                if (sum === 0) {
                    for (let i = sizes.length - 1; i > -1; i--) {
                        sizes[i] = 1 / n;
                    }
                }
                else {
                    for (let i = sizes.length - 1; i > -1; i--) {
                        sizes[i] /= sum;
                    }
                }
                // Return the normalized sizes.
                return sizes;
            }
            /**
             * Fit the layout tree.
             */
            fit(spacing, items) {
                // Compute the required fixed space.
                let horizontal = this.orientation === 'horizontal';
                let fixed = Math.max(0, this.children.length - 1) * spacing;
                // Set up the limit variables.
                let minWidth = horizontal ? fixed : 0;
                let minHeight = horizontal ? 0 : fixed;
                let maxWidth = Infinity;
                let maxHeight = Infinity;
                // Fit the children and update the limits.
                for (let i = 0, n = this.children.length; i < n; ++i) {
                    let limits = this.children[i].fit(spacing, items);
                    if (horizontal) {
                        minHeight = Math.max(minHeight, limits.minHeight);
                        minWidth += limits.minWidth;
                        this.sizers[i].minSize = limits.minWidth;
                    }
                    else {
                        minWidth = Math.max(minWidth, limits.minWidth);
                        minHeight += limits.minHeight;
                        this.sizers[i].minSize = limits.minHeight;
                    }
                }
                // Return the computed limits for the layout node.
                return { minWidth, minHeight, maxWidth, maxHeight };
            }
            /**
             * Update the layout tree.
             */
            update(left, top, width, height, spacing, items) {
                // Compute the available layout space.
                let horizontal = this.orientation === 'horizontal';
                let fixed = Math.max(0, this.children.length - 1) * spacing;
                let space = Math.max(0, (horizontal ? width : height) - fixed);
                // De-normalize the sizes if needed.
                if (this.normalized) {
                    for (const sizer of this.sizers) {
                        sizer.sizeHint *= space;
                    }
                    this.normalized = false;
                }
                // Distribute the layout space to the sizers.
                exports.BoxEngine.calc(this.sizers, space);
                // Update the geometry of the child nodes and handles.
                for (let i = 0, n = this.children.length; i < n; ++i) {
                    let child = this.children[i];
                    let size = this.sizers[i].size;
                    let handleStyle = this.handles[i].style;
                    if (horizontal) {
                        child.update(left, top, size, height, spacing, items);
                        left += size;
                        handleStyle.top = `${top}px`;
                        handleStyle.left = `${left}px`;
                        handleStyle.width = `${spacing}px`;
                        handleStyle.height = `${height}px`;
                        left += spacing;
                    }
                    else {
                        child.update(left, top, width, size, spacing, items);
                        top += size;
                        handleStyle.top = `${top}px`;
                        handleStyle.left = `${left}px`;
                        handleStyle.width = `${width}px`;
                        handleStyle.height = `${spacing}px`;
                        top += spacing;
                    }
                }
            }
        }
        Private.SplitLayoutNode = SplitLayoutNode;
        function addAria(widget, tabBar) {
            widget.node.setAttribute('role', 'tabpanel');
            let renderer = tabBar.renderer;
            if (renderer instanceof TabBar.Renderer) {
                let tabId = renderer.createTabKey({
                    title: widget.title,
                    current: false,
                    zIndex: 0
                });
                widget.node.setAttribute('aria-labelledby', tabId);
            }
        }
        Private.addAria = addAria;
        function removeAria(widget) {
            widget.node.removeAttribute('role');
            widget.node.removeAttribute('aria-labelledby');
        }
        Private.removeAria = removeAria;
        /**
         * Normalize a tab area config and collect the visited widgets.
         */
        function normalizeTabAreaConfig(config, widgetSet) {
            // Bail early if there is no content.
            if (config.widgets.length === 0) {
                return null;
            }
            // Setup the filtered widgets array.
            let widgets = [];
            // Filter the config for unique widgets.
            for (const widget of config.widgets) {
                if (!widgetSet.has(widget)) {
                    widgetSet.add(widget);
                    widgets.push(widget);
                }
            }
            // Bail if there are no effective widgets.
            if (widgets.length === 0) {
                return null;
            }
            // Normalize the current index.
            let index = config.currentIndex;
            if (index !== -1 && (index < 0 || index >= widgets.length)) {
                index = 0;
            }
            // Return a normalized config object.
            return { type: 'tab-area', widgets, currentIndex: index };
        }
        /**
         * Normalize a split area config and collect the visited widgets.
         */
        function normalizeSplitAreaConfig(config, widgetSet) {
            // Set up the result variables.
            let orientation = config.orientation;
            let children = [];
            let sizes = [];
            // Normalize the config children.
            for (let i = 0, n = config.children.length; i < n; ++i) {
                // Normalize the child config.
                let child = normalizeAreaConfig(config.children[i], widgetSet);
                // Ignore an empty child.
                if (!child) {
                    continue;
                }
                // Add the child or hoist its content as appropriate.
                if (child.type === 'tab-area' || child.orientation !== orientation) {
                    children.push(child);
                    sizes.push(Math.abs(config.sizes[i] || 0));
                }
                else {
                    children.push(...child.children);
                    sizes.push(...child.sizes);
                }
            }
            // Bail if there are no effective children.
            if (children.length === 0) {
                return null;
            }
            // If there is only one effective child, return that child.
            if (children.length === 1) {
                return children[0];
            }
            // Return a normalized config object.
            return { type: 'split-area', orientation, children, sizes };
        }
        /**
         * Convert a normalized tab area config into a layout tree.
         */
        function realizeTabAreaConfig(config, renderer, document) {
            // Create the tab bar for the layout node.
            let tabBar = renderer.createTabBar(document);
            // Hide each widget and add it to the tab bar.
            for (const widget of config.widgets) {
                widget.hide();
                tabBar.addTab(widget.title);
                Private.addAria(widget, tabBar);
            }
            // Set the current index of the tab bar.
            tabBar.currentIndex = config.currentIndex;
            // Return the new tab layout node.
            return new TabLayoutNode(tabBar);
        }
        /**
         * Convert a normalized split area config into a layout tree.
         */
        function realizeSplitAreaConfig(config, renderer, document) {
            // Create the split layout node.
            let node = new SplitLayoutNode(config.orientation);
            // Add each child to the layout node.
            config.children.forEach((child, i) => {
                // Create the child data for the layout node.
                let childNode = realizeAreaConfig(child, renderer, document);
                let sizer = createSizer(config.sizes[i]);
                let handle = renderer.createHandle();
                // Add the child data to the layout node.
                node.children.push(childNode);
                node.handles.push(handle);
                node.sizers.push(sizer);
                // Update the parent for the child node.
                childNode.parent = node;
            });
            // Synchronize the handle state for the layout node.
            node.syncHandles();
            // Normalize the sizes for the layout node.
            node.normalizeSizes();
            // Return the new layout node.
            return node;
        }
    })(Private$6 || (Private$6 = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A widget which provides a flexible docking area for widgets.
     *
     * #### Notes
     * See also the related [example](../../examples/dockpanel/index.html) and
     * its [source](https://github.com/jupyterlab/lumino/tree/main/examples/example-dockpanel).
     */
    class DockPanel extends Widget {
        /**
         * Construct a new dock panel.
         *
         * @param options - The options for initializing the panel.
         */
        constructor(options = {}) {
            super();
            this._drag = null;
            this._tabsMovable = true;
            this._tabsConstrained = false;
            this._addButtonEnabled = false;
            this._pressData = null;
            this._layoutModified = new signaling.Signal(this);
            this._addRequested = new signaling.Signal(this);
            this.addClass('lm-DockPanel');
            this._document = options.document || document;
            this._mode = options.mode || 'multiple-document';
            this._renderer = options.renderer || DockPanel.defaultRenderer;
            this._edges = options.edges || Private$5.DEFAULT_EDGES;
            if (options.tabsMovable !== undefined) {
                this._tabsMovable = options.tabsMovable;
            }
            if (options.tabsConstrained !== undefined) {
                this._tabsConstrained = options.tabsConstrained;
            }
            if (options.addButtonEnabled !== undefined) {
                this._addButtonEnabled = options.addButtonEnabled;
            }
            // Toggle the CSS mode attribute.
            this.dataset['mode'] = this._mode;
            // Create the delegate renderer for the layout.
            let renderer = {
                createTabBar: () => this._createTabBar(),
                createHandle: () => this._createHandle()
            };
            // Set up the dock layout for the panel.
            this.layout = new DockLayout({
                document: this._document,
                renderer,
                spacing: options.spacing,
                hiddenMode: options.hiddenMode
            });
            // Set up the overlay drop indicator.
            this.overlay = options.overlay || new DockPanel.Overlay();
            this.node.appendChild(this.overlay.node);
        }
        /**
         * Dispose of the resources held by the panel.
         */
        dispose() {
            // Ensure the mouse is released.
            this._releaseMouse();
            // Hide the overlay.
            this.overlay.hide(0);
            // Cancel a drag if one is in progress.
            if (this._drag) {
                this._drag.dispose();
            }
            // Dispose of the base class.
            super.dispose();
        }
        /**
         * The method for hiding widgets.
         */
        get hiddenMode() {
            return this.layout.hiddenMode;
        }
        /**
         * Set the method for hiding widgets.
         */
        set hiddenMode(v) {
            this.layout.hiddenMode = v;
        }
        /**
         * A signal emitted when the layout configuration is modified.
         *
         * #### Notes
         * This signal is emitted whenever the current layout configuration
         * may have changed.
         *
         * This signal is emitted asynchronously in a collapsed fashion, so
         * that multiple synchronous modifications results in only a single
         * emit of the signal.
         */
        get layoutModified() {
            return this._layoutModified;
        }
        /**
         * A signal emitted when the add button on a tab bar is clicked.
         *
         */
        get addRequested() {
            return this._addRequested;
        }
        /**
         * The renderer used by the dock panel.
         */
        get renderer() {
            return this.layout.renderer;
        }
        /**
         * Get the spacing between the widgets.
         */
        get spacing() {
            return this.layout.spacing;
        }
        /**
         * Set the spacing between the widgets.
         */
        set spacing(value) {
            this.layout.spacing = value;
        }
        /**
         * Get the mode for the dock panel.
         */
        get mode() {
            return this._mode;
        }
        /**
         * Set the mode for the dock panel.
         *
         * #### Notes
         * Changing the mode is a destructive operation with respect to the
         * panel's layout configuration. If layout state must be preserved,
         * save the current layout config before changing the mode.
         */
        set mode(value) {
            // Bail early if the mode does not change.
            if (this._mode === value) {
                return;
            }
            // Update the internal mode.
            this._mode = value;
            // Toggle the CSS mode attribute.
            this.dataset['mode'] = value;
            // Get the layout for the panel.
            let layout = this.layout;
            // Configure the layout for the specified mode.
            switch (value) {
                case 'multiple-document':
                    for (const tabBar of layout.tabBars()) {
                        tabBar.show();
                    }
                    break;
                case 'single-document':
                    layout.restoreLayout(Private$5.createSingleDocumentConfig(this));
                    break;
                default:
                    throw 'unreachable';
            }
            // Schedule an emit of the layout modified signal.
            messaging.MessageLoop.postMessage(this, Private$5.LayoutModified);
        }
        /**
         * Whether the tabs can be dragged / moved at runtime.
         */
        get tabsMovable() {
            return this._tabsMovable;
        }
        /**
         * Enable / Disable draggable / movable tabs.
         */
        set tabsMovable(value) {
            this._tabsMovable = value;
            for (const tabBar of this.tabBars()) {
                tabBar.tabsMovable = value;
            }
        }
        /**
         * Whether the tabs are constrained to their source dock panel
         */
        get tabsConstrained() {
            return this._tabsConstrained;
        }
        /**
         * Constrain/Allow tabs to be dragged outside of this dock panel
         */
        set tabsConstrained(value) {
            this._tabsConstrained = value;
        }
        /**
         * Whether the add buttons for each tab bar are enabled.
         */
        get addButtonEnabled() {
            return this._addButtonEnabled;
        }
        /**
         * Set whether the add buttons for each tab bar are enabled.
         */
        set addButtonEnabled(value) {
            this._addButtonEnabled = value;
            for (const tabBar of this.tabBars()) {
                tabBar.addButtonEnabled = value;
            }
        }
        /**
         * Whether the dock panel is empty.
         */
        get isEmpty() {
            return this.layout.isEmpty;
        }
        /**
         * Create an iterator over the user widgets in the panel.
         *
         * @returns A new iterator over the user widgets in the panel.
         *
         * #### Notes
         * This iterator does not include the generated tab bars.
         */
        *widgets() {
            yield* this.layout.widgets();
        }
        /**
         * Create an iterator over the selected widgets in the panel.
         *
         * @returns A new iterator over the selected user widgets.
         *
         * #### Notes
         * This iterator yields the widgets corresponding to the current tab
         * of each tab bar in the panel.
         */
        *selectedWidgets() {
            yield* this.layout.selectedWidgets();
        }
        /**
         * Create an iterator over the tab bars in the panel.
         *
         * @returns A new iterator over the tab bars in the panel.
         *
         * #### Notes
         * This iterator does not include the user widgets.
         */
        *tabBars() {
            yield* this.layout.tabBars();
        }
        /**
         * Create an iterator over the handles in the panel.
         *
         * @returns A new iterator over the handles in the panel.
         */
        *handles() {
            yield* this.layout.handles();
        }
        /**
         * Select a specific widget in the dock panel.
         *
         * @param widget - The widget of interest.
         *
         * #### Notes
         * This will make the widget the current widget in its tab area.
         */
        selectWidget(widget) {
            // Find the tab bar which contains the widget.
            let tabBar = algorithm.find(this.tabBars(), bar => {
                return bar.titles.indexOf(widget.title) !== -1;
            });
            // Throw an error if no tab bar is found.
            if (!tabBar) {
                throw new Error('Widget is not contained in the dock panel.');
            }
            // Ensure the widget is the current widget.
            tabBar.currentTitle = widget.title;
        }
        /**
         * Activate a specified widget in the dock panel.
         *
         * @param widget - The widget of interest.
         *
         * #### Notes
         * This will select and activate the given widget.
         */
        activateWidget(widget) {
            this.selectWidget(widget);
            widget.activate();
        }
        /**
         * Save the current layout configuration of the dock panel.
         *
         * @returns A new config object for the current layout state.
         *
         * #### Notes
         * The return value can be provided to the `restoreLayout` method
         * in order to restore the layout to its current configuration.
         */
        saveLayout() {
            return this.layout.saveLayout();
        }
        /**
         * Restore the layout to a previously saved configuration.
         *
         * @param config - The layout configuration to restore.
         *
         * #### Notes
         * Widgets which currently belong to the layout but which are not
         * contained in the config will be unparented.
         *
         * The dock panel automatically reverts to `'multiple-document'`
         * mode when a layout config is restored.
         */
        restoreLayout(config) {
            // Reset the mode.
            this._mode = 'multiple-document';
            // Restore the layout.
            this.layout.restoreLayout(config);
            // Flush the message loop on IE and Edge to prevent flicker.
            if (domutils.Platform.IS_EDGE || domutils.Platform.IS_IE) {
                messaging.MessageLoop.flush();
            }
            // Schedule an emit of the layout modified signal.
            messaging.MessageLoop.postMessage(this, Private$5.LayoutModified);
        }
        /**
         * Add a widget to the dock panel.
         *
         * @param widget - The widget to add to the dock panel.
         *
         * @param options - The additional options for adding the widget.
         *
         * #### Notes
         * If the panel is in single document mode, the options are ignored
         * and the widget is always added as tab in the hidden tab bar.
         */
        addWidget(widget, options = {}) {
            // Add the widget to the layout.
            if (this._mode === 'single-document') {
                this.layout.addWidget(widget);
            }
            else {
                this.layout.addWidget(widget, options);
            }
            // Schedule an emit of the layout modified signal.
            messaging.MessageLoop.postMessage(this, Private$5.LayoutModified);
        }
        /**
         * Process a message sent to the widget.
         *
         * @param msg - The message sent to the widget.
         */
        processMessage(msg) {
            if (msg.type === 'layout-modified') {
                this._layoutModified.emit(undefined);
            }
            else {
                super.processMessage(msg);
            }
        }
        /**
         * Handle the DOM events for the dock panel.
         *
         * @param event - The DOM event sent to the panel.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the panel's DOM node. It should
         * not be called directly by user code.
         */
        handleEvent(event) {
            switch (event.type) {
                case 'lm-dragenter':
                    this._evtDragEnter(event);
                    break;
                case 'lm-dragleave':
                    this._evtDragLeave(event);
                    break;
                case 'lm-dragover':
                    this._evtDragOver(event);
                    break;
                case 'lm-drop':
                    this._evtDrop(event);
                    break;
                case 'pointerdown':
                    this._evtPointerDown(event);
                    break;
                case 'pointermove':
                    this._evtPointerMove(event);
                    break;
                case 'pointerup':
                    this._evtPointerUp(event);
                    break;
                case 'keydown':
                    this._evtKeyDown(event);
                    break;
                case 'contextmenu':
                    event.preventDefault();
                    event.stopPropagation();
                    break;
            }
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            this.node.addEventListener('lm-dragenter', this);
            this.node.addEventListener('lm-dragleave', this);
            this.node.addEventListener('lm-dragover', this);
            this.node.addEventListener('lm-drop', this);
            this.node.addEventListener('pointerdown', this);
        }
        /**
         * A message handler invoked on an `'after-detach'` message.
         */
        onAfterDetach(msg) {
            this.node.removeEventListener('lm-dragenter', this);
            this.node.removeEventListener('lm-dragleave', this);
            this.node.removeEventListener('lm-dragover', this);
            this.node.removeEventListener('lm-drop', this);
            this.node.removeEventListener('pointerdown', this);
            this._releaseMouse();
        }
        /**
         * A message handler invoked on a `'child-added'` message.
         */
        onChildAdded(msg) {
            // Ignore the generated tab bars.
            if (Private$5.isGeneratedTabBarProperty.get(msg.child)) {
                return;
            }
            // Add the widget class to the child.
            msg.child.addClass('lm-DockPanel-widget');
        }
        /**
         * A message handler invoked on a `'child-removed'` message.
         */
        onChildRemoved(msg) {
            // Ignore the generated tab bars.
            if (Private$5.isGeneratedTabBarProperty.get(msg.child)) {
                return;
            }
            // Remove the widget class from the child.
            msg.child.removeClass('lm-DockPanel-widget');
            // Schedule an emit of the layout modified signal.
            messaging.MessageLoop.postMessage(this, Private$5.LayoutModified);
        }
        /**
         * Handle the `'lm-dragenter'` event for the dock panel.
         */
        _evtDragEnter(event) {
            // If the factory mime type is present, mark the event as
            // handled in order to get the rest of the drag events.
            if (event.mimeData.hasData('application/vnd.lumino.widget-factory')) {
                event.preventDefault();
                event.stopPropagation();
            }
        }
        /**
         * Handle the `'lm-dragleave'` event for the dock panel.
         */
        _evtDragLeave(event) {
            // Mark the event as handled.
            event.preventDefault();
            if (this._tabsConstrained && event.source !== this)
                return;
            event.stopPropagation();
            // The new target might be a descendant, so we might still handle the drop.
            // Hide asynchronously so that if a lm-dragover event bubbles up to us, the
            // hide is cancelled by the lm-dragover handler's show overlay logic.
            this.overlay.hide(1);
        }
        /**
         * Handle the `'lm-dragover'` event for the dock panel.
         */
        _evtDragOver(event) {
            // Mark the event as handled.
            event.preventDefault();
            // Show the drop indicator overlay and update the drop
            // action based on the drop target zone under the mouse.
            if ((this._tabsConstrained && event.source !== this) ||
                this._showOverlay(event.clientX, event.clientY) === 'invalid') {
                event.dropAction = 'none';
            }
            else {
                event.stopPropagation();
                event.dropAction = event.proposedAction;
            }
        }
        /**
         * Handle the `'lm-drop'` event for the dock panel.
         */
        _evtDrop(event) {
            // Mark the event as handled.
            event.preventDefault();
            // Hide the drop indicator overlay.
            this.overlay.hide(0);
            // Bail if the proposed action is to do nothing.
            if (event.proposedAction === 'none') {
                event.dropAction = 'none';
                return;
            }
            // Find the drop target under the mouse.
            let { clientX, clientY } = event;
            let { zone, target } = Private$5.findDropTarget(this, clientX, clientY, this._edges);
            // Bail if the drop zone is invalid.
            if ((this._tabsConstrained && event.source !== this) ||
                zone === 'invalid') {
                event.dropAction = 'none';
                return;
            }
            // Bail if the factory mime type has invalid data.
            let mimeData = event.mimeData;
            let factory = mimeData.getData('application/vnd.lumino.widget-factory');
            if (typeof factory !== 'function') {
                event.dropAction = 'none';
                return;
            }
            // Bail if the factory does not produce a widget.
            let widget = factory();
            if (!(widget instanceof Widget)) {
                event.dropAction = 'none';
                return;
            }
            // Bail if the widget is an ancestor of the dock panel.
            if (widget.contains(this)) {
                event.dropAction = 'none';
                return;
            }
            // Find the reference widget for the drop target.
            let ref = target ? Private$5.getDropRef(target.tabBar) : null;
            // Add the widget according to the indicated drop zone.
            switch (zone) {
                case 'root-all':
                    this.addWidget(widget);
                    break;
                case 'root-top':
                    this.addWidget(widget, { mode: 'split-top' });
                    break;
                case 'root-left':
                    this.addWidget(widget, { mode: 'split-left' });
                    break;
                case 'root-right':
                    this.addWidget(widget, { mode: 'split-right' });
                    break;
                case 'root-bottom':
                    this.addWidget(widget, { mode: 'split-bottom' });
                    break;
                case 'widget-all':
                    this.addWidget(widget, { mode: 'tab-after', ref });
                    break;
                case 'widget-top':
                    this.addWidget(widget, { mode: 'split-top', ref });
                    break;
                case 'widget-left':
                    this.addWidget(widget, { mode: 'split-left', ref });
                    break;
                case 'widget-right':
                    this.addWidget(widget, { mode: 'split-right', ref });
                    break;
                case 'widget-bottom':
                    this.addWidget(widget, { mode: 'split-bottom', ref });
                    break;
                case 'widget-tab':
                    this.addWidget(widget, { mode: 'tab-after', ref });
                    break;
                default:
                    throw 'unreachable';
            }
            // Accept the proposed drop action.
            event.dropAction = event.proposedAction;
            // Stop propagation if we have not bailed so far.
            event.stopPropagation();
            // Activate the dropped widget.
            this.activateWidget(widget);
        }
        /**
         * Handle the `'keydown'` event for the dock panel.
         */
        _evtKeyDown(event) {
            // Stop input events during drag.
            event.preventDefault();
            event.stopPropagation();
            // Release the mouse if `Escape` is pressed.
            if (event.keyCode === 27) {
                // Finalize the mouse release.
                this._releaseMouse();
                // Schedule an emit of the layout modified signal.
                messaging.MessageLoop.postMessage(this, Private$5.LayoutModified);
            }
        }
        /**
         * Handle the `'pointerdown'` event for the dock panel.
         */
        _evtPointerDown(event) {
            // Do nothing if the left mouse button is not pressed.
            if (event.button !== 0) {
                return;
            }
            // Find the handle which contains the mouse target, if any.
            let layout = this.layout;
            let target = event.target;
            let handle = algorithm.find(layout.handles(), handle => handle.contains(target));
            if (!handle) {
                return;
            }
            // Stop the event when a handle is pressed.
            event.preventDefault();
            event.stopPropagation();
            // Add the extra document listeners.
            this._document.addEventListener('keydown', this, true);
            this._document.addEventListener('pointerup', this, true);
            this._document.addEventListener('pointermove', this, true);
            this._document.addEventListener('contextmenu', this, true);
            // Compute the offset deltas for the handle press.
            let rect = handle.getBoundingClientRect();
            let deltaX = event.clientX - rect.left;
            let deltaY = event.clientY - rect.top;
            // Override the cursor and store the press data.
            let style = window.getComputedStyle(handle);
            let override = dragdrop.Drag.overrideCursor(style.cursor, this._document);
            this._pressData = { handle, deltaX, deltaY, override };
        }
        /**
         * Handle the `'pointermove'` event for the dock panel.
         */
        _evtPointerMove(event) {
            // Bail early if no drag is in progress.
            if (!this._pressData) {
                return;
            }
            // Stop the event when dragging a handle.
            event.preventDefault();
            event.stopPropagation();
            // Compute the desired offset position for the handle.
            let rect = this.node.getBoundingClientRect();
            let xPos = event.clientX - rect.left - this._pressData.deltaX;
            let yPos = event.clientY - rect.top - this._pressData.deltaY;
            // Set the handle as close to the desired position as possible.
            let layout = this.layout;
            layout.moveHandle(this._pressData.handle, xPos, yPos);
        }
        /**
         * Handle the `'pointerup'` event for the dock panel.
         */
        _evtPointerUp(event) {
            // Do nothing if the left mouse button is not released.
            if (event.button !== 0) {
                return;
            }
            // Stop the event when releasing a handle.
            event.preventDefault();
            event.stopPropagation();
            // Finalize the mouse release.
            this._releaseMouse();
            // Schedule an emit of the layout modified signal.
            messaging.MessageLoop.postMessage(this, Private$5.LayoutModified);
        }
        /**
         * Release the mouse grab for the dock panel.
         */
        _releaseMouse() {
            // Bail early if no drag is in progress.
            if (!this._pressData) {
                return;
            }
            // Clear the override cursor.
            this._pressData.override.dispose();
            this._pressData = null;
            // Remove the extra document listeners.
            this._document.removeEventListener('keydown', this, true);
            this._document.removeEventListener('pointerup', this, true);
            this._document.removeEventListener('pointermove', this, true);
            this._document.removeEventListener('contextmenu', this, true);
        }
        /**
         * Show the overlay indicator at the given client position.
         *
         * Returns the drop zone at the specified client position.
         *
         * #### Notes
         * If the position is not over a valid zone, the overlay is hidden.
         */
        _showOverlay(clientX, clientY) {
            // Find the dock target for the given client position.
            let { zone, target } = Private$5.findDropTarget(this, clientX, clientY, this._edges);
            // If the drop zone is invalid, hide the overlay and bail.
            if (zone === 'invalid') {
                this.overlay.hide(100);
                return zone;
            }
            // Setup the variables needed to compute the overlay geometry.
            let top;
            let left;
            let right;
            let bottom;
            let box = domutils.ElementExt.boxSizing(this.node); // TODO cache this?
            let rect = this.node.getBoundingClientRect();
            // Compute the overlay geometry based on the dock zone.
            switch (zone) {
                case 'root-all':
                    top = box.paddingTop;
                    left = box.paddingLeft;
                    right = box.paddingRight;
                    bottom = box.paddingBottom;
                    break;
                case 'root-top':
                    top = box.paddingTop;
                    left = box.paddingLeft;
                    right = box.paddingRight;
                    bottom = rect.height * Private$5.GOLDEN_RATIO;
                    break;
                case 'root-left':
                    top = box.paddingTop;
                    left = box.paddingLeft;
                    right = rect.width * Private$5.GOLDEN_RATIO;
                    bottom = box.paddingBottom;
                    break;
                case 'root-right':
                    top = box.paddingTop;
                    left = rect.width * Private$5.GOLDEN_RATIO;
                    right = box.paddingRight;
                    bottom = box.paddingBottom;
                    break;
                case 'root-bottom':
                    top = rect.height * Private$5.GOLDEN_RATIO;
                    left = box.paddingLeft;
                    right = box.paddingRight;
                    bottom = box.paddingBottom;
                    break;
                case 'widget-all':
                    top = target.top;
                    left = target.left;
                    right = target.right;
                    bottom = target.bottom;
                    break;
                case 'widget-top':
                    top = target.top;
                    left = target.left;
                    right = target.right;
                    bottom = target.bottom + target.height / 2;
                    break;
                case 'widget-left':
                    top = target.top;
                    left = target.left;
                    right = target.right + target.width / 2;
                    bottom = target.bottom;
                    break;
                case 'widget-right':
                    top = target.top;
                    left = target.left + target.width / 2;
                    right = target.right;
                    bottom = target.bottom;
                    break;
                case 'widget-bottom':
                    top = target.top + target.height / 2;
                    left = target.left;
                    right = target.right;
                    bottom = target.bottom;
                    break;
                case 'widget-tab': {
                    const tabHeight = target.tabBar.node.getBoundingClientRect().height;
                    top = target.top;
                    left = target.left;
                    right = target.right;
                    bottom = target.bottom + target.height - tabHeight;
                    break;
                }
                default:
                    throw 'unreachable';
            }
            // Show the overlay with the computed geometry.
            this.overlay.show({ top, left, right, bottom });
            // Finally, return the computed drop zone.
            return zone;
        }
        /**
         * Create a new tab bar for use by the panel.
         */
        _createTabBar() {
            // Create the tab bar.
            let tabBar = this._renderer.createTabBar(this._document);
            // Set the generated tab bar property for the tab bar.
            Private$5.isGeneratedTabBarProperty.set(tabBar, true);
            // Hide the tab bar when in single document mode.
            if (this._mode === 'single-document') {
                tabBar.hide();
            }
            // Enforce necessary tab bar behavior.
            // TODO do we really want to enforce *all* of these?
            tabBar.tabsMovable = this._tabsMovable;
            tabBar.allowDeselect = false;
            tabBar.addButtonEnabled = this._addButtonEnabled;
            tabBar.removeBehavior = 'select-previous-tab';
            tabBar.insertBehavior = 'select-tab-if-needed';
            // Connect the signal handlers for the tab bar.
            tabBar.tabMoved.connect(this._onTabMoved, this);
            tabBar.currentChanged.connect(this._onCurrentChanged, this);
            tabBar.tabCloseRequested.connect(this._onTabCloseRequested, this);
            tabBar.tabDetachRequested.connect(this._onTabDetachRequested, this);
            tabBar.tabActivateRequested.connect(this._onTabActivateRequested, this);
            tabBar.addRequested.connect(this._onTabAddRequested, this);
            // Return the initialized tab bar.
            return tabBar;
        }
        /**
         * Create a new handle for use by the panel.
         */
        _createHandle() {
            return this._renderer.createHandle();
        }
        /**
         * Handle the `tabMoved` signal from a tab bar.
         */
        _onTabMoved() {
            messaging.MessageLoop.postMessage(this, Private$5.LayoutModified);
        }
        /**
         * Handle the `currentChanged` signal from a tab bar.
         */
        _onCurrentChanged(sender, args) {
            // Extract the previous and current title from the args.
            let { previousTitle, currentTitle } = args;
            // Hide the previous widget.
            if (previousTitle) {
                previousTitle.owner.hide();
            }
            // Show the current widget.
            if (currentTitle) {
                currentTitle.owner.show();
            }
            // Flush the message loop on IE and Edge to prevent flicker.
            if (domutils.Platform.IS_EDGE || domutils.Platform.IS_IE) {
                messaging.MessageLoop.flush();
            }
            // Schedule an emit of the layout modified signal.
            messaging.MessageLoop.postMessage(this, Private$5.LayoutModified);
        }
        /**
         * Handle the `addRequested` signal from a tab bar.
         */
        _onTabAddRequested(sender) {
            this._addRequested.emit(sender);
        }
        /**
         * Handle the `tabActivateRequested` signal from a tab bar.
         */
        _onTabActivateRequested(sender, args) {
            args.title.owner.activate();
        }
        /**
         * Handle the `tabCloseRequested` signal from a tab bar.
         */
        _onTabCloseRequested(sender, args) {
            args.title.owner.close();
        }
        /**
         * Handle the `tabDetachRequested` signal from a tab bar.
         */
        _onTabDetachRequested(sender, args) {
            // Do nothing if a drag is already in progress.
            if (this._drag) {
                return;
            }
            // Release the tab bar's hold on the mouse.
            sender.releaseMouse();
            // Extract the data from the args.
            let { title, tab, clientX, clientY, offset } = args;
            // Setup the mime data for the drag operation.
            let mimeData = new coreutils.MimeData();
            let factory = () => title.owner;
            mimeData.setData('application/vnd.lumino.widget-factory', factory);
            // Create the drag image for the drag operation.
            let dragImage = tab.cloneNode(true);
            if (offset) {
                dragImage.style.top = `-${offset.y}px`;
                dragImage.style.left = `-${offset.x}px`;
            }
            // Create the drag object to manage the drag-drop operation.
            this._drag = new dragdrop.Drag({
                document: this._document,
                mimeData,
                dragImage,
                proposedAction: 'move',
                supportedActions: 'move',
                source: this
            });
            // Hide the tab node in the original tab.
            tab.classList.add('lm-mod-hidden');
            let cleanup = () => {
                this._drag = null;
                tab.classList.remove('lm-mod-hidden');
            };
            // Start the drag operation and cleanup when done.
            this._drag.start(clientX, clientY).then(cleanup);
        }
    }
    /**
     * The namespace for the `DockPanel` class statics.
     */
    (function (DockPanel) {
        /**
         * A concrete implementation of `IOverlay`.
         *
         * This is the default overlay implementation for a dock panel.
         */
        class Overlay {
            /**
             * Construct a new overlay.
             */
            constructor() {
                this._timer = -1;
                this._hidden = true;
                this.node = document.createElement('div');
                this.node.classList.add('lm-DockPanel-overlay');
                this.node.classList.add('lm-mod-hidden');
                this.node.style.position = 'absolute';
                this.node.style.contain = 'strict';
            }
            /**
             * Show the overlay using the given overlay geometry.
             *
             * @param geo - The desired geometry for the overlay.
             */
            show(geo) {
                // Update the position of the overlay.
                let style = this.node.style;
                style.top = `${geo.top}px`;
                style.left = `${geo.left}px`;
                style.right = `${geo.right}px`;
                style.bottom = `${geo.bottom}px`;
                // Clear any pending hide timer.
                clearTimeout(this._timer);
                this._timer = -1;
                // If the overlay is already visible, we're done.
                if (!this._hidden) {
                    return;
                }
                // Clear the hidden flag.
                this._hidden = false;
                // Finally, show the overlay.
                this.node.classList.remove('lm-mod-hidden');
            }
            /**
             * Hide the overlay node.
             *
             * @param delay - The delay (in ms) before hiding the overlay.
             *   A delay value <= 0 will hide the overlay immediately.
             */
            hide(delay) {
                // Do nothing if the overlay is already hidden.
                if (this._hidden) {
                    return;
                }
                // Hide immediately if the delay is <= 0.
                if (delay <= 0) {
                    clearTimeout(this._timer);
                    this._timer = -1;
                    this._hidden = true;
                    this.node.classList.add('lm-mod-hidden');
                    return;
                }
                // Do nothing if a hide is already pending.
                if (this._timer !== -1) {
                    return;
                }
                // Otherwise setup the hide timer.
                this._timer = window.setTimeout(() => {
                    this._timer = -1;
                    this._hidden = true;
                    this.node.classList.add('lm-mod-hidden');
                }, delay);
            }
        }
        DockPanel.Overlay = Overlay;
        /**
         * The default implementation of `IRenderer`.
         */
        class Renderer {
            /**
             * Create a new tab bar for use with a dock panel.
             *
             * @returns A new tab bar for a dock panel.
             */
            createTabBar(document) {
                let bar = new TabBar({ document });
                bar.addClass('lm-DockPanel-tabBar');
                return bar;
            }
            /**
             * Create a new handle node for use with a dock panel.
             *
             * @returns A new handle node for a dock panel.
             */
            createHandle() {
                let handle = document.createElement('div');
                handle.className = 'lm-DockPanel-handle';
                return handle;
            }
        }
        DockPanel.Renderer = Renderer;
        /**
         * The default `Renderer` instance.
         */
        DockPanel.defaultRenderer = new Renderer();
    })(DockPanel || (DockPanel = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$5;
    (function (Private) {
        /**
         * A fraction used for sizing root panels; ~= `1 / golden_ratio`.
         */
        Private.GOLDEN_RATIO = 0.618;
        /**
         * The default sizes for the edge drop zones, in pixels.
         */
        Private.DEFAULT_EDGES = {
            /**
             * The size of the top edge dock zone for the root panel, in pixels.
             * This is different from the others to distinguish between the top
             * tab bar and the top root zone.
             */
            top: 12,
            /**
             * The size of the edge dock zone for the root panel, in pixels.
             */
            right: 40,
            /**
             * The size of the edge dock zone for the root panel, in pixels.
             */
            bottom: 40,
            /**
             * The size of the edge dock zone for the root panel, in pixels.
             */
            left: 40
        };
        /**
         * A singleton `'layout-modified'` conflatable message.
         */
        Private.LayoutModified = new messaging.ConflatableMessage('layout-modified');
        /**
         * An attached property used to track generated tab bars.
         */
        Private.isGeneratedTabBarProperty = new properties.AttachedProperty({
            name: 'isGeneratedTabBar',
            create: () => false
        });
        /**
         * Create a single document config for the widgets in a dock panel.
         */
        function createSingleDocumentConfig(panel) {
            // Return an empty config if the panel is empty.
            if (panel.isEmpty) {
                return { main: null };
            }
            // Get a flat array of the widgets in the panel.
            let widgets = Array.from(panel.widgets());
            // Get the first selected widget in the panel.
            let selected = panel.selectedWidgets().next().value;
            // Compute the current index for the new config.
            let currentIndex = selected ? widgets.indexOf(selected) : -1;
            // Return the single document config.
            return { main: { type: 'tab-area', widgets, currentIndex } };
        }
        Private.createSingleDocumentConfig = createSingleDocumentConfig;
        /**
         * Find the drop target at the given client position.
         */
        function findDropTarget(panel, clientX, clientY, edges) {
            // Bail if the mouse is not over the dock panel.
            if (!domutils.ElementExt.hitTest(panel.node, clientX, clientY)) {
                return { zone: 'invalid', target: null };
            }
            // Look up the layout for the panel.
            let layout = panel.layout;
            // If the layout is empty, indicate the entire root drop zone.
            if (layout.isEmpty) {
                return { zone: 'root-all', target: null };
            }
            // Test the edge zones when in multiple document mode.
            if (panel.mode === 'multiple-document') {
                // Get the client rect for the dock panel.
                let panelRect = panel.node.getBoundingClientRect();
                // Compute the distance to each edge of the panel.
                let pl = clientX - panelRect.left + 1;
                let pt = clientY - panelRect.top + 1;
                let pr = panelRect.right - clientX;
                let pb = panelRect.bottom - clientY;
                // Find the minimum distance to an edge.
                let pd = Math.min(pt, pr, pb, pl);
                // Return a root zone if the mouse is within an edge.
                switch (pd) {
                    case pt:
                        if (pt < edges.top) {
                            return { zone: 'root-top', target: null };
                        }
                        break;
                    case pr:
                        if (pr < edges.right) {
                            return { zone: 'root-right', target: null };
                        }
                        break;
                    case pb:
                        if (pb < edges.bottom) {
                            return { zone: 'root-bottom', target: null };
                        }
                        break;
                    case pl:
                        if (pl < edges.left) {
                            return { zone: 'root-left', target: null };
                        }
                        break;
                    default:
                        throw 'unreachable';
                }
            }
            // Hit test the dock layout at the given client position.
            let target = layout.hitTestTabAreas(clientX, clientY);
            // Bail if no target area was found.
            if (!target) {
                return { zone: 'invalid', target: null };
            }
            // Return the whole tab area when in single document mode.
            if (panel.mode === 'single-document') {
                return { zone: 'widget-all', target };
            }
            // Compute the distance to each edge of the tab area.
            let al = target.x - target.left + 1;
            let at = target.y - target.top + 1;
            let ar = target.left + target.width - target.x;
            let ab = target.top + target.height - target.y;
            const tabHeight = target.tabBar.node.getBoundingClientRect().height;
            if (at < tabHeight) {
                return { zone: 'widget-tab', target };
            }
            // Get the X and Y edge sizes for the area.
            let rx = Math.round(target.width / 3);
            let ry = Math.round(target.height / 3);
            // If the mouse is not within an edge, indicate the entire area.
            if (al > rx && ar > rx && at > ry && ab > ry) {
                return { zone: 'widget-all', target };
            }
            // Scale the distances by the slenderness ratio.
            al /= rx;
            at /= ry;
            ar /= rx;
            ab /= ry;
            // Find the minimum distance to the area edge.
            let ad = Math.min(al, at, ar, ab);
            // Find the widget zone for the area edge.
            let zone;
            switch (ad) {
                case al:
                    zone = 'widget-left';
                    break;
                case at:
                    zone = 'widget-top';
                    break;
                case ar:
                    zone = 'widget-right';
                    break;
                case ab:
                    zone = 'widget-bottom';
                    break;
                default:
                    throw 'unreachable';
            }
            // Return the final drop target.
            return { zone, target };
        }
        Private.findDropTarget = findDropTarget;
        /**
         * Get the drop reference widget for a tab bar.
         */
        function getDropRef(tabBar) {
            if (tabBar.titles.length === 0) {
                return null;
            }
            if (tabBar.currentTitle) {
                return tabBar.currentTitle.owner;
            }
            return tabBar.titles[tabBar.titles.length - 1].owner;
        }
        Private.getDropRef = getDropRef;
    })(Private$5 || (Private$5 = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A class which tracks focus among a set of widgets.
     *
     * This class is useful when code needs to keep track of the most
     * recently focused widget(s) among a set of related widgets.
     */
    class FocusTracker {
        constructor() {
            this._counter = 0;
            this._widgets = [];
            this._activeWidget = null;
            this._currentWidget = null;
            this._numbers = new Map();
            this._nodes = new Map();
            this._activeChanged = new signaling.Signal(this);
            this._currentChanged = new signaling.Signal(this);
        }
        /**
         * Dispose of the resources held by the tracker.
         */
        dispose() {
            // Do nothing if the tracker is already disposed.
            if (this._counter < 0) {
                return;
            }
            // Mark the tracker as disposed.
            this._counter = -1;
            // Clear the connections for the tracker.
            signaling.Signal.clearData(this);
            // Remove all event listeners.
            for (const widget of this._widgets) {
                widget.node.removeEventListener('focus', this, true);
                widget.node.removeEventListener('blur', this, true);
            }
            // Clear the internal data structures.
            this._activeWidget = null;
            this._currentWidget = null;
            this._nodes.clear();
            this._numbers.clear();
            this._widgets.length = 0;
        }
        /**
         * A signal emitted when the current widget has changed.
         */
        get currentChanged() {
            return this._currentChanged;
        }
        /**
         * A signal emitted when the active widget has changed.
         */
        get activeChanged() {
            return this._activeChanged;
        }
        /**
         * A flag indicating whether the tracker is disposed.
         */
        get isDisposed() {
            return this._counter < 0;
        }
        /**
         * The current widget in the tracker.
         *
         * #### Notes
         * The current widget is the widget among the tracked widgets which
         * has the *descendant node* which has most recently been focused.
         *
         * The current widget will not be updated if the node loses focus. It
         * will only be updated when a different tracked widget gains focus.
         *
         * If the current widget is removed from the tracker, the previous
         * current widget will be restored.
         *
         * This behavior is intended to follow a user's conceptual model of
         * a semantically "current" widget, where the "last thing of type X"
         * to be interacted with is the "current instance of X", regardless
         * of whether that instance still has focus.
         */
        get currentWidget() {
            return this._currentWidget;
        }
        /**
         * The active widget in the tracker.
         *
         * #### Notes
         * The active widget is the widget among the tracked widgets which
         * has the *descendant node* which is currently focused.
         */
        get activeWidget() {
            return this._activeWidget;
        }
        /**
         * A read only array of the widgets being tracked.
         */
        get widgets() {
            return this._widgets;
        }
        /**
         * Get the focus number for a particular widget in the tracker.
         *
         * @param widget - The widget of interest.
         *
         * @returns The focus number for the given widget, or `-1` if the
         *   widget has not had focus since being added to the tracker, or
         *   is not contained by the tracker.
         *
         * #### Notes
         * The focus number indicates the relative order in which the widgets
         * have gained focus. A widget with a larger number has gained focus
         * more recently than a widget with a smaller number.
         *
         * The `currentWidget` will always have the largest focus number.
         *
         * All widgets start with a focus number of `-1`, which indicates that
         * the widget has not been focused since being added to the tracker.
         */
        focusNumber(widget) {
            let n = this._numbers.get(widget);
            return n === undefined ? -1 : n;
        }
        /**
         * Test whether the focus tracker contains a given widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns `true` if the widget is tracked, `false` otherwise.
         */
        has(widget) {
            return this._numbers.has(widget);
        }
        /**
         * Add a widget to the focus tracker.
         *
         * @param widget - The widget of interest.
         *
         * #### Notes
         * A widget will be automatically removed from the tracker if it
         * is disposed after being added.
         *
         * If the widget is already tracked, this is a no-op.
         */
        add(widget) {
            // Do nothing if the widget is already tracked.
            if (this._numbers.has(widget)) {
                return;
            }
            // Test whether the widget has focus.
            let focused = widget.node.contains(document.activeElement);
            // Set up the initial focus number.
            let n = focused ? this._counter++ : -1;
            // Add the widget to the internal data structures.
            this._widgets.push(widget);
            this._numbers.set(widget, n);
            this._nodes.set(widget.node, widget);
            // Set up the event listeners. The capturing phase must be used
            // since the 'focus' and 'blur' events don't bubble and Firefox
            // doesn't support the 'focusin' or 'focusout' events.
            widget.node.addEventListener('focus', this, true);
            widget.node.addEventListener('blur', this, true);
            // Connect the disposed signal handler.
            widget.disposed.connect(this._onWidgetDisposed, this);
            // Set the current and active widgets if needed.
            if (focused) {
                this._setWidgets(widget, widget);
            }
        }
        /**
         * Remove a widget from the focus tracker.
         *
         * #### Notes
         * If the widget is the `currentWidget`, the previous current widget
         * will become the new `currentWidget`.
         *
         * A widget will be automatically removed from the tracker if it
         * is disposed after being added.
         *
         * If the widget is not tracked, this is a no-op.
         */
        remove(widget) {
            // Bail early if the widget is not tracked.
            if (!this._numbers.has(widget)) {
                return;
            }
            // Disconnect the disposed signal handler.
            widget.disposed.disconnect(this._onWidgetDisposed, this);
            // Remove the event listeners.
            widget.node.removeEventListener('focus', this, true);
            widget.node.removeEventListener('blur', this, true);
            // Remove the widget from the internal data structures.
            algorithm.ArrayExt.removeFirstOf(this._widgets, widget);
            this._nodes.delete(widget.node);
            this._numbers.delete(widget);
            // Bail early if the widget is not the current widget.
            if (this._currentWidget !== widget) {
                return;
            }
            // Filter the widgets for those which have had focus.
            let valid = this._widgets.filter(w => this._numbers.get(w) !== -1);
            // Get the valid widget with the max focus number.
            let previous = algorithm.max(valid, (first, second) => {
                let a = this._numbers.get(first);
                let b = this._numbers.get(second);
                return a - b;
            }) || null;
            // Set the current and active widgets.
            this._setWidgets(previous, null);
        }
        /**
         * Handle the DOM events for the focus tracker.
         *
         * @param event - The DOM event sent to the panel.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the tracked nodes. It should
         * not be called directly by user code.
         */
        handleEvent(event) {
            switch (event.type) {
                case 'focus':
                    this._evtFocus(event);
                    break;
                case 'blur':
                    this._evtBlur(event);
                    break;
            }
        }
        /**
         * Set the current and active widgets for the tracker.
         */
        _setWidgets(current, active) {
            // Swap the current widget.
            let oldCurrent = this._currentWidget;
            this._currentWidget = current;
            // Swap the active widget.
            let oldActive = this._activeWidget;
            this._activeWidget = active;
            // Emit the `currentChanged` signal if needed.
            if (oldCurrent !== current) {
                this._currentChanged.emit({ oldValue: oldCurrent, newValue: current });
            }
            // Emit the `activeChanged` signal if needed.
            if (oldActive !== active) {
                this._activeChanged.emit({ oldValue: oldActive, newValue: active });
            }
        }
        /**
         * Handle the `'focus'` event for a tracked widget.
         */
        _evtFocus(event) {
            // Find the widget which gained focus, which is known to exist.
            let widget = this._nodes.get(event.currentTarget);
            // Update the focus number if necessary.
            if (widget !== this._currentWidget) {
                this._numbers.set(widget, this._counter++);
            }
            // Set the current and active widgets.
            this._setWidgets(widget, widget);
        }
        /**
         * Handle the `'blur'` event for a tracked widget.
         */
        _evtBlur(event) {
            // Find the widget which lost focus, which is known to exist.
            let widget = this._nodes.get(event.currentTarget);
            // Get the node which being focused after this blur.
            let focusTarget = event.relatedTarget;
            // If no other node is being focused, clear the active widget.
            if (!focusTarget) {
                this._setWidgets(this._currentWidget, null);
                return;
            }
            // Bail if the focus widget is not changing.
            if (widget.node.contains(focusTarget)) {
                return;
            }
            // If no tracked widget is being focused, clear the active widget.
            if (!algorithm.find(this._widgets, w => w.node.contains(focusTarget))) {
                this._setWidgets(this._currentWidget, null);
                return;
            }
        }
        /**
         * Handle the `disposed` signal for a tracked widget.
         */
        _onWidgetDisposed(sender) {
            this.remove(sender);
        }
    }

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A layout which arranges its widgets in a grid.
     */
    class GridLayout extends Layout {
        /**
         * Construct a new grid layout.
         *
         * @param options - The options for initializing the layout.
         */
        constructor(options = {}) {
            super(options);
            this._dirty = false;
            this._rowSpacing = 4;
            this._columnSpacing = 4;
            this._items = [];
            this._rowStarts = [];
            this._columnStarts = [];
            this._rowSizers = [new BoxSizer()];
            this._columnSizers = [new BoxSizer()];
            this._box = null;
            if (options.rowCount !== undefined) {
                Private$4.reallocSizers(this._rowSizers, options.rowCount);
            }
            if (options.columnCount !== undefined) {
                Private$4.reallocSizers(this._columnSizers, options.columnCount);
            }
            if (options.rowSpacing !== undefined) {
                this._rowSpacing = Private$4.clampValue(options.rowSpacing);
            }
            if (options.columnSpacing !== undefined) {
                this._columnSpacing = Private$4.clampValue(options.columnSpacing);
            }
        }
        /**
         * Dispose of the resources held by the layout.
         */
        dispose() {
            // Dispose of the widgets and layout items.
            for (const item of this._items) {
                let widget = item.widget;
                item.dispose();
                widget.dispose();
            }
            // Clear the layout state.
            this._box = null;
            this._items.length = 0;
            this._rowStarts.length = 0;
            this._rowSizers.length = 0;
            this._columnStarts.length = 0;
            this._columnSizers.length = 0;
            // Dispose of the rest of the layout.
            super.dispose();
        }
        /**
         * Get the number of rows in the layout.
         */
        get rowCount() {
            return this._rowSizers.length;
        }
        /**
         * Set the number of rows in the layout.
         *
         * #### Notes
         * The minimum row count is `1`.
         */
        set rowCount(value) {
            // Do nothing if the row count does not change.
            if (value === this.rowCount) {
                return;
            }
            // Reallocate the row sizers.
            Private$4.reallocSizers(this._rowSizers, value);
            // Schedule a fit of the parent.
            if (this.parent) {
                this.parent.fit();
            }
        }
        /**
         * Get the number of columns in the layout.
         */
        get columnCount() {
            return this._columnSizers.length;
        }
        /**
         * Set the number of columns in the layout.
         *
         * #### Notes
         * The minimum column count is `1`.
         */
        set columnCount(value) {
            // Do nothing if the column count does not change.
            if (value === this.columnCount) {
                return;
            }
            // Reallocate the column sizers.
            Private$4.reallocSizers(this._columnSizers, value);
            // Schedule a fit of the parent.
            if (this.parent) {
                this.parent.fit();
            }
        }
        /**
         * Get the row spacing for the layout.
         */
        get rowSpacing() {
            return this._rowSpacing;
        }
        /**
         * Set the row spacing for the layout.
         */
        set rowSpacing(value) {
            // Clamp the spacing to the allowed range.
            value = Private$4.clampValue(value);
            // Bail if the spacing does not change
            if (this._rowSpacing === value) {
                return;
            }
            // Update the internal spacing.
            this._rowSpacing = value;
            // Schedule a fit of the parent.
            if (this.parent) {
                this.parent.fit();
            }
        }
        /**
         * Get the column spacing for the layout.
         */
        get columnSpacing() {
            return this._columnSpacing;
        }
        /**
         * Set the col spacing for the layout.
         */
        set columnSpacing(value) {
            // Clamp the spacing to the allowed range.
            value = Private$4.clampValue(value);
            // Bail if the spacing does not change
            if (this._columnSpacing === value) {
                return;
            }
            // Update the internal spacing.
            this._columnSpacing = value;
            // Schedule a fit of the parent.
            if (this.parent) {
                this.parent.fit();
            }
        }
        /**
         * Get the stretch factor for a specific row.
         *
         * @param index - The row index of interest.
         *
         * @returns The stretch factor for the row.
         *
         * #### Notes
         * This returns `-1` if the index is out of range.
         */
        rowStretch(index) {
            let sizer = this._rowSizers[index];
            return sizer ? sizer.stretch : -1;
        }
        /**
         * Set the stretch factor for a specific row.
         *
         * @param index - The row index of interest.
         *
         * @param value - The stretch factor for the row.
         *
         * #### Notes
         * This is a no-op if the index is out of range.
         */
        setRowStretch(index, value) {
            // Look up the row sizer.
            let sizer = this._rowSizers[index];
            // Bail if the index is out of range.
            if (!sizer) {
                return;
            }
            // Clamp the value to the allowed range.
            value = Private$4.clampValue(value);
            // Bail if the stretch does not change.
            if (sizer.stretch === value) {
                return;
            }
            // Update the sizer stretch.
            sizer.stretch = value;
            // Schedule an update of the parent.
            if (this.parent) {
                this.parent.update();
            }
        }
        /**
         * Get the stretch factor for a specific column.
         *
         * @param index - The column index of interest.
         *
         * @returns The stretch factor for the column.
         *
         * #### Notes
         * This returns `-1` if the index is out of range.
         */
        columnStretch(index) {
            let sizer = this._columnSizers[index];
            return sizer ? sizer.stretch : -1;
        }
        /**
         * Set the stretch factor for a specific column.
         *
         * @param index - The column index of interest.
         *
         * @param value - The stretch factor for the column.
         *
         * #### Notes
         * This is a no-op if the index is out of range.
         */
        setColumnStretch(index, value) {
            // Look up the column sizer.
            let sizer = this._columnSizers[index];
            // Bail if the index is out of range.
            if (!sizer) {
                return;
            }
            // Clamp the value to the allowed range.
            value = Private$4.clampValue(value);
            // Bail if the stretch does not change.
            if (sizer.stretch === value) {
                return;
            }
            // Update the sizer stretch.
            sizer.stretch = value;
            // Schedule an update of the parent.
            if (this.parent) {
                this.parent.update();
            }
        }
        /**
         * Create an iterator over the widgets in the layout.
         *
         * @returns A new iterator over the widgets in the layout.
         */
        *[Symbol.iterator]() {
            for (const item of this._items) {
                yield item.widget;
            }
        }
        /**
         * Add a widget to the grid layout.
         *
         * @param widget - The widget to add to the layout.
         *
         * #### Notes
         * If the widget is already contained in the layout, this is no-op.
         */
        addWidget(widget) {
            // Look up the index for the widget.
            let i = algorithm.ArrayExt.findFirstIndex(this._items, it => it.widget === widget);
            // Bail if the widget is already in the layout.
            if (i !== -1) {
                return;
            }
            // Add the widget to the layout.
            this._items.push(new LayoutItem(widget));
            // Attach the widget to the parent.
            if (this.parent) {
                this.attachWidget(widget);
            }
        }
        /**
         * Remove a widget from the grid layout.
         *
         * @param widget - The widget to remove from the layout.
         *
         * #### Notes
         * A widget is automatically removed from the layout when its `parent`
         * is set to `null`. This method should only be invoked directly when
         * removing a widget from a layout which has yet to be installed on a
         * parent widget.
         *
         * This method does *not* modify the widget's `parent`.
         */
        removeWidget(widget) {
            // Look up the index for the widget.
            let i = algorithm.ArrayExt.findFirstIndex(this._items, it => it.widget === widget);
            // Bail if the widget is not in the layout.
            if (i === -1) {
                return;
            }
            // Remove the widget from the layout.
            let item = algorithm.ArrayExt.removeAt(this._items, i);
            // Detach the widget from the parent.
            if (this.parent) {
                this.detachWidget(widget);
            }
            // Dispose the layout item.
            item.dispose();
        }
        /**
         * Perform layout initialization which requires the parent widget.
         */
        init() {
            super.init();
            for (const widget of this) {
                this.attachWidget(widget);
            }
        }
        /**
         * Attach a widget to the parent's DOM node.
         *
         * @param widget - The widget to attach to the parent.
         */
        attachWidget(widget) {
            // Send a `'before-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            }
            // Add the widget's node to the parent.
            this.parent.node.appendChild(widget.node);
            // Send an `'after-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
            }
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * Detach a widget from the parent's DOM node.
         *
         * @param widget - The widget to detach from the parent.
         */
        detachWidget(widget) {
            // Send a `'before-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            }
            // Remove the widget's node from the parent.
            this.parent.node.removeChild(widget.node);
            // Send an `'after-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
            }
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'before-show'` message.
         */
        onBeforeShow(msg) {
            super.onBeforeShow(msg);
            this.parent.update();
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            super.onBeforeAttach(msg);
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-shown'` message.
         */
        onChildShown(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-hidden'` message.
         */
        onChildHidden(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'resize'` message.
         */
        onResize(msg) {
            if (this.parent.isVisible) {
                this._update(msg.width, msg.height);
            }
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            if (this.parent.isVisible) {
                this._update(-1, -1);
            }
        }
        /**
         * A message handler invoked on a `'fit-request'` message.
         */
        onFitRequest(msg) {
            if (this.parent.isAttached) {
                this._fit();
            }
        }
        /**
         * Fit the layout to the total size required by the widgets.
         */
        _fit() {
            // Reset the min sizes of the sizers.
            for (let i = 0, n = this.rowCount; i < n; ++i) {
                this._rowSizers[i].minSize = 0;
            }
            for (let i = 0, n = this.columnCount; i < n; ++i) {
                this._columnSizers[i].minSize = 0;
            }
            // Filter for the visible layout items.
            let items = this._items.filter(it => !it.isHidden);
            // Fit the layout items.
            for (let i = 0, n = items.length; i < n; ++i) {
                items[i].fit();
            }
            // Get the max row and column index.
            let maxRow = this.rowCount - 1;
            let maxCol = this.columnCount - 1;
            // Sort the items by row span.
            items.sort(Private$4.rowSpanCmp);
            // Update the min sizes of the row sizers.
            for (let i = 0, n = items.length; i < n; ++i) {
                // Fetch the item.
                let item = items[i];
                // Get the row bounds for the item.
                let config = GridLayout.getCellConfig(item.widget);
                let r1 = Math.min(config.row, maxRow);
                let r2 = Math.min(config.row + config.rowSpan - 1, maxRow);
                // Distribute the minimum height to the sizers as needed.
                Private$4.distributeMin(this._rowSizers, r1, r2, item.minHeight);
            }
            // Sort the items by column span.
            items.sort(Private$4.columnSpanCmp);
            // Update the min sizes of the column sizers.
            for (let i = 0, n = items.length; i < n; ++i) {
                // Fetch the item.
                let item = items[i];
                // Get the column bounds for the item.
                let config = GridLayout.getCellConfig(item.widget);
                let c1 = Math.min(config.column, maxCol);
                let c2 = Math.min(config.column + config.columnSpan - 1, maxCol);
                // Distribute the minimum width to the sizers as needed.
                Private$4.distributeMin(this._columnSizers, c1, c2, item.minWidth);
            }
            // If no size constraint is needed, just update the parent.
            if (this.fitPolicy === 'set-no-constraint') {
                messaging.MessageLoop.sendMessage(this.parent, Widget.Msg.UpdateRequest);
                return;
            }
            // Set up the computed min size.
            let minH = maxRow * this._rowSpacing;
            let minW = maxCol * this._columnSpacing;
            // Add the sizer minimums to the computed min size.
            for (let i = 0, n = this.rowCount; i < n; ++i) {
                minH += this._rowSizers[i].minSize;
            }
            for (let i = 0, n = this.columnCount; i < n; ++i) {
                minW += this._columnSizers[i].minSize;
            }
            // Update the box sizing and add it to the computed min size.
            let box = (this._box = domutils.ElementExt.boxSizing(this.parent.node));
            minW += box.horizontalSum;
            minH += box.verticalSum;
            // Update the parent's min size constraints.
            let style = this.parent.node.style;
            style.minWidth = `${minW}px`;
            style.minHeight = `${minH}px`;
            // Set the dirty flag to ensure only a single update occurs.
            this._dirty = true;
            // Notify the ancestor that it should fit immediately. This may
            // cause a resize of the parent, fulfilling the required update.
            if (this.parent.parent) {
                messaging.MessageLoop.sendMessage(this.parent.parent, Widget.Msg.FitRequest);
            }
            // If the dirty flag is still set, the parent was not resized.
            // Trigger the required update on the parent widget immediately.
            if (this._dirty) {
                messaging.MessageLoop.sendMessage(this.parent, Widget.Msg.UpdateRequest);
            }
        }
        /**
         * Update the layout position and size of the widgets.
         *
         * The parent offset dimensions should be `-1` if unknown.
         */
        _update(offsetWidth, offsetHeight) {
            // Clear the dirty flag to indicate the update occurred.
            this._dirty = false;
            // Measure the parent if the offset dimensions are unknown.
            if (offsetWidth < 0) {
                offsetWidth = this.parent.node.offsetWidth;
            }
            if (offsetHeight < 0) {
                offsetHeight = this.parent.node.offsetHeight;
            }
            // Ensure the parent box sizing data is computed.
            if (!this._box) {
                this._box = domutils.ElementExt.boxSizing(this.parent.node);
            }
            // Compute the layout area adjusted for border and padding.
            let top = this._box.paddingTop;
            let left = this._box.paddingLeft;
            let width = offsetWidth - this._box.horizontalSum;
            let height = offsetHeight - this._box.verticalSum;
            // Get the max row and column index.
            let maxRow = this.rowCount - 1;
            let maxCol = this.columnCount - 1;
            // Compute the total fixed row and column space.
            let fixedRowSpace = maxRow * this._rowSpacing;
            let fixedColSpace = maxCol * this._columnSpacing;
            // Distribute the available space to the box sizers.
            exports.BoxEngine.calc(this._rowSizers, Math.max(0, height - fixedRowSpace));
            exports.BoxEngine.calc(this._columnSizers, Math.max(0, width - fixedColSpace));
            // Update the row start positions.
            for (let i = 0, pos = top, n = this.rowCount; i < n; ++i) {
                this._rowStarts[i] = pos;
                pos += this._rowSizers[i].size + this._rowSpacing;
            }
            // Update the column start positions.
            for (let i = 0, pos = left, n = this.columnCount; i < n; ++i) {
                this._columnStarts[i] = pos;
                pos += this._columnSizers[i].size + this._columnSpacing;
            }
            // Update the geometry of the layout items.
            for (let i = 0, n = this._items.length; i < n; ++i) {
                // Fetch the item.
                let item = this._items[i];
                // Ignore hidden items.
                if (item.isHidden) {
                    continue;
                }
                // Fetch the cell bounds for the widget.
                let config = GridLayout.getCellConfig(item.widget);
                let r1 = Math.min(config.row, maxRow);
                let c1 = Math.min(config.column, maxCol);
                let r2 = Math.min(config.row + config.rowSpan - 1, maxRow);
                let c2 = Math.min(config.column + config.columnSpan - 1, maxCol);
                // Compute the cell geometry.
                let x = this._columnStarts[c1];
                let y = this._rowStarts[r1];
                let w = this._columnStarts[c2] + this._columnSizers[c2].size - x;
                let h = this._rowStarts[r2] + this._rowSizers[r2].size - y;
                // Update the geometry of the layout item.
                item.update(x, y, w, h);
            }
        }
    }
    /**
     * The namespace for the `GridLayout` class statics.
     */
    (function (GridLayout) {
        /**
         * Get the cell config for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @returns The cell config for the widget.
         */
        function getCellConfig(widget) {
            return Private$4.cellConfigProperty.get(widget);
        }
        GridLayout.getCellConfig = getCellConfig;
        /**
         * Set the cell config for the given widget.
         *
         * @param widget - The widget of interest.
         *
         * @param value - The value for the cell config.
         */
        function setCellConfig(widget, value) {
            Private$4.cellConfigProperty.set(widget, Private$4.normalizeConfig(value));
        }
        GridLayout.setCellConfig = setCellConfig;
    })(GridLayout || (GridLayout = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$4;
    (function (Private) {
        /**
         * The property descriptor for the widget cell config.
         */
        Private.cellConfigProperty = new properties.AttachedProperty({
            name: 'cellConfig',
            create: () => ({ row: 0, column: 0, rowSpan: 1, columnSpan: 1 }),
            changed: onChildCellConfigChanged
        });
        /**
         * Normalize a partial cell config object.
         */
        function normalizeConfig(config) {
            let row = Math.max(0, Math.floor(config.row || 0));
            let column = Math.max(0, Math.floor(config.column || 0));
            let rowSpan = Math.max(1, Math.floor(config.rowSpan || 0));
            let columnSpan = Math.max(1, Math.floor(config.columnSpan || 0));
            return { row, column, rowSpan, columnSpan };
        }
        Private.normalizeConfig = normalizeConfig;
        /**
         * Clamp a value to an integer >= 0.
         */
        function clampValue(value) {
            return Math.max(0, Math.floor(value));
        }
        Private.clampValue = clampValue;
        /**
         * A sort comparison function for row spans.
         */
        function rowSpanCmp(a, b) {
            let c1 = Private.cellConfigProperty.get(a.widget);
            let c2 = Private.cellConfigProperty.get(b.widget);
            return c1.rowSpan - c2.rowSpan;
        }
        Private.rowSpanCmp = rowSpanCmp;
        /**
         * A sort comparison function for column spans.
         */
        function columnSpanCmp(a, b) {
            let c1 = Private.cellConfigProperty.get(a.widget);
            let c2 = Private.cellConfigProperty.get(b.widget);
            return c1.columnSpan - c2.columnSpan;
        }
        Private.columnSpanCmp = columnSpanCmp;
        /**
         * Reallocate the box sizers for the given grid dimensions.
         */
        function reallocSizers(sizers, count) {
            // Coerce the count to the valid range.
            count = Math.max(1, Math.floor(count));
            // Add the missing sizers.
            while (sizers.length < count) {
                sizers.push(new BoxSizer());
            }
            // Remove the extra sizers.
            if (sizers.length > count) {
                sizers.length = count;
            }
        }
        Private.reallocSizers = reallocSizers;
        /**
         * Distribute a min size constraint across a range of sizers.
         */
        function distributeMin(sizers, i1, i2, minSize) {
            // Sanity check the indices.
            if (i2 < i1) {
                return;
            }
            // Handle the simple case of no cell span.
            if (i1 === i2) {
                let sizer = sizers[i1];
                sizer.minSize = Math.max(sizer.minSize, minSize);
                return;
            }
            // Compute the total current min size of the span.
            let totalMin = 0;
            for (let i = i1; i <= i2; ++i) {
                totalMin += sizers[i].minSize;
            }
            // Do nothing if the total is greater than the required.
            if (totalMin >= minSize) {
                return;
            }
            // Compute the portion of the space to allocate to each sizer.
            let portion = (minSize - totalMin) / (i2 - i1 + 1);
            // Add the portion to each sizer.
            for (let i = i1; i <= i2; ++i) {
                sizers[i].minSize += portion;
            }
        }
        Private.distributeMin = distributeMin;
        /**
         * The change handler for the child cell config property.
         */
        function onChildCellConfigChanged(child) {
            if (child.parent && child.parent.layout instanceof GridLayout) {
                child.parent.fit();
            }
        }
    })(Private$4 || (Private$4 = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A widget which displays menus as a canonical menu bar.
     *
     * #### Notes
     * See also the related [example](../../examples/menubar/index.html) and
     * its [source](https://github.com/jupyterlab/lumino/tree/main/examples/example-menubar).
     */
    class MenuBar extends Widget {
        /**
         * Construct a new menu bar.
         *
         * @param options - The options for initializing the menu bar.
         */
        constructor(options = {}) {
            super({ node: Private$3.createNode() });
            // Track the index of the item that is currently focused or hovered. -1 means nothing focused or hovered.
            this._activeIndex = -1;
            // Track which item can be focused using the TAB key. Unlike _activeIndex will
            // always point to a menuitem. Whenever you update this value, it's important
            // to follow it with an "update-request" message so that the `tabindex`
            // attribute on each menubar item gets properly updated.
            this._tabFocusIndex = 0;
            this._menus = [];
            this._childMenu = null;
            this._overflowMenu = null;
            this._menuItemSizes = [];
            this._overflowIndex = -1;
            this.addClass('lm-MenuBar');
            this.setFlag(Widget.Flag.DisallowLayout);
            this.renderer = options.renderer || MenuBar.defaultRenderer;
            this._forceItemsPosition = options.forceItemsPosition || {
                forceX: true,
                forceY: true
            };
            this._overflowMenuOptions = options.overflowMenuOptions || {
                isVisible: true
            };
        }
        /**
         * Dispose of the resources held by the widget.
         */
        dispose() {
            this._closeChildMenu();
            this._menus.length = 0;
            super.dispose();
        }
        /**
         * The child menu of the menu bar.
         *
         * #### Notes
         * This will be `null` if the menu bar does not have an open menu.
         */
        get childMenu() {
            return this._childMenu;
        }
        /**
         * The overflow index of the menu bar.
         */
        get overflowIndex() {
            return this._overflowIndex;
        }
        /**
         * The overflow menu of the menu bar.
         */
        get overflowMenu() {
            return this._overflowMenu;
        }
        /**
         * Get the menu bar content node.
         *
         * #### Notes
         * This is the node which holds the menu title nodes.
         *
         * Modifying this node directly can lead to undefined behavior.
         */
        get contentNode() {
            return this.node.getElementsByClassName('lm-MenuBar-content')[0];
        }
        /**
         * Get the currently active menu.
         */
        get activeMenu() {
            return this._menus[this._activeIndex] || null;
        }
        /**
         * Set the currently active menu.
         *
         * #### Notes
         * If the menu does not exist, the menu will be set to `null`.
         */
        set activeMenu(value) {
            this.activeIndex = value ? this._menus.indexOf(value) : -1;
        }
        /**
         * Get the index of the currently active menu.
         *
         * #### Notes
         * This will be `-1` if no menu is active.
         */
        get activeIndex() {
            return this._activeIndex;
        }
        /**
         * Set the index of the currently active menu.
         *
         * #### Notes
         * If the menu cannot be activated, the index will be set to `-1`.
         */
        set activeIndex(value) {
            // Adjust the value for an out of range index.
            if (value < 0 || value >= this._menus.length) {
                value = -1;
            }
            // An empty menu cannot be active
            if (value > -1 && this._menus[value].items.length === 0) {
                value = -1;
            }
            // Bail early if the index will not change.
            if (this._activeIndex === value) {
                return;
            }
            // Update the active index.
            this._activeIndex = value;
            // Schedule an update of the items.
            this.update();
        }
        /**
         * A read-only array of the menus in the menu bar.
         */
        get menus() {
            return this._menus;
        }
        /**
         * Open the active menu and activate its first menu item.
         *
         * #### Notes
         * If there is no active menu, this is a no-op.
         */
        openActiveMenu() {
            // Bail early if there is no active item.
            if (this._activeIndex === -1) {
                return;
            }
            // Open the child menu.
            this._openChildMenu();
            // Activate the first item in the child menu.
            if (this._childMenu) {
                this._childMenu.activeIndex = -1;
                this._childMenu.activateNextItem();
            }
        }
        /**
         * Add a menu to the end of the menu bar.
         *
         * @param menu - The menu to add to the menu bar.
         *
         * #### Notes
         * If the menu is already added to the menu bar, it will be moved.
         */
        addMenu(menu, update = true) {
            this.insertMenu(this._menus.length, menu, update);
        }
        /**
         * Insert a menu into the menu bar at the specified index.
         *
         * @param index - The index at which to insert the menu.
         *
         * @param menu - The menu to insert into the menu bar.
         *
         * #### Notes
         * The index will be clamped to the bounds of the menus.
         *
         * If the menu is already added to the menu bar, it will be moved.
         */
        insertMenu(index, menu, update = true) {
            // Close the child menu before making changes.
            this._closeChildMenu();
            // Look up the index of the menu.
            let i = this._menus.indexOf(menu);
            // Clamp the insert index to the array bounds.
            let j = Math.max(0, Math.min(index, this._menus.length));
            // If the menu is not in the array, insert it.
            if (i === -1) {
                // Insert the menu into the array.
                algorithm.ArrayExt.insert(this._menus, j, menu);
                // Add the styling class to the menu.
                menu.addClass('lm-MenuBar-menu');
                // Connect to the menu signals.
                menu.aboutToClose.connect(this._onMenuAboutToClose, this);
                menu.menuRequested.connect(this._onMenuMenuRequested, this);
                menu.title.changed.connect(this._onTitleChanged, this);
                // Schedule an update of the items.
                if (update) {
                    this.update();
                }
                // There is nothing more to do.
                return;
            }
            // Otherwise, the menu exists in the array and should be moved.
            // Adjust the index if the location is at the end of the array.
            if (j === this._menus.length) {
                j--;
            }
            // Bail if there is no effective move.
            if (i === j) {
                return;
            }
            // Move the menu to the new locations.
            algorithm.ArrayExt.move(this._menus, i, j);
            // Schedule an update of the items.
            if (update) {
                this.update();
            }
        }
        /**
         * Remove a menu from the menu bar.
         *
         * @param menu - The menu to remove from the menu bar.
         *
         * #### Notes
         * This is a no-op if the menu is not in the menu bar.
         */
        removeMenu(menu, update = true) {
            this.removeMenuAt(this._menus.indexOf(menu), update);
        }
        /**
         * Remove the menu at a given index from the menu bar.
         *
         * @param index - The index of the menu to remove.
         *
         * #### Notes
         * This is a no-op if the index is out of range.
         */
        removeMenuAt(index, update = true) {
            // Close the child menu before making changes.
            this._closeChildMenu();
            // Remove the menu from the array.
            let menu = algorithm.ArrayExt.removeAt(this._menus, index);
            // Bail if the index is out of range.
            if (!menu) {
                return;
            }
            // Disconnect from the menu signals.
            menu.aboutToClose.disconnect(this._onMenuAboutToClose, this);
            menu.menuRequested.disconnect(this._onMenuMenuRequested, this);
            menu.title.changed.disconnect(this._onTitleChanged, this);
            // Remove the styling class from the menu.
            menu.removeClass('lm-MenuBar-menu');
            // Schedule an update of the items.
            if (update) {
                this.update();
            }
        }
        /**
         * Remove all menus from the menu bar.
         */
        clearMenus() {
            // Bail if there is nothing to remove.
            if (this._menus.length === 0) {
                return;
            }
            // Close the child menu before making changes.
            this._closeChildMenu();
            // Disconnect from the menu signals and remove the styling class.
            for (let menu of this._menus) {
                menu.aboutToClose.disconnect(this._onMenuAboutToClose, this);
                menu.menuRequested.disconnect(this._onMenuMenuRequested, this);
                menu.title.changed.disconnect(this._onTitleChanged, this);
                menu.removeClass('lm-MenuBar-menu');
            }
            // Clear the menus array.
            this._menus.length = 0;
            // Schedule an update of the items.
            this.update();
        }
        /**
         * Handle the DOM events for the menu bar.
         *
         * @param event - The DOM event sent to the menu bar.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the menu bar's DOM nodes. It
         * should not be called directly by user code.
         */
        handleEvent(event) {
            switch (event.type) {
                case 'keydown':
                    this._evtKeyDown(event);
                    break;
                case 'pointerdown':
                    this._evtPointerDown(event);
                    break;
                case 'pointermove':
                case 'pointerleave':
                    this._evtPointerMove(event);
                    break;
                case 'focusout':
                    this._evtFocusOut(event);
                    break;
                case 'contextmenu':
                    event.preventDefault();
                    event.stopPropagation();
                    break;
            }
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            this.node.addEventListener('keydown', this);
            this.node.addEventListener('pointerdown', this);
            this.node.addEventListener('pointermove', this);
            this.node.addEventListener('pointerleave', this);
            this.node.addEventListener('focusout', this);
            this.node.addEventListener('contextmenu', this);
        }
        /**
         * A message handler invoked on an `'after-detach'` message.
         */
        onAfterDetach(msg) {
            this.node.removeEventListener('keydown', this);
            this.node.removeEventListener('pointerdown', this);
            this.node.removeEventListener('pointermove', this);
            this.node.removeEventListener('pointerleave', this);
            this.node.removeEventListener('focusout', this);
            this.node.removeEventListener('contextmenu', this);
            this._closeChildMenu();
        }
        /**
         * A message handler invoked on an `'activate-request'` message.
         */
        onActivateRequest(msg) {
            if (this.isAttached) {
                this._focusItemAt(0);
            }
        }
        /**
         * A message handler invoked on a `'resize'` message.
         */
        onResize(msg) {
            this.update();
            super.onResize(msg);
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            var _a;
            let menus = this._menus;
            let renderer = this.renderer;
            let activeIndex = this._activeIndex;
            let tabFocusIndex = this._tabFocusIndex >= 0 && this._tabFocusIndex < menus.length
                ? this._tabFocusIndex
                : 0;
            let length = this._overflowIndex > -1 ? this._overflowIndex : menus.length;
            let totalMenuSize = 0;
            let isVisible = false;
            // Check that the overflow menu doesn't count
            length = this._overflowMenu !== null ? length - 1 : length;
            let content = new Array(length);
            // Render visible menus
            for (let i = 0; i < length; ++i) {
                content[i] = renderer.renderItem({
                    title: menus[i].title,
                    active: i === activeIndex,
                    tabbable: i === tabFocusIndex,
                    disabled: menus[i].items.length === 0,
                    onfocus: () => {
                        this._tabFocusIndex = i;
                        this.activeIndex = i;
                    }
                });
                // Calculate size of current menu
                totalMenuSize += this._menuItemSizes[i];
                // Check if overflow menu is already rendered
                if (menus[i].title.label === this._overflowMenuOptions.title) {
                    isVisible = true;
                    length--;
                }
            }
            // Render overflow menu if needed and active
            if (this._overflowMenuOptions.isVisible) {
                if (this._overflowIndex > -1 && !isVisible) {
                    // Create overflow menu
                    if (this._overflowMenu === null) {
                        const overflowMenuTitle = (_a = this._overflowMenuOptions.title) !== null && _a !== void 0 ? _a : '...';
                        this._overflowMenu = new Menu({ commands: new commands.CommandRegistry() });
                        this._overflowMenu.title.label = overflowMenuTitle;
                        this._overflowMenu.title.mnemonic = 0;
                        this.addMenu(this._overflowMenu, false);
                    }
                    // Move menus to overflow menu
                    for (let i = menus.length - 2; i >= length; i--) {
                        const submenu = this.menus[i];
                        submenu.title.mnemonic = 0;
                        this._overflowMenu.insertItem(0, {
                            type: 'submenu',
                            submenu: submenu
                        });
                        this.removeMenu(submenu, false);
                    }
                    content[length] = renderer.renderItem({
                        title: this._overflowMenu.title,
                        active: length === activeIndex && menus[length].items.length !== 0,
                        tabbable: length === tabFocusIndex,
                        disabled: menus[length].items.length === 0,
                        onfocus: () => {
                            this._tabFocusIndex = length;
                            this.activeIndex = length;
                        }
                    });
                    length++;
                }
                else if (this._overflowMenu !== null) {
                    // Remove submenus from overflow menu
                    let overflowMenuItems = this._overflowMenu.items;
                    let screenSize = this.node.offsetWidth;
                    let n = this._overflowMenu.items.length;
                    for (let i = 0; i < n; ++i) {
                        let index = menus.length - 1 - i;
                        if (screenSize - totalMenuSize > this._menuItemSizes[index]) {
                            let menu = overflowMenuItems[0].submenu;
                            this._overflowMenu.removeItemAt(0);
                            this.insertMenu(length, menu, false);
                            content[length] = renderer.renderItem({
                                title: menu.title,
                                active: false,
                                tabbable: length === tabFocusIndex,
                                disabled: menus[length].items.length === 0,
                                onfocus: () => {
                                    this._tabFocusIndex = length;
                                    this.activeIndex = length;
                                }
                            });
                            length++;
                        }
                    }
                    if (this._overflowMenu.items.length === 0) {
                        this.removeMenu(this._overflowMenu, false);
                        content.pop();
                        this._overflowMenu = null;
                        this._overflowIndex = -1;
                    }
                }
            }
            virtualdom.VirtualDOM.render(content, this.contentNode);
            this._updateOverflowIndex();
        }
        /**
         * Calculate and update the current overflow index.
         */
        _updateOverflowIndex() {
            if (!this._overflowMenuOptions.isVisible) {
                return;
            }
            // Get elements visible in the main menu bar
            const itemMenus = this.contentNode.childNodes;
            let screenSize = this.node.offsetWidth;
            let totalMenuSize = 0;
            let index = -1;
            let n = itemMenus.length;
            if (this._menuItemSizes.length == 0) {
                // Check if it is the first resize and get info about menu items sizes
                for (let i = 0; i < n; i++) {
                    let item = itemMenus[i];
                    // Add sizes to array
                    totalMenuSize += item.offsetWidth;
                    this._menuItemSizes.push(item.offsetWidth);
                    if (totalMenuSize > screenSize && index === -1) {
                        index = i;
                    }
                }
            }
            else {
                // Calculate current menu size
                for (let i = 0; i < this._menuItemSizes.length; i++) {
                    totalMenuSize += this._menuItemSizes[i];
                    if (totalMenuSize > screenSize) {
                        index = i;
                        break;
                    }
                }
            }
            this._overflowIndex = index;
        }
        /**
         * Handle the `'keydown'` event for the menu bar.
         *
         * #### Notes
         * All keys are trapped except the tab key that is ignored.
         */
        _evtKeyDown(event) {
            // Fetch the key code for the event.
            let kc = event.keyCode;
            // Reset the active index on tab, but do not trap the tab key.
            if (kc === 9) {
                this.activeIndex = -1;
                return;
            }
            // A menu bar handles all other keydown events.
            event.preventDefault();
            event.stopPropagation();
            // Enter, Space, Up Arrow, Down Arrow
            if (kc === 13 || kc === 32 || kc === 38 || kc === 40) {
                // The active index may have changed (for example, user hovers over an
                // item with the mouse), so be sure to use the focus index.
                this.activeIndex = this._tabFocusIndex;
                if (this.activeIndex !== this._tabFocusIndex) {
                    // Bail if the setter refused to set activeIndex to tabFocusIndex
                    // because it means that the item at tabFocusIndex cannot be opened (for
                    // example, it has an empty menu)
                    return;
                }
                this.openActiveMenu();
                return;
            }
            // Escape
            if (kc === 27) {
                this._closeChildMenu();
                this._focusItemAt(this.activeIndex);
                return;
            }
            // Left or Right Arrow
            if (kc === 37 || kc === 39) {
                let direction = kc === 37 ? -1 : 1;
                let start = this._tabFocusIndex + direction;
                let n = this._menus.length;
                for (let i = 0; i < n; i++) {
                    let index = (n + start + direction * i) % n;
                    if (this._menus[index].items.length) {
                        this._focusItemAt(index);
                        return;
                    }
                }
                return;
            }
            // Get the pressed key character.
            let key = keyboard.getKeyboardLayout().keyForKeydownEvent(event);
            // Bail if the key is not valid.
            if (!key) {
                return;
            }
            // Search for the next best matching mnemonic item.
            let start = this._activeIndex + 1;
            let result = Private$3.findMnemonic(this._menus, key, start);
            // Handle the requested mnemonic based on the search results.
            // If exactly one mnemonic is matched, that menu is opened.
            // Otherwise, the next mnemonic is activated if available,
            // followed by the auto mnemonic if available.
            if (result.index !== -1 && !result.multiple) {
                this.activeIndex = result.index;
                this.openActiveMenu();
            }
            else if (result.index !== -1) {
                this.activeIndex = result.index;
                this._focusItemAt(this.activeIndex);
            }
            else if (result.auto !== -1) {
                this.activeIndex = result.auto;
                this._focusItemAt(this.activeIndex);
            }
        }
        /**
         * Handle the `'pointerdown'` event for the menu bar.
         */
        _evtPointerDown(event) {
            // Bail if the mouse press was not on the menu bar. This can occur
            // when the document listener is installed for an active menu bar.
            if (!domutils.ElementExt.hitTest(this.node, event.clientX, event.clientY)) {
                return;
            }
            // Stop the propagation of the event. Immediate propagation is
            // also stopped so that an open menu does not handle the event.
            event.stopPropagation();
            event.stopImmediatePropagation();
            // Check if the mouse is over one of the menu items.
            let index = algorithm.ArrayExt.findFirstIndex(this.contentNode.children, node => {
                return domutils.ElementExt.hitTest(node, event.clientX, event.clientY);
            });
            // If the press was not on an item, close the child menu.
            if (index === -1) {
                this._closeChildMenu();
                return;
            }
            // If the press was not the left mouse button, do nothing further.
            if (event.button !== 0) {
                return;
            }
            // Otherwise, toggle the open state of the child menu.
            if (this._childMenu) {
                this._closeChildMenu();
                this.activeIndex = index;
            }
            else {
                // If we don't call preventDefault() here, then the item in the menu
                // bar will take focus over the menu that is being opened.
                event.preventDefault();
                const position = this._positionForMenu(index);
                Menu.saveWindowData();
                // Begin DOM modifications.
                this.activeIndex = index;
                this._openChildMenu(position);
            }
        }
        /**
         * Handle the `'pointermove'` event for the menu bar.
         */
        _evtPointerMove(event) {
            if (Utils$1.isTouchEvent(event)) {
                return;
            }
            // Check if the mouse is over one of the menu items.
            let index = algorithm.ArrayExt.findFirstIndex(this.contentNode.children, node => {
                return domutils.ElementExt.hitTest(node, event.clientX, event.clientY);
            });
            // Bail early if the active index will not change.
            if (index === this._activeIndex) {
                return;
            }
            // Bail early if a child menu is open and the mouse is not over
            // an item. This allows the child menu to be kept open when the
            // mouse is over the empty part of the menu bar.
            if (index === -1 && this._childMenu) {
                return;
            }
            // Get position for the new menu >before< updating active index.
            const position = index >= 0 && this._childMenu ? this._positionForMenu(index) : null;
            // Before any modification, update window data.
            Menu.saveWindowData();
            // Begin DOM modifications.
            // Update the active index to the hovered item.
            this.activeIndex = index;
            // Open the new menu if a menu is already open.
            if (position) {
                this._openChildMenu(position);
            }
        }
        /**
         * Find initial position for the menu based on menubar item position.
         *
         * NOTE: this should be called before updating active index to avoid
         * an additional layout and style invalidation as changing active
         * index modifies DOM.
         */
        _positionForMenu(index) {
            let itemNode = this.contentNode.children[index];
            let { left, bottom } = itemNode.getBoundingClientRect();
            return {
                top: bottom,
                left
            };
        }
        /**
         * Handle the `'focusout'` event for the menu bar.
         */
        _evtFocusOut(event) {
            // Reset the active index if there is no open menu and the menubar is losing focus.
            if (!this._childMenu && !this.node.contains(event.relatedTarget)) {
                this.activeIndex = -1;
            }
        }
        /**
         * Focus an item in the menu bar.
         *
         * #### Notes
         * Does not open the associated menu.
         */
        _focusItemAt(index) {
            const itemNode = this.contentNode.childNodes[index];
            if (itemNode) {
                itemNode.focus();
            }
        }
        /**
         * Open the child menu at the active index immediately.
         *
         * If a different child menu is already open, it will be closed,
         * even if there is no active menu.
         */
        _openChildMenu(options = {}) {
            // If there is no active menu, close the current menu.
            let newMenu = this.activeMenu;
            if (!newMenu) {
                this._closeChildMenu();
                return;
            }
            // Bail if there is no effective menu change.
            let oldMenu = this._childMenu;
            if (oldMenu === newMenu) {
                return;
            }
            // Swap the internal menu reference.
            this._childMenu = newMenu;
            // Close the current menu, or setup for the new menu.
            if (oldMenu) {
                oldMenu.close();
            }
            else {
                document.addEventListener('pointerdown', this, true);
            }
            // Update the tab focus index and ensure the menu bar is updated.
            this._tabFocusIndex = this.activeIndex;
            messaging.MessageLoop.sendMessage(this, Widget.Msg.UpdateRequest);
            // Get the positioning data for the new menu.
            let { left, top } = options;
            if (typeof left === 'undefined' || typeof top === 'undefined') {
                ({ left, top } = this._positionForMenu(this._activeIndex));
            }
            // Begin DOM modifications
            if (!oldMenu) {
                // Continue setup for new menu
                this.addClass('lm-mod-active');
            }
            // Open the new menu at the computed location.
            if (newMenu.items.length > 0) {
                newMenu.open(left, top, this._forceItemsPosition);
            }
        }
        /**
         * Close the child menu immediately.
         *
         * This is a no-op if a child menu is not open.
         */
        _closeChildMenu() {
            // Bail if no child menu is open.
            if (!this._childMenu) {
                return;
            }
            // Remove the active class from the menu bar.
            this.removeClass('lm-mod-active');
            // Remove the document listeners.
            document.removeEventListener('pointerdown', this, true);
            // Clear the internal menu reference.
            let menu = this._childMenu;
            this._childMenu = null;
            // Close the menu.
            menu.close();
            // Reset the active index.
            this.activeIndex = -1;
        }
        /**
         * Handle the `aboutToClose` signal of a menu.
         */
        _onMenuAboutToClose(sender) {
            // Bail if the sender is not the child menu.
            if (sender !== this._childMenu) {
                return;
            }
            // Remove the active class from the menu bar.
            this.removeClass('lm-mod-active');
            // Remove the document listeners.
            document.removeEventListener('pointerdown', this, true);
            // Clear the internal menu reference.
            this._childMenu = null;
            // Reset the active index.
            this.activeIndex = -1;
        }
        /**
         * Handle the `menuRequested` signal of a child menu.
         */
        _onMenuMenuRequested(sender, args) {
            // Bail if the sender is not the child menu.
            if (sender !== this._childMenu) {
                return;
            }
            // Look up the active index and menu count.
            let i = this._activeIndex;
            let n = this._menus.length;
            // Active the next requested index.
            switch (args) {
                case 'next':
                    this.activeIndex = i === n - 1 ? 0 : i + 1;
                    break;
                case 'previous':
                    this.activeIndex = i === 0 ? n - 1 : i - 1;
                    break;
            }
            // Open the active menu.
            this.openActiveMenu();
        }
        /**
         * Handle the `changed` signal of a title object.
         */
        _onTitleChanged() {
            this.update();
        }
    }
    /**
     * The namespace for the `MenuBar` class statics.
     */
    (function (MenuBar) {
        /**
         * The default implementation of `IRenderer`.
         *
         * #### Notes
         * Subclasses are free to reimplement rendering methods as needed.
         */
        class Renderer {
            /**
             * Render the virtual element for a menu bar item.
             *
             * @param data - The data to use for rendering the item.
             *
             * @returns A virtual element representing the item.
             */
            renderItem(data) {
                let className = this.createItemClass(data);
                let dataset = this.createItemDataset(data);
                let aria = this.createItemARIA(data);
                return virtualdom.h.li({
                    className,
                    dataset,
                    ...(data.disabled ? {} : { tabindex: data.tabbable ? '0' : '-1' }),
                    onfocus: data.onfocus,
                    ...aria
                }, this.renderIcon(data), this.renderLabel(data));
            }
            /**
             * Render the icon element for a menu bar item.
             *
             * @param data - The data to use for rendering the icon.
             *
             * @returns A virtual element representing the item icon.
             */
            renderIcon(data) {
                let className = this.createIconClass(data);
                // If data.title.icon is undefined, it will be ignored.
                return virtualdom.h.div({ className }, data.title.icon, data.title.iconLabel);
            }
            /**
             * Render the label element for a menu item.
             *
             * @param data - The data to use for rendering the label.
             *
             * @returns A virtual element representing the item label.
             */
            renderLabel(data) {
                let content = this.formatLabel(data);
                return virtualdom.h.div({ className: 'lm-MenuBar-itemLabel' }, content);
            }
            /**
             * Create the class name for the menu bar item.
             *
             * @param data - The data to use for the class name.
             *
             * @returns The full class name for the menu item.
             */
            createItemClass(data) {
                let name = 'lm-MenuBar-item';
                if (data.title.className) {
                    name += ` ${data.title.className}`;
                }
                if (data.active && !data.disabled) {
                    name += ' lm-mod-active';
                }
                return name;
            }
            /**
             * Create the dataset for a menu bar item.
             *
             * @param data - The data to use for the item.
             *
             * @returns The dataset for the menu bar item.
             */
            createItemDataset(data) {
                return data.title.dataset;
            }
            /**
             * Create the aria attributes for menu bar item.
             *
             * @param data - The data to use for the aria attributes.
             *
             * @returns The aria attributes object for the item.
             */
            createItemARIA(data) {
                return {
                    role: 'menuitem',
                    'aria-haspopup': 'true',
                    'aria-disabled': data.disabled ? 'true' : 'false'
                };
            }
            /**
             * Create the class name for the menu bar item icon.
             *
             * @param data - The data to use for the class name.
             *
             * @returns The full class name for the item icon.
             */
            createIconClass(data) {
                let name = 'lm-MenuBar-itemIcon';
                let extra = data.title.iconClass;
                return extra ? `${name} ${extra}` : name;
            }
            /**
             * Create the render content for the label node.
             *
             * @param data - The data to use for the label content.
             *
             * @returns The content to add to the label node.
             */
            formatLabel(data) {
                // Fetch the label text and mnemonic index.
                let { label, mnemonic } = data.title;
                // If the index is out of range, do not modify the label.
                if (mnemonic < 0 || mnemonic >= label.length) {
                    return label;
                }
                // Split the label into parts.
                let prefix = label.slice(0, mnemonic);
                let suffix = label.slice(mnemonic + 1);
                let char = label[mnemonic];
                // Wrap the mnemonic character in a span.
                let span = virtualdom.h.span({ className: 'lm-MenuBar-itemMnemonic' }, char);
                // Return the content parts.
                return [prefix, span, suffix];
            }
        }
        MenuBar.Renderer = Renderer;
        /**
         * The default `Renderer` instance.
         */
        MenuBar.defaultRenderer = new Renderer();
    })(MenuBar || (MenuBar = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private$3;
    (function (Private) {
        /**
         * Create the DOM node for a menu bar.
         */
        function createNode() {
            let node = document.createElement('div');
            let content = document.createElement('ul');
            content.className = 'lm-MenuBar-content';
            node.appendChild(content);
            content.setAttribute('role', 'menubar');
            return node;
        }
        Private.createNode = createNode;
        /**
         * Find the best matching mnemonic item.
         *
         * The search starts at the given index and wraps around.
         */
        function findMnemonic(menus, key, start) {
            // Setup the result variables.
            let index = -1;
            let auto = -1;
            let multiple = false;
            // Normalize the key to upper case.
            let upperKey = key.toUpperCase();
            // Search the items from the given start index.
            for (let i = 0, n = menus.length; i < n; ++i) {
                // Compute the wrapped index.
                let k = (i + start) % n;
                // Look up the menu title.
                let title = menus[k].title;
                // Ignore titles with an empty label.
                if (title.label.length === 0) {
                    continue;
                }
                // Look up the mnemonic index for the label.
                let mn = title.mnemonic;
                // Handle a valid mnemonic index.
                if (mn >= 0 && mn < title.label.length) {
                    if (title.label[mn].toUpperCase() === upperKey) {
                        if (index === -1) {
                            index = k;
                        }
                        else {
                            multiple = true;
                        }
                    }
                    continue;
                }
                // Finally, handle the auto index if possible.
                if (auto === -1 && title.label[0].toUpperCase() === upperKey) {
                    auto = k;
                }
            }
            // Return the search results.
            return { index, multiple, auto };
        }
        Private.findMnemonic = findMnemonic;
    })(Private$3 || (Private$3 = {}));

    /**
     * A widget which implements a canonical scroll bar.
     */
    class ScrollBar extends Widget {
        /**
         * Construct a new scroll bar.
         *
         * @param options - The options for initializing the scroll bar.
         */
        constructor(options = {}) {
            super({ node: Private$2.createNode() });
            /**
             * A timeout callback for repeating the mouse press.
             */
            this._onRepeat = () => {
                // Clear the repeat timer id.
                this._repeatTimer = -1;
                // Bail if the mouse has been released.
                if (!this._pressData) {
                    return;
                }
                // Look up the part that was pressed.
                let part = this._pressData.part;
                // Bail if the thumb was pressed.
                if (part === 'thumb') {
                    return;
                }
                // Schedule the timer for another repeat.
                this._repeatTimer = window.setTimeout(this._onRepeat, 20);
                // Get the current mouse position.
                let mouseX = this._pressData.mouseX;
                let mouseY = this._pressData.mouseY;
                // Handle a decrement button repeat.
                if (part === 'decrement') {
                    // Bail if the mouse is not over the button.
                    if (!domutils.ElementExt.hitTest(this.decrementNode, mouseX, mouseY)) {
                        return;
                    }
                    // Emit the step requested signal.
                    this._stepRequested.emit('decrement');
                    // Finished.
                    return;
                }
                // Handle an increment button repeat.
                if (part === 'increment') {
                    // Bail if the mouse is not over the button.
                    if (!domutils.ElementExt.hitTest(this.incrementNode, mouseX, mouseY)) {
                        return;
                    }
                    // Emit the step requested signal.
                    this._stepRequested.emit('increment');
                    // Finished.
                    return;
                }
                // Handle a track repeat.
                if (part === 'track') {
                    // Bail if the mouse is not over the track.
                    if (!domutils.ElementExt.hitTest(this.trackNode, mouseX, mouseY)) {
                        return;
                    }
                    // Fetch the thumb node.
                    let thumbNode = this.thumbNode;
                    // Bail if the mouse is over the thumb.
                    if (domutils.ElementExt.hitTest(thumbNode, mouseX, mouseY)) {
                        return;
                    }
                    // Fetch the client rect for the thumb.
                    let thumbRect = thumbNode.getBoundingClientRect();
                    // Determine the direction for the page request.
                    let dir;
                    if (this._orientation === 'horizontal') {
                        dir = mouseX < thumbRect.left ? 'decrement' : 'increment';
                    }
                    else {
                        dir = mouseY < thumbRect.top ? 'decrement' : 'increment';
                    }
                    // Emit the page requested signal.
                    this._pageRequested.emit(dir);
                    // Finished.
                    return;
                }
            };
            this._value = 0;
            this._page = 10;
            this._maximum = 100;
            this._repeatTimer = -1;
            this._pressData = null;
            this._thumbMoved = new signaling.Signal(this);
            this._stepRequested = new signaling.Signal(this);
            this._pageRequested = new signaling.Signal(this);
            this.addClass('lm-ScrollBar');
            this.setFlag(Widget.Flag.DisallowLayout);
            // Set the orientation.
            this._orientation = options.orientation || 'vertical';
            this.dataset['orientation'] = this._orientation;
            // Parse the rest of the options.
            if (options.maximum !== undefined) {
                this._maximum = Math.max(0, options.maximum);
            }
            if (options.page !== undefined) {
                this._page = Math.max(0, options.page);
            }
            if (options.value !== undefined) {
                this._value = Math.max(0, Math.min(options.value, this._maximum));
            }
        }
        /**
         * A signal emitted when the user moves the scroll thumb.
         *
         * #### Notes
         * The payload is the current value of the scroll bar.
         */
        get thumbMoved() {
            return this._thumbMoved;
        }
        /**
         * A signal emitted when the user clicks a step button.
         *
         * #### Notes
         * The payload is whether a decrease or increase is requested.
         */
        get stepRequested() {
            return this._stepRequested;
        }
        /**
         * A signal emitted when the user clicks the scroll track.
         *
         * #### Notes
         * The payload is whether a decrease or increase is requested.
         */
        get pageRequested() {
            return this._pageRequested;
        }
        /**
         * Get the orientation of the scroll bar.
         */
        get orientation() {
            return this._orientation;
        }
        /**
         * Set the orientation of the scroll bar.
         */
        set orientation(value) {
            // Do nothing if the orientation does not change.
            if (this._orientation === value) {
                return;
            }
            // Release the mouse before making changes.
            this._releaseMouse();
            // Update the internal orientation.
            this._orientation = value;
            this.dataset['orientation'] = value;
            // Schedule an update the scroll bar.
            this.update();
        }
        /**
         * Get the current value of the scroll bar.
         */
        get value() {
            return this._value;
        }
        /**
         * Set the current value of the scroll bar.
         *
         * #### Notes
         * The value will be clamped to the range `[0, maximum]`.
         */
        set value(value) {
            // Clamp the value to the allowable range.
            value = Math.max(0, Math.min(value, this._maximum));
            // Do nothing if the value does not change.
            if (this._value === value) {
                return;
            }
            // Update the internal value.
            this._value = value;
            // Schedule an update the scroll bar.
            this.update();
        }
        /**
         * Get the page size of the scroll bar.
         *
         * #### Notes
         * The page size is the amount of visible content in the scrolled
         * region, expressed in data units. It determines the size of the
         * scroll bar thumb.
         */
        get page() {
            return this._page;
        }
        /**
         * Set the page size of the scroll bar.
         *
         * #### Notes
         * The page size will be clamped to the range `[0, Infinity]`.
         */
        set page(value) {
            // Clamp the page size to the allowable range.
            value = Math.max(0, value);
            // Do nothing if the value does not change.
            if (this._page === value) {
                return;
            }
            // Update the internal page size.
            this._page = value;
            // Schedule an update the scroll bar.
            this.update();
        }
        /**
         * Get the maximum value of the scroll bar.
         */
        get maximum() {
            return this._maximum;
        }
        /**
         * Set the maximum value of the scroll bar.
         *
         * #### Notes
         * The max size will be clamped to the range `[0, Infinity]`.
         */
        set maximum(value) {
            // Clamp the value to the allowable range.
            value = Math.max(0, value);
            // Do nothing if the value does not change.
            if (this._maximum === value) {
                return;
            }
            // Update the internal values.
            this._maximum = value;
            // Clamp the current value to the new range.
            this._value = Math.min(this._value, value);
            // Schedule an update the scroll bar.
            this.update();
        }
        /**
         * The scroll bar decrement button node.
         *
         * #### Notes
         * Modifying this node directly can lead to undefined behavior.
         */
        get decrementNode() {
            return this.node.getElementsByClassName('lm-ScrollBar-button')[0];
        }
        /**
         * The scroll bar increment button node.
         *
         * #### Notes
         * Modifying this node directly can lead to undefined behavior.
         */
        get incrementNode() {
            return this.node.getElementsByClassName('lm-ScrollBar-button')[1];
        }
        /**
         * The scroll bar track node.
         *
         * #### Notes
         * Modifying this node directly can lead to undefined behavior.
         */
        get trackNode() {
            return this.node.getElementsByClassName('lm-ScrollBar-track')[0];
        }
        /**
         * The scroll bar thumb node.
         *
         * #### Notes
         * Modifying this node directly can lead to undefined behavior.
         */
        get thumbNode() {
            return this.node.getElementsByClassName('lm-ScrollBar-thumb')[0];
        }
        /**
         * Handle the DOM events for the scroll bar.
         *
         * @param event - The DOM event sent to the scroll bar.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events on the scroll bar's DOM node.
         *
         * This should not be called directly by user code.
         */
        handleEvent(event) {
            switch (event.type) {
                case 'mousedown':
                    this._evtMouseDown(event);
                    break;
                case 'mousemove':
                    this._evtMouseMove(event);
                    break;
                case 'mouseup':
                    this._evtMouseUp(event);
                    break;
                case 'keydown':
                    this._evtKeyDown(event);
                    break;
                case 'contextmenu':
                    event.preventDefault();
                    event.stopPropagation();
                    break;
            }
        }
        /**
         * A method invoked on a 'before-attach' message.
         */
        onBeforeAttach(msg) {
            this.node.addEventListener('mousedown', this);
            this.update();
        }
        /**
         * A method invoked on an 'after-detach' message.
         */
        onAfterDetach(msg) {
            this.node.removeEventListener('mousedown', this);
            this._releaseMouse();
        }
        /**
         * A method invoked on an 'update-request' message.
         */
        onUpdateRequest(msg) {
            // Convert the value and page into percentages.
            let value = (this._value * 100) / this._maximum;
            let page = (this._page * 100) / (this._page + this._maximum);
            // Clamp the value and page to the relevant range.
            value = Math.max(0, Math.min(value, 100));
            page = Math.max(0, Math.min(page, 100));
            // Fetch the thumb style.
            let thumbStyle = this.thumbNode.style;
            // Update the thumb style for the current orientation.
            if (this._orientation === 'horizontal') {
                thumbStyle.top = '';
                thumbStyle.height = '';
                thumbStyle.left = `${value}%`;
                thumbStyle.width = `${page}%`;
                thumbStyle.transform = `translate(${-value}%, 0%)`;
            }
            else {
                thumbStyle.left = '';
                thumbStyle.width = '';
                thumbStyle.top = `${value}%`;
                thumbStyle.height = `${page}%`;
                thumbStyle.transform = `translate(0%, ${-value}%)`;
            }
        }
        /**
         * Handle the `'keydown'` event for the scroll bar.
         */
        _evtKeyDown(event) {
            // Stop all input events during drag.
            event.preventDefault();
            event.stopPropagation();
            // Ignore anything except the `Escape` key.
            if (event.keyCode !== 27) {
                return;
            }
            // Fetch the previous scroll value.
            let value = this._pressData ? this._pressData.value : -1;
            // Release the mouse.
            this._releaseMouse();
            // Restore the old scroll value if possible.
            if (value !== -1) {
                this._moveThumb(value);
            }
        }
        /**
         * Handle the `'mousedown'` event for the scroll bar.
         */
        _evtMouseDown(event) {
            // Do nothing if it's not a left mouse press.
            if (event.button !== 0) {
                return;
            }
            // Send an activate request to the scroll bar. This can be
            // used by message hooks to activate something relevant.
            this.activate();
            // Do nothing if the mouse is already captured.
            if (this._pressData) {
                return;
            }
            // Find the pressed scroll bar part.
            let part = Private$2.findPart(this, event.target);
            // Do nothing if the part is not of interest.
            if (!part) {
                return;
            }
            // Stop the event propagation.
            event.preventDefault();
            event.stopPropagation();
            // Override the mouse cursor.
            let override = dragdrop.Drag.overrideCursor('default');
            // Set up the press data.
            this._pressData = {
                part,
                override,
                delta: -1,
                value: -1,
                mouseX: event.clientX,
                mouseY: event.clientY
            };
            // Add the extra event listeners.
            document.addEventListener('mousemove', this, true);
            document.addEventListener('mouseup', this, true);
            document.addEventListener('keydown', this, true);
            document.addEventListener('contextmenu', this, true);
            // Handle a thumb press.
            if (part === 'thumb') {
                // Fetch the thumb node.
                let thumbNode = this.thumbNode;
                // Fetch the client rect for the thumb.
                let thumbRect = thumbNode.getBoundingClientRect();
                // Update the press data delta for the current orientation.
                if (this._orientation === 'horizontal') {
                    this._pressData.delta = event.clientX - thumbRect.left;
                }
                else {
                    this._pressData.delta = event.clientY - thumbRect.top;
                }
                // Add the active class to the thumb node.
                thumbNode.classList.add('lm-mod-active');
                // Store the current value in the press data.
                this._pressData.value = this._value;
                // Finished.
                return;
            }
            // Handle a track press.
            if (part === 'track') {
                // Fetch the client rect for the thumb.
                let thumbRect = this.thumbNode.getBoundingClientRect();
                // Determine the direction for the page request.
                let dir;
                if (this._orientation === 'horizontal') {
                    dir = event.clientX < thumbRect.left ? 'decrement' : 'increment';
                }
                else {
                    dir = event.clientY < thumbRect.top ? 'decrement' : 'increment';
                }
                // Start the repeat timer.
                this._repeatTimer = window.setTimeout(this._onRepeat, 350);
                // Emit the page requested signal.
                this._pageRequested.emit(dir);
                // Finished.
                return;
            }
            // Handle a decrement button press.
            if (part === 'decrement') {
                // Add the active class to the decrement node.
                this.decrementNode.classList.add('lm-mod-active');
                // Start the repeat timer.
                this._repeatTimer = window.setTimeout(this._onRepeat, 350);
                // Emit the step requested signal.
                this._stepRequested.emit('decrement');
                // Finished.
                return;
            }
            // Handle an increment button press.
            if (part === 'increment') {
                // Add the active class to the increment node.
                this.incrementNode.classList.add('lm-mod-active');
                // Start the repeat timer.
                this._repeatTimer = window.setTimeout(this._onRepeat, 350);
                // Emit the step requested signal.
                this._stepRequested.emit('increment');
                // Finished.
                return;
            }
        }
        /**
         * Handle the `'mousemove'` event for the scroll bar.
         */
        _evtMouseMove(event) {
            // Do nothing if no drag is in progress.
            if (!this._pressData) {
                return;
            }
            // Stop the event propagation.
            event.preventDefault();
            event.stopPropagation();
            // Update the mouse position.
            this._pressData.mouseX = event.clientX;
            this._pressData.mouseY = event.clientY;
            // Bail if the thumb is not being dragged.
            if (this._pressData.part !== 'thumb') {
                return;
            }
            // Get the client rect for the thumb and track.
            let thumbRect = this.thumbNode.getBoundingClientRect();
            let trackRect = this.trackNode.getBoundingClientRect();
            // Fetch the scroll geometry based on the orientation.
            let trackPos;
            let trackSpan;
            if (this._orientation === 'horizontal') {
                trackPos = event.clientX - trackRect.left - this._pressData.delta;
                trackSpan = trackRect.width - thumbRect.width;
            }
            else {
                trackPos = event.clientY - trackRect.top - this._pressData.delta;
                trackSpan = trackRect.height - thumbRect.height;
            }
            // Compute the desired value from the scroll geometry.
            let value = trackSpan === 0 ? 0 : (trackPos * this._maximum) / trackSpan;
            // Move the thumb to the computed value.
            this._moveThumb(value);
        }
        /**
         * Handle the `'mouseup'` event for the scroll bar.
         */
        _evtMouseUp(event) {
            // Do nothing if it's not a left mouse release.
            if (event.button !== 0) {
                return;
            }
            // Stop the event propagation.
            event.preventDefault();
            event.stopPropagation();
            // Release the mouse.
            this._releaseMouse();
        }
        /**
         * Release the mouse and restore the node states.
         */
        _releaseMouse() {
            // Bail if there is no press data.
            if (!this._pressData) {
                return;
            }
            // Clear the repeat timer.
            clearTimeout(this._repeatTimer);
            this._repeatTimer = -1;
            // Clear the press data.
            this._pressData.override.dispose();
            this._pressData = null;
            // Remove the extra event listeners.
            document.removeEventListener('mousemove', this, true);
            document.removeEventListener('mouseup', this, true);
            document.removeEventListener('keydown', this, true);
            document.removeEventListener('contextmenu', this, true);
            // Remove the active classes from the nodes.
            this.thumbNode.classList.remove('lm-mod-active');
            this.decrementNode.classList.remove('lm-mod-active');
            this.incrementNode.classList.remove('lm-mod-active');
        }
        /**
         * Move the thumb to the specified position.
         */
        _moveThumb(value) {
            // Clamp the value to the allowed range.
            value = Math.max(0, Math.min(value, this._maximum));
            // Bail if the value does not change.
            if (this._value === value) {
                return;
            }
            // Update the internal value.
            this._value = value;
            // Schedule an update of the scroll bar.
            this.update();
            // Emit the thumb moved signal.
            this._thumbMoved.emit(value);
        }
    }
    /**
     * The namespace for the module implementation details.
     */
    var Private$2;
    (function (Private) {
        /**
         * Create the DOM node for a scroll bar.
         */
        function createNode() {
            let node = document.createElement('div');
            let decrement = document.createElement('div');
            let increment = document.createElement('div');
            let track = document.createElement('div');
            let thumb = document.createElement('div');
            decrement.className = 'lm-ScrollBar-button';
            increment.className = 'lm-ScrollBar-button';
            decrement.dataset['action'] = 'decrement';
            increment.dataset['action'] = 'increment';
            track.className = 'lm-ScrollBar-track';
            thumb.className = 'lm-ScrollBar-thumb';
            track.appendChild(thumb);
            node.appendChild(decrement);
            node.appendChild(track);
            node.appendChild(increment);
            return node;
        }
        Private.createNode = createNode;
        /**
         * Find the scroll bar part which contains the given target.
         */
        function findPart(scrollBar, target) {
            // Test the thumb.
            if (scrollBar.thumbNode.contains(target)) {
                return 'thumb';
            }
            // Test the track.
            if (scrollBar.trackNode.contains(target)) {
                return 'track';
            }
            // Test the decrement button.
            if (scrollBar.decrementNode.contains(target)) {
                return 'decrement';
            }
            // Test the increment button.
            if (scrollBar.incrementNode.contains(target)) {
                return 'increment';
            }
            // Indicate no match.
            return null;
        }
        Private.findPart = findPart;
    })(Private$2 || (Private$2 = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A concrete layout implementation which holds a single widget.
     *
     * #### Notes
     * This class is useful for creating simple container widgets which
     * hold a single child. The child should be positioned with CSS.
     */
    class SingletonLayout extends Layout {
        constructor() {
            super(...arguments);
            this._widget = null;
        }
        /**
         * Dispose of the resources held by the layout.
         */
        dispose() {
            if (this._widget) {
                let widget = this._widget;
                this._widget = null;
                widget.dispose();
            }
            super.dispose();
        }
        /**
         * Get the child widget for the layout.
         */
        get widget() {
            return this._widget;
        }
        /**
         * Set the child widget for the layout.
         *
         * #### Notes
         * Setting the child widget will cause the old child widget to be
         * automatically disposed. If that is not desired, set the parent
         * of the old child to `null` before assigning a new child.
         */
        set widget(widget) {
            // Remove the widget from its current parent. This is a no-op
            // if the widget's parent is already the layout parent widget.
            if (widget) {
                widget.parent = this.parent;
            }
            // Bail early if the widget does not change.
            if (this._widget === widget) {
                return;
            }
            // Dispose of the old child widget.
            if (this._widget) {
                this._widget.dispose();
            }
            // Update the internal widget.
            this._widget = widget;
            // Attach the new child widget if needed.
            if (this.parent && widget) {
                this.attachWidget(widget);
            }
        }
        /**
         * Create an iterator over the widgets in the layout.
         *
         * @returns A new iterator over the widgets in the layout.
         */
        *[Symbol.iterator]() {
            if (this._widget) {
                yield this._widget;
            }
        }
        /**
         * Remove a widget from the layout.
         *
         * @param widget - The widget to remove from the layout.
         *
         * #### Notes
         * A widget is automatically removed from the layout when its `parent`
         * is set to `null`. This method should only be invoked directly when
         * removing a widget from a layout which has yet to be installed on a
         * parent widget.
         *
         * This method does *not* modify the widget's `parent`.
         */
        removeWidget(widget) {
            // Bail early if the widget does not exist in the layout.
            if (this._widget !== widget) {
                return;
            }
            // Clear the internal widget.
            this._widget = null;
            // If the layout is parented, detach the widget from the DOM.
            if (this.parent) {
                this.detachWidget(widget);
            }
        }
        /**
         * Perform layout initialization which requires the parent widget.
         */
        init() {
            super.init();
            for (const widget of this) {
                this.attachWidget(widget);
            }
        }
        /**
         * Attach a widget to the parent's DOM node.
         *
         * @param widget - The widget to attach to the parent.
         *
         * #### Notes
         * This method is called automatically by the single layout at the
         * appropriate time. It should not be called directly by user code.
         *
         * The default implementation adds the widgets's node to the parent's
         * node at the proper location, and sends the appropriate attach
         * messages to the widget if the parent is attached to the DOM.
         *
         * Subclasses may reimplement this method to control how the widget's
         * node is added to the parent's node.
         */
        attachWidget(widget) {
            // Send a `'before-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            }
            // Add the widget's node to the parent.
            this.parent.node.appendChild(widget.node);
            // Send an `'after-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
            }
        }
        /**
         * Detach a widget from the parent's DOM node.
         *
         * @param widget - The widget to detach from the parent.
         *
         * #### Notes
         * This method is called automatically by the single layout at the
         * appropriate time. It should not be called directly by user code.
         *
         * The default implementation removes the widget's node from the
         * parent's node, and sends the appropriate detach messages to the
         * widget if the parent is attached to the DOM.
         *
         * Subclasses may reimplement this method to control how the widget's
         * node is removed from the parent's node.
         */
        detachWidget(widget) {
            // Send a `'before-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            }
            // Remove the widget's node from the parent.
            this.parent.node.removeChild(widget.node);
            // Send an `'after-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
            }
        }
    }

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A layout where visible widgets are stacked atop one another.
     *
     * #### Notes
     * The Z-order of the visible widgets follows their layout order.
     */
    class StackedLayout extends PanelLayout {
        constructor(options = {}) {
            super(options);
            this._dirty = false;
            this._items = [];
            this._box = null;
            this._hiddenMode =
                options.hiddenMode !== undefined
                    ? options.hiddenMode
                    : Widget.HiddenMode.Display;
        }
        /**
         * The method for hiding widgets.
         *
         * #### Notes
         * If there is only one child widget, `Display` hiding mode will be used
         * regardless of this setting.
         */
        get hiddenMode() {
            return this._hiddenMode;
        }
        /**
         * Set the method for hiding widgets.
         *
         * #### Notes
         * If there is only one child widget, `Display` hiding mode will be used
         * regardless of this setting.
         */
        set hiddenMode(v) {
            if (this._hiddenMode === v) {
                return;
            }
            this._hiddenMode = v;
            if (this.widgets.length > 1) {
                this.widgets.forEach(w => {
                    w.hiddenMode = this._hiddenMode;
                });
            }
        }
        /**
         * Dispose of the resources held by the layout.
         */
        dispose() {
            // Dispose of the layout items.
            for (const item of this._items) {
                item.dispose();
            }
            // Clear the layout state.
            this._box = null;
            this._items.length = 0;
            // Dispose of the rest of the layout.
            super.dispose();
        }
        /**
         * Attach a widget to the parent's DOM node.
         *
         * @param index - The current index of the widget in the layout.
         *
         * @param widget - The widget to attach to the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        attachWidget(index, widget) {
            // Using transform create an additional layer in the pixel pipeline
            // to limit the number of layer, it is set only if there is more than one widget.
            if (this._hiddenMode === Widget.HiddenMode.Scale &&
                this._items.length > 0) {
                if (this._items.length === 1) {
                    this.widgets[0].hiddenMode = Widget.HiddenMode.Scale;
                }
                widget.hiddenMode = Widget.HiddenMode.Scale;
            }
            else {
                widget.hiddenMode = Widget.HiddenMode.Display;
            }
            // Create and add a new layout item for the widget.
            algorithm.ArrayExt.insert(this._items, index, new LayoutItem(widget));
            // Send a `'before-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
            }
            // Add the widget's node to the parent.
            this.parent.node.appendChild(widget.node);
            // Send an `'after-attach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
            }
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * Move a widget in the parent's DOM node.
         *
         * @param fromIndex - The previous index of the widget in the layout.
         *
         * @param toIndex - The current index of the widget in the layout.
         *
         * @param widget - The widget to move in the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        moveWidget(fromIndex, toIndex, widget) {
            // Move the layout item for the widget.
            algorithm.ArrayExt.move(this._items, fromIndex, toIndex);
            // Post an update request for the parent widget.
            this.parent.update();
        }
        /**
         * Detach a widget from the parent's DOM node.
         *
         * @param index - The previous index of the widget in the layout.
         *
         * @param widget - The widget to detach from the parent.
         *
         * #### Notes
         * This is a reimplementation of the superclass method.
         */
        detachWidget(index, widget) {
            // Remove the layout item for the widget.
            let item = algorithm.ArrayExt.removeAt(this._items, index);
            // Send a `'before-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach);
            }
            // Remove the widget's node from the parent.
            this.parent.node.removeChild(widget.node);
            // Send an `'after-detach'` message if the parent is attached.
            if (this.parent.isAttached) {
                messaging.MessageLoop.sendMessage(widget, Widget.Msg.AfterDetach);
            }
            // Reset the z-index for the widget.
            item.widget.node.style.zIndex = '';
            // Reset the hidden mode for the widget.
            if (this._hiddenMode === Widget.HiddenMode.Scale) {
                widget.hiddenMode = Widget.HiddenMode.Display;
                // Reset the hidden mode for the first widget if necessary.
                if (this._items.length === 1) {
                    this._items[0].widget.hiddenMode = Widget.HiddenMode.Display;
                }
            }
            // Dispose of the layout item.
            item.dispose();
            // Post a fit request for the parent widget.
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'before-show'` message.
         */
        onBeforeShow(msg) {
            super.onBeforeShow(msg);
            this.parent.update();
        }
        /**
         * A message handler invoked on a `'before-attach'` message.
         */
        onBeforeAttach(msg) {
            super.onBeforeAttach(msg);
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-shown'` message.
         */
        onChildShown(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'child-hidden'` message.
         */
        onChildHidden(msg) {
            this.parent.fit();
        }
        /**
         * A message handler invoked on a `'resize'` message.
         */
        onResize(msg) {
            if (this.parent.isVisible) {
                this._update(msg.width, msg.height);
            }
        }
        /**
         * A message handler invoked on an `'update-request'` message.
         */
        onUpdateRequest(msg) {
            if (this.parent.isVisible) {
                this._update(-1, -1);
            }
        }
        /**
         * A message handler invoked on a `'fit-request'` message.
         */
        onFitRequest(msg) {
            if (this.parent.isAttached) {
                this._fit();
            }
        }
        /**
         * Fit the layout to the total size required by the widgets.
         */
        _fit() {
            // Set up the computed minimum size.
            let minW = 0;
            let minH = 0;
            // Update the computed minimum size.
            for (let i = 0, n = this._items.length; i < n; ++i) {
                // Fetch the item.
                let item = this._items[i];
                // Ignore hidden items.
                if (item.isHidden) {
                    continue;
                }
                // Update the size limits for the item.
                item.fit();
                // Update the computed minimum size.
                minW = Math.max(minW, item.minWidth);
                minH = Math.max(minH, item.minHeight);
            }
            // Update the box sizing and add it to the computed min size.
            let box = (this._box = domutils.ElementExt.boxSizing(this.parent.node));
            minW += box.horizontalSum;
            minH += box.verticalSum;
            // Update the parent's min size constraints.
            let style = this.parent.node.style;
            style.minWidth = `${minW}px`;
            style.minHeight = `${minH}px`;
            // Set the dirty flag to ensure only a single update occurs.
            this._dirty = true;
            // Notify the ancestor that it should fit immediately. This may
            // cause a resize of the parent, fulfilling the required update.
            if (this.parent.parent) {
                messaging.MessageLoop.sendMessage(this.parent.parent, Widget.Msg.FitRequest);
            }
            // If the dirty flag is still set, the parent was not resized.
            // Trigger the required update on the parent widget immediately.
            if (this._dirty) {
                messaging.MessageLoop.sendMessage(this.parent, Widget.Msg.UpdateRequest);
            }
        }
        /**
         * Update the layout position and size of the widgets.
         *
         * The parent offset dimensions should be `-1` if unknown.
         */
        _update(offsetWidth, offsetHeight) {
            // Clear the dirty flag to indicate the update occurred.
            this._dirty = false;
            // Compute the visible item count.
            let nVisible = 0;
            for (let i = 0, n = this._items.length; i < n; ++i) {
                nVisible += +!this._items[i].isHidden;
            }
            // Bail early if there are no visible items to layout.
            if (nVisible === 0) {
                return;
            }
            // Measure the parent if the offset dimensions are unknown.
            if (offsetWidth < 0) {
                offsetWidth = this.parent.node.offsetWidth;
            }
            if (offsetHeight < 0) {
                offsetHeight = this.parent.node.offsetHeight;
            }
            // Ensure the parent box sizing data is computed.
            if (!this._box) {
                this._box = domutils.ElementExt.boxSizing(this.parent.node);
            }
            // Compute the actual layout bounds adjusted for border and padding.
            let top = this._box.paddingTop;
            let left = this._box.paddingLeft;
            let width = offsetWidth - this._box.horizontalSum;
            let height = offsetHeight - this._box.verticalSum;
            // Update the widget stacking order and layout geometry.
            for (let i = 0, n = this._items.length; i < n; ++i) {
                // Fetch the item.
                let item = this._items[i];
                // Ignore hidden items.
                if (item.isHidden) {
                    continue;
                }
                // Set the z-index for the widget.
                item.widget.node.style.zIndex = `${i}`;
                // Update the item geometry.
                item.update(left, top, width, height);
            }
        }
    }

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A panel where visible widgets are stacked atop one another.
     *
     * #### Notes
     * This class provides a convenience wrapper around a {@link StackedLayout}.
     */
    class StackedPanel extends Panel {
        /**
         * Construct a new stacked panel.
         *
         * @param options - The options for initializing the panel.
         */
        constructor(options = {}) {
            super({ layout: Private$1.createLayout(options) });
            this._widgetRemoved = new signaling.Signal(this);
            this.addClass('lm-StackedPanel');
        }
        /**
         * The method for hiding widgets.
         *
         * #### Notes
         * If there is only one child widget, `Display` hiding mode will be used
         * regardless of this setting.
         */
        get hiddenMode() {
            return this.layout.hiddenMode;
        }
        /**
         * Set the method for hiding widgets.
         *
         * #### Notes
         * If there is only one child widget, `Display` hiding mode will be used
         * regardless of this setting.
         */
        set hiddenMode(v) {
            this.layout.hiddenMode = v;
        }
        /**
         * A signal emitted when a widget is removed from a stacked panel.
         */
        get widgetRemoved() {
            return this._widgetRemoved;
        }
        /**
         * A message handler invoked on a `'child-added'` message.
         */
        onChildAdded(msg) {
            msg.child.addClass('lm-StackedPanel-child');
        }
        /**
         * A message handler invoked on a `'child-removed'` message.
         */
        onChildRemoved(msg) {
            msg.child.removeClass('lm-StackedPanel-child');
            this._widgetRemoved.emit(msg.child);
        }
    }
    /**
     * The namespace for the module implementation details.
     */
    var Private$1;
    (function (Private) {
        /**
         * Create a stacked layout for the given panel options.
         */
        function createLayout(options) {
            return options.layout || new StackedLayout();
        }
        Private.createLayout = createLayout;
    })(Private$1 || (Private$1 = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * A widget which combines a `TabBar` and a `StackedPanel`.
     *
     * #### Notes
     * This is a simple panel which handles the common case of a tab bar
     * placed next to a content area. The selected tab controls the widget
     * which is shown in the content area.
     *
     * For use cases which require more control than is provided by this
     * panel, the `TabBar` widget may be used independently.
     */
    class TabPanel extends Widget {
        /**
         * Construct a new tab panel.
         *
         * @param options - The options for initializing the tab panel.
         */
        constructor(options = {}) {
            super();
            this._currentChanged = new signaling.Signal(this);
            this._addRequested = new signaling.Signal(this);
            this.addClass('lm-TabPanel');
            // Create the tab bar and stacked panel.
            this.tabBar = new TabBar(options);
            this.tabBar.addClass('lm-TabPanel-tabBar');
            this.stackedPanel = new StackedPanel();
            this.stackedPanel.addClass('lm-TabPanel-stackedPanel');
            // Connect the tab bar signal handlers.
            this.tabBar.tabMoved.connect(this._onTabMoved, this);
            this.tabBar.currentChanged.connect(this._onCurrentChanged, this);
            this.tabBar.tabCloseRequested.connect(this._onTabCloseRequested, this);
            this.tabBar.tabActivateRequested.connect(this._onTabActivateRequested, this);
            this.tabBar.addRequested.connect(this._onTabAddRequested, this);
            // Connect the stacked panel signal handlers.
            this.stackedPanel.widgetRemoved.connect(this._onWidgetRemoved, this);
            // Get the data related to the placement.
            this._tabPlacement = options.tabPlacement || 'top';
            let direction = Private.directionFromPlacement(this._tabPlacement);
            let orientation = Private.orientationFromPlacement(this._tabPlacement);
            // Configure the tab bar for the placement.
            this.tabBar.orientation = orientation;
            this.tabBar.dataset['placement'] = this._tabPlacement;
            // Create the box layout.
            let layout = new BoxLayout({ direction, spacing: 0 });
            // Set the stretch factors for the child widgets.
            BoxLayout.setStretch(this.tabBar, 0);
            BoxLayout.setStretch(this.stackedPanel, 1);
            // Add the child widgets to the layout.
            layout.addWidget(this.tabBar);
            layout.addWidget(this.stackedPanel);
            // Install the layout on the tab panel.
            this.layout = layout;
        }
        /**
         * A signal emitted when the current tab is changed.
         *
         * #### Notes
         * This signal is emitted when the currently selected tab is changed
         * either through user or programmatic interaction.
         *
         * Notably, this signal is not emitted when the index of the current
         * tab changes due to tabs being inserted, removed, or moved. It is
         * only emitted when the actual current tab node is changed.
         */
        get currentChanged() {
            return this._currentChanged;
        }
        /**
         * Get the index of the currently selected tab.
         *
         * #### Notes
         * This will be `-1` if no tab is selected.
         */
        get currentIndex() {
            return this.tabBar.currentIndex;
        }
        /**
         * Set the index of the currently selected tab.
         *
         * #### Notes
         * If the index is out of range, it will be set to `-1`.
         */
        set currentIndex(value) {
            this.tabBar.currentIndex = value;
        }
        /**
         * Get the currently selected widget.
         *
         * #### Notes
         * This will be `null` if there is no selected tab.
         */
        get currentWidget() {
            let title = this.tabBar.currentTitle;
            return title ? title.owner : null;
        }
        /**
         * Set the currently selected widget.
         *
         * #### Notes
         * If the widget is not in the panel, it will be set to `null`.
         */
        set currentWidget(value) {
            this.tabBar.currentTitle = value ? value.title : null;
        }
        /**
         * Get the whether the tabs are movable by the user.
         *
         * #### Notes
         * Tabs can always be moved programmatically.
         */
        get tabsMovable() {
            return this.tabBar.tabsMovable;
        }
        /**
         * Set the whether the tabs are movable by the user.
         *
         * #### Notes
         * Tabs can always be moved programmatically.
         */
        set tabsMovable(value) {
            this.tabBar.tabsMovable = value;
        }
        /**
         * Get the whether the add button is enabled.
         *
         */
        get addButtonEnabled() {
            return this.tabBar.addButtonEnabled;
        }
        /**
         * Set the whether the add button is enabled.
         *
         */
        set addButtonEnabled(value) {
            this.tabBar.addButtonEnabled = value;
        }
        /**
         * Get the tab placement for the tab panel.
         *
         * #### Notes
         * This controls the position of the tab bar relative to the content.
         */
        get tabPlacement() {
            return this._tabPlacement;
        }
        /**
         * Set the tab placement for the tab panel.
         *
         * #### Notes
         * This controls the position of the tab bar relative to the content.
         */
        set tabPlacement(value) {
            // Bail if the placement does not change.
            if (this._tabPlacement === value) {
                return;
            }
            // Update the internal value.
            this._tabPlacement = value;
            // Get the values related to the placement.
            let direction = Private.directionFromPlacement(value);
            let orientation = Private.orientationFromPlacement(value);
            // Configure the tab bar for the placement.
            this.tabBar.orientation = orientation;
            this.tabBar.dataset['placement'] = value;
            // Update the layout direction.
            this.layout.direction = direction;
        }
        /**
         * A signal emitted when the add button on a tab bar is clicked.
         *
         */
        get addRequested() {
            return this._addRequested;
        }
        /**
         * A read-only array of the widgets in the panel.
         */
        get widgets() {
            return this.stackedPanel.widgets;
        }
        /**
         * Add a widget to the end of the tab panel.
         *
         * @param widget - The widget to add to the tab panel.
         *
         * #### Notes
         * If the widget is already contained in the panel, it will be moved.
         *
         * The widget's `title` is used to populate the tab.
         */
        addWidget(widget) {
            this.insertWidget(this.widgets.length, widget);
        }
        /**
         * Insert a widget into the tab panel at a specified index.
         *
         * @param index - The index at which to insert the widget.
         *
         * @param widget - The widget to insert into to the tab panel.
         *
         * #### Notes
         * If the widget is already contained in the panel, it will be moved.
         *
         * The widget's `title` is used to populate the tab.
         */
        insertWidget(index, widget) {
            if (widget !== this.currentWidget) {
                widget.hide();
            }
            this.stackedPanel.insertWidget(index, widget);
            this.tabBar.insertTab(index, widget.title);
            widget.node.setAttribute('role', 'tabpanel');
            let renderer = this.tabBar.renderer;
            if (renderer instanceof TabBar.Renderer) {
                let tabId = renderer.createTabKey({
                    title: widget.title,
                    current: false,
                    zIndex: 0
                });
                widget.node.setAttribute('aria-labelledby', tabId);
            }
        }
        /**
         * Handle the `currentChanged` signal from the tab bar.
         */
        _onCurrentChanged(sender, args) {
            // Extract the previous and current title from the args.
            let { previousIndex, previousTitle, currentIndex, currentTitle } = args;
            // Extract the widgets from the titles.
            let previousWidget = previousTitle ? previousTitle.owner : null;
            let currentWidget = currentTitle ? currentTitle.owner : null;
            // Hide the previous widget.
            if (previousWidget) {
                previousWidget.hide();
            }
            // Show the current widget.
            if (currentWidget) {
                currentWidget.show();
            }
            // Emit the `currentChanged` signal for the tab panel.
            this._currentChanged.emit({
                previousIndex,
                previousWidget,
                currentIndex,
                currentWidget
            });
            // Flush the message loop on IE and Edge to prevent flicker.
            if (domutils.Platform.IS_EDGE || domutils.Platform.IS_IE) {
                messaging.MessageLoop.flush();
            }
        }
        /**
         * Handle the `tabAddRequested` signal from the tab bar.
         */
        _onTabAddRequested(sender, args) {
            this._addRequested.emit(sender);
        }
        /**
         * Handle the `tabActivateRequested` signal from the tab bar.
         */
        _onTabActivateRequested(sender, args) {
            args.title.owner.activate();
        }
        /**
         * Handle the `tabCloseRequested` signal from the tab bar.
         */
        _onTabCloseRequested(sender, args) {
            args.title.owner.close();
        }
        /**
         * Handle the `tabMoved` signal from the tab bar.
         */
        _onTabMoved(sender, args) {
            this.stackedPanel.insertWidget(args.toIndex, args.title.owner);
        }
        /**
         * Handle the `widgetRemoved` signal from the stacked panel.
         */
        _onWidgetRemoved(sender, widget) {
            widget.node.removeAttribute('role');
            widget.node.removeAttribute('aria-labelledby');
            this.tabBar.removeTab(widget.title);
        }
    }
    /**
     * The namespace for the module implementation details.
     */
    var Private;
    (function (Private) {
        /**
         * Convert a tab placement to tab bar orientation.
         */
        function orientationFromPlacement(plc) {
            return placementToOrientationMap[plc];
        }
        Private.orientationFromPlacement = orientationFromPlacement;
        /**
         * Convert a tab placement to a box layout direction.
         */
        function directionFromPlacement(plc) {
            return placementToDirectionMap[plc];
        }
        Private.directionFromPlacement = directionFromPlacement;
        /**
         * A mapping of tab placement to tab bar orientation.
         */
        const placementToOrientationMap = {
            top: 'horizontal',
            left: 'vertical',
            right: 'vertical',
            bottom: 'horizontal'
        };
        /**
         * A mapping of tab placement to box layout direction.
         */
        const placementToDirectionMap = {
            top: 'top-to-bottom',
            left: 'left-to-right',
            right: 'right-to-left',
            bottom: 'bottom-to-top'
        };
    })(Private || (Private = {}));

    exports.AccordionLayout = AccordionLayout;
    exports.AccordionPanel = AccordionPanel;
    exports.BoxLayout = BoxLayout;
    exports.BoxPanel = BoxPanel;
    exports.BoxSizer = BoxSizer;
    exports.CommandPalette = CommandPalette;
    exports.ContextMenu = ContextMenu;
    exports.DockLayout = DockLayout;
    exports.DockPanel = DockPanel;
    exports.FocusTracker = FocusTracker;
    exports.GridLayout = GridLayout;
    exports.Layout = Layout;
    exports.LayoutItem = LayoutItem;
    exports.Menu = Menu;
    exports.MenuBar = MenuBar;
    exports.Panel = Panel;
    exports.PanelLayout = PanelLayout;
    exports.ScrollBar = ScrollBar;
    exports.SingletonLayout = SingletonLayout;
    exports.SplitLayout = SplitLayout;
    exports.SplitPanel = SplitPanel;
    exports.StackedLayout = StackedLayout;
    exports.StackedPanel = StackedPanel;
    exports.TabBar = TabBar;
    exports.TabPanel = TabPanel;
    exports.Title = Title;
    exports.Widget = Widget;

}));
//# sourceMappingURL=index.js.map