/*!
 * Copyright (c) 2026 Vertex Software LLC. All rights reserved.
 */
import { h, F as Fragment, r as registerInstance, c as createEvent, f as forceUpdate, H as Host, a as getElement } from './index-EpnuuCxl.js';
import { s as scene_tree_api_pb_serviceExports, w as webSocketSubscriptionTransportFactory, S as SceneTreeController, a as SceneTreeErrorDetails, b as SceneTreeErrorCode } from './controller-zACO6f4n.js';
import { p as parseAndValidateConfig } from './config-CfTifCEz.js';
import { i as isSceneTreeTableCellElement } from './utils-DC86ZXai.js';
import { g as getSceneTreeContainsElement } from './dom-DX6pEG54.js';
import { i as isLoadedRow } from './row-CiSHWNC4.js';
import { S as SCENE_ITEM_NAME_METADATA_KEY } from './types-BZwvQkyb.js';
import './wrappers_pb-Xw_x1jYm.js';
import './_commonjsHelpers-CG9AChtV.js';
import './errors-Cs13_LRq.js';
import './bundle-DLfw87TV.js';
import './bundle-DfdVaPrt.js';
import './events-CPk-nchx.js';
import './streamAttributes-B6xi3kRV.js';
import './bundle-CLE0fYge.js';
import './entities-BAhfr1Nx.js';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const SceneTreeError = ({ details }, children) => {
    return (h("div", { class: "error" }, h("div", { class: "error-section error-message" }, h("span", null, details.message), details.link && (h(Fragment, null, h("span", null, " See our "), h("a", { href: details.link, target: "_blank" }, "documentation"), ' ', h("span", null, " for more information.")))), h("div", { class: "error-section" }, children)));
};

const DEFAULT_SCENE_TREE_OP_ANIMATION_MS = 500;
async function showItem(viewer, id, { suppliedCorrelationId } = {}) {
    const scene = await viewer.scene();
    return scene
        .elements((op) => op.items.where((q) => q.withItemId(id)).show())
        .execute({ suppliedCorrelationId });
}
async function hideItem(viewer, id, { suppliedCorrelationId } = {}) {
    const scene = await viewer.scene();
    return scene
        .elements((op) => op.items.where((q) => q.withItemId(id)).hide())
        .execute({
        suppliedCorrelationId,
    });
}
async function selectItem(viewer, id, { append = false, suppliedCorrelationId }) {
    const scene = await viewer.scene();
    return scene
        .elements((op) => [
        ...(append ? [] : [op.items.where((q) => q.all()).deselect()]),
        op.items.where((q) => q.withItemId(id)).select(),
    ])
        .execute({ suppliedCorrelationId });
}
async function selectRangeInSceneTree(viewer, start, end, { append = true, suppliedCorrelationId }) {
    const scene = await viewer.scene();
    return scene
        .elements((op) => [
        ...(append ? [] : [op.items.where((q) => q.all()).deselect()]),
        op.items.where((q) => q.withSceneTreeRange({ start, end })).select(),
    ])
        .execute({
        suppliedCorrelationId,
    });
}
async function selectFilterResults(viewer, filter, keys, exactMatch, removeHiddenResults, { append = false, suppliedCorrelationId }) {
    const scene = await viewer.scene();
    return scene
        .elements((op) => [
        ...(append ? [] : [op.items.where((q) => q.all()).deselect()]),
        op.items
            .where((q) => q.withMetadata(filter, keys, exactMatch, removeHiddenResults))
            .select(),
    ])
        .execute({
        suppliedCorrelationId,
    });
}
async function deselectItem(viewer, id, { suppliedCorrelationId } = {}) {
    const scene = await viewer.scene();
    return scene
        .elements((op) => op.items.where((q) => q.withItemId(id)).deselect())
        .execute({
        suppliedCorrelationId,
    });
}
async function isolateItem(viewer, id, { suppliedCorrelationId, animationDurationMs } = {}) {
    const scene = await viewer.scene();
    await scene
        .elements((op) => [
        op.items.where((q) => q.all()).hide(),
        op.items.where((q) => q.withItemId(id)).show(),
    ])
        .execute({
        suppliedCorrelationId,
    });
    const renderResult = await scene
        .camera()
        .flyTo((q) => q.withItemId(id))
        .render({
        animation: {
            milliseconds: animationDurationMs !== null && animationDurationMs !== void 0 ? animationDurationMs : DEFAULT_SCENE_TREE_OP_ANIMATION_MS,
        },
    });
    await renderResult.onAnimationCompleted.once();
}

const sceneTreeCss = () => `button{border:none;background:transparent;cursor:pointer;margin:0;padding:0}.button{padding:0.5rem;border-radius:0.25rem}.button-secondary{color:var(--neutral-800);background-color:var(--neutral-300)}.button-secondary:hover{color:var(--neutral-900)}.button-secondary:hover:enabled{box-shadow:0 2px 2px rgb(0 0 0 / 20%)}.button-secondary:disabled{opacity:0.8}.button-secondary:hover:disabled{cursor:not-allowed}:host{--scene-tree-toolbar-separator:1px solid var(--neutral-300);--scene-tree-default-font-family:unset;--scene-tree-default-font-size:unset;--scene-tree-default-font-color:unset;--scene-tree-secondary-font-color:unset;--scene-tree-selected-row-background-color:unset;--scene-tree-hovered-row-background-color:unset;--scene-tree-row-background-color:unset;--scene-tree-row-background-color-depth-n:unset;--scene-tree-overlay-z-index:3000;width:300px;height:100%;user-select:none;-webkit-user-select:none;cursor:default;display:flex;flex-direction:column;position:relative}.rows-scroll{position:relative;height:var(--scene-tree-row-height, 0);flex-grow:1}.rows{position:relative}.row{position:absolute;display:flex;align-items:center;width:100%;height:var(--scene-tree-row-height);padding:var(--scene-tree-row-padding);box-sizing:border-box}.row:hover{background-color:var(--scene-tree-row-hover-color)}.row.is-selected{background-color:var(--scene-tree-row-selected-color)}.expand-toggle{cursor:pointer;width:16px}.row-text{margin-left:0.25rem;width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.visibility-btn{display:flex;align-items:center;visibility:hidden;padding:4px}.visibility-btn.is-hidden{visibility:visible}.row:hover .visibility-btn{visibility:visible}.error,.empty-results{position:absolute;top:0;left:0;display:flex;flex-direction:column;align-items:center;font-size:0.875rem;width:100%;height:100%;padding:1rem;box-sizing:border-box;justify-content:center;background-color:white;z-index:var(--scene-tree-overlay-z-index)}.error-section{margin-top:1rem}.error-message{text-align:center}.search-toolbar,slot[name='header']::slotted(*){border-bottom:var(--scene-tree-toolbar-separator)}slot[name='footer']::slotted(:not(:last-child)){border-top:var(--scene-tree-toolbar-separator)}.header{display:flex;flex-direction:column}.footer{display:flex;flex-direction:column}.loading{position:absolute;top:150px;left:50%;transform:translate(-50%, -50%);text-align:center}`;

/**
 * The minimum amount of time provided by requestIdleCallback to clear purged
 * data. A value too low may cause contention with browser rendering. A value
 * too high will cause too many items to be accumulated.
 */
const MIN_CLEAR_UNUSED_DATA_MS = 25;
const SceneTree = class {
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.connectionError = createEvent(this, "connectionError");
        this.firstRowRendered = createEvent(this, "firstRowRendered");
        /**
         * The number of offscreen rows above and below the viewport to render. Having
         * a higher number reduces the chance of the browser not displaying a row
         * while scrolling.
         */
        this.overScanCount = 25;
        /**
         * Sets the default environment for the viewer. This setting is used for
         * auto-configuring network hosts.
         *
         * Use the `config` property for manually setting hosts.
         */
        this.configEnv = 'platprod';
        /**
         * A set of options to configure scene tree searching behavior.
         */
        this.searchOptions = {};
        /**
         * @deprecated Use `searchOptions`
         * Indicates whether the metadata search should use an exact match.
         */
        this.metadataSearchExactMatch = false;
        /**
         * @deprecated Use `searchOptions`
         * A list of the metadata keys that a scene tree search should be performed on.
         */
        this.metadataSearchKeys = [];
        /**
         * A list of part metadata keys that will be made available to each row. This
         * metadata can be used for data binding inside the scene tree's template.
         *
         * **Note:** for the values of these metadata keys to be evaluated for search,
         * they must be provided to the `metadataSearchKeys` specified in the `searchOptions`.
         * Otherwise, the search will only be performed against the item name.
         */
        this.metadataKeys = [];
        /**
         * The duration of operations with animations, in milliseconds, when a user performs
         * an action that results in an animation such as isolate. Defaults to 500ms.
         */
        this.operationAnimationDuration = 500;
        this.rows = [];
        this.totalRows = 0;
        this.showLoader = false;
        this.showEmptyResults = false;
        /**
         * This stores internal state that you want to preserve across live-reloads,
         * but shouldn't trigger a refresh if the data changes. Marking this with
         * @State to allow to preserve state across live-reloads.
         */
        this.stateMap = {
            componentLoaded: false,
        };
        this.hasPartialFilterResults = false;
        this.refreshingResults = false;
        this.attemptingRetry = false;
        this.firstCellRendered = false;
    }
    /**
     * Schedules a render of the rows in the scene tree. Useful if any custom
     * data in your scene tree has changed, and you want to update the row's
     * contents.
     *
     * **Note:** This is an asynchronous operation. The update may happen on the
     * next frame.
     */
    async invalidateRows() {
        forceUpdate(this.getLayoutElement());
    }
    /**
     * Scrolls the tree to the given row index.
     *
     * @param index An index of the row to scroll to.
     * @param options A set of options to configure the scrolling behavior.
     */
    async scrollToIndex(index, options = {}) {
        const { animate, position = 'middle' } = options;
        const i = Math.max(0, Math.min(index, this.totalRows));
        const top = this.getScrollToPosition(i, position);
        await this.getLayoutElement().scrollToPosition(top, {
            behavior: animate ? 'smooth' : undefined,
        });
    }
    /**
     * Scrolls the tree to an item with the given ID. If the node for the item is
     * not expanded, the tree will expand each of its parent nodes.
     *
     * @param itemId An ID of an item to scroll to.
     * @param options A set of options to configure the scrolling behavior.
     * @returns A promise that resolves when the operation is finished.
     */
    async scrollToItem(itemId, options = {}) {
        var _a;
        const rowsBeforeExpand = this.totalRows;
        const index = await ((_a = this.controller) === null || _a === void 0 ? void 0 : _a.expandParentNodes(itemId));
        if (index != null && rowsBeforeExpand !== this.totalRows) {
            return new Promise((resolve) => {
                const layoutEl = this.getLayoutElement();
                const handleLayoutRendered = async () => {
                    layoutEl.removeEventListener('layoutRendered', handleLayoutRendered);
                    await this.scrollToIndex(index, options);
                    resolve();
                };
                layoutEl.addEventListener('layoutRendered', handleLayoutRendered);
            });
        }
        else if (index != null) {
            await this.scrollToIndex(index, options);
        }
    }
    /**
     * Performs an API call to expand all nodes in the tree.
     */
    async expandAll() {
        var _a;
        await ((_a = this.controller) === null || _a === void 0 ? void 0 : _a.expandAll());
    }
    /**
     * Performs an API call to collapse all nodes in the tree.
     */
    async collapseAll() {
        var _a;
        await ((_a = this.controller) === null || _a === void 0 ? void 0 : _a.collapseAll());
    }
    /**
     * Performs an API call that will expand the node associated to the specified
     * row or row index.
     *
     * @param row A row, row index, or node to expand.
     */
    async expandItem(row) {
        await this.performRowOperation(row, async ({ id, node }) => {
            var _a;
            if (!node.expanded) {
                await ((_a = this.controller) === null || _a === void 0 ? void 0 : _a.expandNode(id));
            }
        });
    }
    /**
     * Performs an API call that will collapse the node associated to the
     * specified row or row index.
     *
     * @param row A row, row index, or node to collapse.
     */
    async collapseItem(row) {
        await this.performRowOperation(row, async ({ id, node }) => {
            var _a;
            if (node.expanded) {
                await ((_a = this.controller) === null || _a === void 0 ? void 0 : _a.collapseNode(id));
            }
        });
    }
    /**
     * Performs an API call that will either expand or collapse the node
     * associated to the given row or row index.
     *
     * @param row The row, row index, or node to collapse or expand.
     */
    async toggleExpandItem(row) {
        await this.performRowOperation(row, async ({ node }) => {
            if (node.expanded) {
                await this.collapseItem(node);
            }
            else {
                await this.expandItem(node);
            }
        });
    }
    /**
     * Performs an API call that will either hide or show the item associated to
     * the given row or row index.
     *
     * @param row The row, row index, or node to toggle visibility.
     */
    async toggleItemVisibility(row) {
        await this.performRowOperation(row, async ({ viewer, id, node }) => {
            if (node.visible || node.partiallyVisible) {
                await hideItem(viewer, id);
            }
            else {
                await showItem(viewer, id);
            }
        });
    }
    /**
     * Performs an API call that will hide the item associated to the given row
     * or row index.
     *
     * @param row The row, row index, or node to hide.
     */
    async hideItem(row) {
        await this.performRowOperation(row, async ({ viewer, id, node }) => {
            if (node.visible) {
                await hideItem(viewer, id);
            }
        });
    }
    /**
     * Performs an API call that will show the item associated to the given row
     * or row index.
     *
     * @param row The row, row index, or node to show.
     */
    async showItem(row) {
        await this.performRowOperation(row, async ({ viewer, id, node }) => {
            if (!node.visible) {
                await showItem(viewer, id);
            }
        });
    }
    /**
     * Performs an API call that will select the item associated to the given row
     * or row index.
     *
     * This method supports a `recurseParent` option that allows for recursively
     * selecting the next unselected parent node. This behavior is considered
     * stateful. Each call to `selectItem` will track the ancestry of the passed
     * in `rowArg`. If calling `selectItem` with a row not belonging to the
     * ancestry of a previous selection, then this method will perform a standard
     * selection.
     *
     * @param row The row, row index or node to select.
     * @param options A set of options to configure selection behavior.
     */
    async selectItem(row, { recurseParent, ...options } = {}) {
        await this.performRowOperation(row, async ({ viewer, id }) => {
            var _a, _b, _c, _d;
            const ancestors = (await ((_a = this.controller) === null || _a === void 0 ? void 0 : _a.fetchNodeAncestors(id))) || [];
            // The parent node should be selected if recurseParent is true and either
            // the given node is in the selection path (so was intentionally selected by clicking in the tree)
            // or if the given node is selected, but its immediate parent is not. The second case covers the
            // situation that the given node was selected as part of a previous ancestor selection and the
            // user wishes to clear the selection list and only select the given node.
            const isInPath = (_b = this.stateMap.selectionPath) === null || _b === void 0 ? void 0 : _b.includes(id);
            const nodeForRow = this.getNodeFromRowOrIndex(row);
            const immediateParentNodeIsSelected = ancestors.length > 0 ? ancestors[0].selected : true;
            const nodeIsSelectedAndParentIsUnselected = nodeForRow.selected && !immediateParentNodeIsSelected;
            if (recurseParent && (isInPath || nodeIsSelectedAndParentIsUnselected)) {
                const nextNode = ancestors.find(({ selected }) => !selected);
                if (nextNode != null) {
                    await this.selectItem(nextNode, options);
                    this.lastSelectedItemId = id;
                }
                else {
                    await selectItem(viewer, id, options);
                    this.lastSelectedItemId = id;
                }
            }
            else if (options.range && this.lastSelectedItemId != null) {
                const currentRowIndex = await ((_c = this.controller) === null || _c === void 0 ? void 0 : _c.expandParentNodes(id));
                const previouslySelectedIndex = await ((_d = this.controller) === null || _d === void 0 ? void 0 : _d.expandParentNodes(this.lastSelectedItemId));
                if (previouslySelectedIndex && currentRowIndex) {
                    const start = Math.min(previouslySelectedIndex, currentRowIndex);
                    const end = Math.max(previouslySelectedIndex, currentRowIndex);
                    await selectRangeInSceneTree(viewer, start, end, options);
                }
            }
            else {
                await selectItem(viewer, id, options);
                this.lastSelectedItemId = id;
            }
            this.stateMap.selectionPath = [
                ...ancestors.map(({ id }) => (id === null || id === void 0 ? void 0 : id.hex) || ''),
                id,
            ];
        });
    }
    /**
     * Performs an API call that will deselect the item associated to the given
     * row or row index.
     *
     * @param row The row, row index, or node to deselect.
     */
    async deselectItem(row) {
        await this.performRowOperation(row, async ({ viewer, id, node }) => {
            if (node.selected) {
                this.lastSelectedItemId = undefined;
                await deselectItem(viewer, id);
            }
        });
    }
    /**
     * Performs an API call that will show only the item associated to
     * the given row or row index and fit the camera to the item's bounding box.
     *
     * @param row The row, row index, or node to isolate.
     */
    async isolateItem(row) {
        await this.performRowOperation(row, async ({ viewer, id }) => {
            await isolateItem(viewer, id, {
                animationDurationMs: this.operationAnimationDuration,
            });
        });
    }
    /**
     * Returns a row at the given index. If the row data has not been loaded,
     * returns `undefined`.
     *
     * @param index The index of the row.
     * @returns A row, or `undefined` if the row hasn't been loaded.
     */
    async getRowAtIndex(index) {
        return this.rows[index];
    }
    /**
     * Returns the row with the given id. If there is not a row
     * matching the id, returns `undefined`.
     *
     * @param itemId An ID of an item to return the row for.
     * @returns A row, or `undefined` if a corresponding row doesn't exist
     */
    async getRowForItemId(itemId) {
        var _a;
        const index = await ((_a = this.controller) === null || _a === void 0 ? void 0 : _a.expandParentNodes(itemId));
        if (index != null) {
            return this.rows[index];
        }
        else {
            return undefined;
        }
    }
    /**
     * Returns the row data from the given mouse or pointer event. The event must
     * originate from a `vertex-scene-tree-table-cell` contained by this element,
     * otherwise `undefined` is returned.
     *
     * @param event A mouse or pointer event that originated from this component.
     * @returns A row, or `undefined` if the row hasn't been loaded.
     */
    async getRowForEvent(event) {
        const { clientY, target } = event;
        if (target != null &&
            this.errorDetails == null &&
            getSceneTreeContainsElement(this.el, target) &&
            isSceneTreeTableCellElement(target)) {
            return this.getRowAtClientY(clientY);
        }
        else {
            return undefined;
        }
    }
    /**
     * Returns the row data from the given vertical client position.
     *
     * @param clientY The vertical client position.
     * @returns A row or `undefined` if the row hasn't been loaded.
     */
    getRowAtClientY(clientY) {
        const layoutEl = this.getLayoutElement();
        const top = layoutEl.layoutOffset;
        const index = Math.floor((clientY - top + layoutEl.scrollOffset) / layoutEl.rowHeight);
        return this.getRowAtIndex(index);
    }
    /**
     * Performs an async request that will filter the displayed items in the tree
     * that match the given term and options.
     *
     * @param term The filter term.
     * @param options The options to apply to the filter.
     * @returns A promise that completes when the request has completed. Note,
     *  items are displayed asynchronously. So the displayed items may not reflect
     *  the result of this filter when the promise completes.
     */
    async filterItems(term, options = {}) {
        var _a;
        const optionsAsFilterOptions = {
            ...options,
            metadataSearchKeys: options.columns,
        };
        const columnsToSearch = this.getMetadataSearchKeys(optionsAsFilterOptions);
        return (_a = this.controller) === null || _a === void 0 ? void 0 : _a.filter(term, {
            ...options,
            columns: columnsToSearch,
        });
    }
    /**
     * Performs an async request that will select the filtered items in the tree
     * that match the given term.
     *
     * @param term The filter term.
     * @returns A promise that completes when the request has completed.
     */
    async selectFilteredItems(term, options) {
        var _a, _b, _c, _d, _e, _f;
        if (this.viewer != null) {
            const metadataSearchKeys = (_b = (_a = this.searchOptions) === null || _a === void 0 ? void 0 : _a.metadataSearchKeys) !== null && _b !== void 0 ? _b : this.metadataSearchKeys;
            const definedMetadataKeys = metadataSearchKeys.length > 0 ? metadataSearchKeys : this.metadataKeys;
            const columnsToSearch = definedMetadataKeys.length > 0
                ? definedMetadataKeys
                : ['VERTEX_SCENE_ITEM_NAME'];
            const shouldSearchExactMatch = (_d = (_c = this.searchOptions) === null || _c === void 0 ? void 0 : _c.exactMatch) !== null && _d !== void 0 ? _d : this.metadataSearchExactMatch;
            const shouldSearchRemoveHiddenItems = (_f = (_e = this.searchOptions) === null || _e === void 0 ? void 0 : _e.removeHiddenItems) !== null && _f !== void 0 ? _f : false;
            await selectFilterResults(this.viewer, term, columnsToSearch, shouldSearchExactMatch, shouldSearchRemoveHiddenItems, {
                append: false,
                ...options,
            });
        }
    }
    /**
     * Fetches the metadata keys that are available to the scene tree. Metadata
     * keys can be assigned to the scene tree using the `metadataKeys` property.
     * The scene tree will fetch this metadata and make these values available
     * for data binding.
     *
     * @returns A promise that resolves with the names of available keys.
     */
    async fetchMetadataKeys() {
        var _a, _b;
        return (_b = (_a = this.controller) === null || _a === void 0 ? void 0 : _a.fetchMetadataKeys()) !== null && _b !== void 0 ? _b : [];
    }
    /**
     * @ignore
     */
    disconnectedCallback() {
        var _a, _b, _c;
        (_a = this.stateMap.viewerDisposable) === null || _a === void 0 ? void 0 : _a.dispose();
        (_b = this.stateMap.viewerSceneReadyDisposable) === null || _b === void 0 ? void 0 : _b.dispose();
        (_c = this.controller) === null || _c === void 0 ? void 0 : _c.cancel();
    }
    /**
     * @ignore
     */
    connectedCallback() {
        this.connectToViewer();
    }
    /**
     * @ignore
     */
    componentWillLoad() {
        var _a;
        if (this.controller == null) {
            const { sceneTreeHost } = this.getConfig().network;
            const client = new scene_tree_api_pb_serviceExports.SceneTreeAPIClient(sceneTreeHost, this.getConfig().flags.grpcUseStreamingWebSocketTransport
                ? {
                    transport: webSocketSubscriptionTransportFactory,
                }
                : undefined);
            this.controller = new SceneTreeController(client, 100);
            (_a = this.controller) === null || _a === void 0 ? void 0 : _a.setMetadataKeys(this.metadataKeys);
        }
        this.stateMap.onStateChangeDisposable = this.controller.onStateChange.on((state) => this.handleControllerStateChange(state));
        this.connectToViewer();
    }
    /**
     * @ignore
     */
    async componentDidLoad() {
        var _a;
        this.ensureLayoutDefined();
        this.updateLayoutElement();
        const layoutEl = this.getLayoutElement();
        const resizeObserver = new ResizeObserver(() => {
            this.invalidateRows();
        });
        resizeObserver.observe(layoutEl);
        this.stateMap.resizeObserver = resizeObserver;
        this.stateMap.componentLoaded = true;
        (_a = this.controller) === null || _a === void 0 ? void 0 : _a.setMetadataKeys(this.metadataKeys);
        if (this.viewer == null) {
            this.errorDetails = new SceneTreeErrorDetails('MISSING_VIEWER', SceneTreeErrorCode.MISSING_VIEWER);
        }
    }
    componentWillRender() {
        // The controller can load data prior to the first render
        // ensure that this renders any time the state changes.
        this.updateLayoutElement();
    }
    /**
     * @ignore
     */
    render() {
        return (h(Host, { key: '8e9ef4a71e8d7d1a1aeefa2016624eab34392923' }, h("div", { key: 'ebd64d4b347759e00a71f1230cd70d2809a5404e', class: "header" }, h("slot", { key: '6fca7d74ee3bd07e9c940bf4c13d6c92b4ec71f7', name: "header" }, h("vertex-scene-tree-toolbar", { key: '75aab7028ba9d4514976f8ba40df63761b3941b0', class: "search-toolbar" }, h("vertex-scene-tree-search", { key: '4f9d9c05120a4410b34c206d7bfce754978aa285', controller: this.controller })))), h("slot", { key: '84b5977063e55cc88ee740375f710591b1245b0a', name: "partial-filter-results-banner" }, this.hasPartialFilterResults && (h("vertex-scene-tree-notification-banner", { key: 'a6784c2c811cadfd1354c1df227d1dd5a24c0ef0', message: this.refreshingResults
                ? 'Refreshing results...'
                : 'Data loading, refresh for more.', actionLabel: "Refresh", onAction: () => { var _a; return (_a = this.controller) === null || _a === void 0 ? void 0 : _a.refreshFilter(); } }))), this.errorDetails != null && this.renderError(this.errorDetails), this.errorDetails == null && (h("div", { key: 'b31b8dd61936644886963a14690a5e6a7e89f253', class: "rows-scroll" }, this.showLoader && (h("slot", { key: '974d731c0430a9d739b3b4142a1e3b1d40ca3a35', name: "loading" }, h("vertex-viewer-spinner", { key: 'f0bdfbe2bb315527b523d4d318f155310b3af0ba', class: "loading", size: "md" }))), h("slot", { key: '0f8ee9a72cc72b43782ad40249f5f76f7b9147b3' }), this.showEmptyResults && (h("slot", { key: '64b69d0144e2a149181aead8d18f2a351c996664', name: "empty-results" }, h("div", { key: '2152af85264c466ed75d26193e242f9bd0db04c5', class: "empty-results" }, "No Results Found."))))), h("div", { key: '45fc6cdc33b3decc4352b0d5747ee299e8324ff0', class: "footer" }, h("slot", { key: 'd6b197db964a2c55c29172d39060ece3584da7d9', name: "footer" }))));
    }
    renderError(details) {
        if (details.code !== SceneTreeErrorCode.SCENE_TREE_DISABLED) {
            return (h(SceneTreeError, { details: details }, h("button", { class: "button button-secondary", onClick: () => this.retryConnectToViewer(), disabled: this.attemptingRetry }, "Retry")));
        }
        else {
            return h(SceneTreeError, { details: details });
        }
    }
    /**
     * @ignore
     */
    handleViewerChanged(newViewer, oldViewer) {
        // StencilJS will invoke this callback even before the component has been
        // loaded. According to their docs, this shouldn't happen. Return if the
        // component hasn't been loaded.
        // See https://stenciljs.com/docs/reactive-data#watch-decorator
        if (!this.stateMap.componentLoaded) {
            return;
        }
        this.connectToViewer();
    }
    /**
     * @ignore
     */
    handleControllerChanged(newController) {
        var _a;
        // StencilJS will invoke this callback even before the component has been
        // loaded. According to their docs, this shouldn't happen. Return if the
        // component hasn't been loaded.
        // See https://stenciljs.com/docs/reactive-data#watch-decorator
        if (!this.stateMap.componentLoaded) {
            return;
        }
        (_a = this.stateMap.onStateChangeDisposable) === null || _a === void 0 ? void 0 : _a.dispose();
        this.stateMap.onStateChangeDisposable = newController.onStateChange.on((state) => this.handleControllerStateChange(state));
        newController.setMetadataKeys(this.metadataKeys);
    }
    /**
     * @ignore
     */
    handleMetadataKeysChanged() {
        var _a;
        (_a = this.controller) === null || _a === void 0 ? void 0 : _a.setMetadataKeys(this.metadataKeys);
    }
    retryConnectToViewer() {
        this.attemptingRetry = true;
        this.errorDetails = undefined;
        this.connectToViewer();
    }
    connectToViewer() {
        var _a, _b, _c;
        (_a = this.stateMap.viewerDisposable) === null || _a === void 0 ? void 0 : _a.dispose();
        (_b = this.stateMap.viewerSceneReadyDisposable) === null || _b === void 0 ? void 0 : _b.dispose();
        if (this.viewer == null && this.viewerSelector != null) {
            this.viewer = document.querySelector(this.viewerSelector);
        }
        if (this.viewer != null) {
            const handleSceneReady = async () => {
                const layoutEl = this.getLayoutElement();
                await layoutEl.attemptComputeCellHeight();
            };
            this.viewer.addEventListener('sceneReady', handleSceneReady);
            this.stateMap.viewerDisposable = (_c = this.controller) === null || _c === void 0 ? void 0 : _c.connectToViewer(this.viewer);
            this.stateMap.viewerSceneReadyDisposable = {
                dispose: () => { var _a; return (_a = this.viewer) === null || _a === void 0 ? void 0 : _a.removeEventListener('sceneReady', handleSceneReady); },
            };
        }
        else {
            this.attemptingRetry = false;
        }
    }
    scheduleClearUnusedData() {
        if (this.stateMap.idleCallbackId != null) {
            window.cancelIdleCallback(this.stateMap.idleCallbackId);
        }
        this.stateMap.idleCallbackId = window.requestIdleCallback((foo) => {
            var _a, _b, _c;
            const remaining = (_a = foo.timeRemaining) === null || _a === void 0 ? void 0 : _a.call(foo);
            if (remaining == null || remaining >= MIN_CLEAR_UNUSED_DATA_MS) {
                const layoutEl = this.getLayoutElement();
                const startIndex = layoutEl.viewportStartIndex;
                const endIndex = layoutEl.viewportEndIndex;
                const [start, end] = ((_b = this.controller) === null || _b === void 0 ? void 0 : _b.getPageIndexesForRange(startIndex, endIndex)) || [];
                if (start != null && end != null) {
                    (_c = this.controller) === null || _c === void 0 ? void 0 : _c.invalidatePagesOutsideRange(start, end, 50);
                }
            }
            else {
                this.scheduleClearUnusedData();
            }
        });
    }
    handleControllerStateChange(state) {
        this.showLoader = !!state.shouldShowLoading;
        this.showEmptyResults = !!state.shouldShowEmptyResults;
        this.rows = state.rows;
        this.totalRows = state.totalRows;
        this.hasPartialFilterResults = !!state.isPartialFilterResponse;
        this.refreshingResults = !!state.isSearching;
        if (state.connection.type === 'failure') {
            this.errorDetails = state.connection.details;
            this.connectionError.emit(state.connection.details);
        }
        else if (state.connection.type === 'disconnected') {
            this.errorDetails = new SceneTreeErrorDetails('DISCONNECTED', SceneTreeErrorCode.DISCONNECTED);
        }
        else {
            this.errorDetails = undefined;
        }
        if (state.connection.type === 'connected' ||
            state.connection.type === 'failure') {
            this.attemptingRetry = false;
        }
    }
    getNodeFromRowOrIndex(rowOrIndex) {
        const row = typeof rowOrIndex === 'number' ? this.rows[rowOrIndex] : rowOrIndex;
        if (row == null) {
            throw new Error(`Cannot perform scene tree operation. Row not found.`);
        }
        return isLoadedRow(row) ? row.node : row;
    }
    async performRowOperation(rowOrIndex, op) {
        const node = this.getNodeFromRowOrIndex(rowOrIndex);
        if (node.id == null) {
            throw new Error(`Cannot perform scene tree operation. ID is undefined.`);
        }
        if (this.viewer == null) {
            throw new Error(`Cannot perform scene tree operation. Cannot get reference to viewer.`);
        }
        await op({ viewer: this.viewer, id: node.id.hex, node });
    }
    async handleSearch(event) {
        var _a, _b, _c, _d;
        const columnsToSearch = this.getMetadataSearchKeys(this.searchOptions);
        const shouldSearchExactMatch = (_b = (_a = this.searchOptions) === null || _a === void 0 ? void 0 : _a.exactMatch) !== null && _b !== void 0 ? _b : this.metadataSearchExactMatch;
        const shouldSearchRemoveHiddenItems = (_d = (_c = this.searchOptions) === null || _c === void 0 ? void 0 : _c.removeHiddenItems) !== null && _d !== void 0 ? _d : false;
        try {
            await this.filterItems(event.detail, {
                columns: columnsToSearch,
                exactMatch: shouldSearchExactMatch,
                removeHiddenItems: shouldSearchRemoveHiddenItems,
            });
        }
        catch (e) {
            console.error('Failed to filter tree with exception: ', e);
        }
    }
    async handleCellLoaded() {
        if (!this.firstCellRendered && this.rows.length > 0) {
            this.firstCellRendered = true;
            this.firstRowRendered.emit();
        }
    }
    getScrollToPosition(index, position) {
        var _a;
        const layoutEl = this.getLayoutElement();
        const constrainedIndex = Math.max(0, Math.min(index, this.totalRows - 1));
        const viewportHeight = (_a = layoutEl.layoutHeight) !== null && _a !== void 0 ? _a : 0;
        const rowHeight = layoutEl.rowHeight;
        if (position === 'start') {
            return constrainedIndex * rowHeight;
        }
        else if (position === 'middle') {
            const rowCenterY = constrainedIndex * rowHeight + rowHeight / 2;
            return rowCenterY - viewportHeight / 2;
        }
        else {
            const rowBottomY = constrainedIndex * rowHeight + rowHeight;
            return rowBottomY - viewportHeight;
        }
    }
    getConfig() {
        return parseAndValidateConfig(this.configEnv, this.config);
    }
    ensureLayoutDefined() {
        let layout = this.el.querySelector('vertex-scene-tree-table-layout');
        if (layout == null) {
            layout = document.createElement('vertex-scene-tree-table-layout');
            layout.innerHTML = `
      <vertex-scene-tree-table-column>
        <template>
        <vertex-scene-tree-table-cell prop:value="{{row.node.name}}" expand-toggle isolate-button visibility-toggle></vertex-scene-tree-table-cell>
        </template>
      </vertex-scene-tree-table-column>
      `;
            this.el.appendChild(layout);
        }
        this.stateMap.layoutEl = layout;
    }
    updateLayoutElement() {
        const layout = this.stateMap.layoutEl;
        if (layout != null) {
            layout.rows = this.rows;
            layout.tree = this.el;
            layout.totalRows = this.totalRows;
            layout.controller = this.controller;
            layout.rowData = this.rowData;
        }
        else if (!this.stateMap.componentLoaded && this.totalRows > 0) {
            console.debug('Scene tree has rows, but the component has not yet rendered');
        }
    }
    getLayoutElement() {
        if (this.stateMap.layoutEl != null) {
            return this.stateMap.layoutEl;
        }
        else {
            throw new Error('Layout element is undefined');
        }
    }
    getMetadataSearchKeys(options) {
        var _a, _b;
        const metadataSearchKeys = (_a = options === null || options === void 0 ? void 0 : options.metadataSearchKeys) !== null && _a !== void 0 ? _a : this.metadataSearchKeys;
        const shouldSearchExactMatch = (_b = options === null || options === void 0 ? void 0 : options.exactMatch) !== null && _b !== void 0 ? _b : this.metadataSearchExactMatch;
        if (shouldSearchExactMatch) {
            // If we're performing an exact match search, we want to include the searched
            // metadata keys if provided. In the case that none have been provided, simply
            // fall back the item name.
            const definedMetadataKeys = metadataSearchKeys.length > 0
                ? metadataSearchKeys
                : [SCENE_ITEM_NAME_METADATA_KEY];
            return definedMetadataKeys;
        }
        else if (metadataSearchKeys.length > 0) {
            // If we're not performing an exact match search, and a set of metadata keys
            // to search against have been provided, we want to include those in the request.
            return metadataSearchKeys;
        }
        // If we're not performing an exact match search, and we have no provided metadata
        // search keys, we can perform a name-only search, and omit the array of keys.
        return undefined;
    }
    get el() { return getElement(this); }
    static get watchers() { return {
        "viewer": [{
                "handleViewerChanged": 0
            }],
        "controller": [{
                "handleControllerChanged": 0
            }],
        "metadataKeys": [{
                "handleMetadataKeysChanged": 0
            }]
    }; }
};
SceneTree.style = sceneTreeCss();

export { SceneTree as vertex_scene_tree };
//# sourceMappingURL=vertex-scene-tree.entry.js.map

//# sourceMappingURL=vertex-scene-tree.entry.js.map