/*!
 * Built by Revolist OU ❤️
 */
// filter.panel.tsx
import { h, Host, } from "@stencil/core";
import debounce from "lodash/debounce";
import { AndOrButton, isFilterBtn, ReorderButton, TrashButton } from "./filter.button";
import "../../utils/closest.polifill";
import { getFilterReorderId, moveFilterItem, setFilterReorderData, } from "./filter.reorder";
const defaultType = 'none';
const FILTER_LIST_CLASS = 'multi-filter-list';
const FILTER_LIST_CLASS_ACTION = 'multi-filter-list-action';
const FILTER_ID = 'add-filter';
const VIEWPORT_PADDING = 8;
/**
 * Filter panel for editing filters
 */
/**
 * @internal
 */
export class FilterPanel {
    constructor() {
        this.filterCaptionsInternal = {
            title: 'Filter by',
            ok: 'Close',
            save: 'Save',
            // drops the filter
            reset: 'Reset',
            cancel: 'Cancel',
            add: 'Add condition',
            placeholder: 'Enter value...',
            and: 'and',
            or: 'or',
            filterCondition: 'Filter condition',
            removeFilter: 'Remove filter',
            reorderFilter: 'Reorder filter',
        };
        this.isFilterIdSet = false;
        this.filterId = 0;
        this.currentFilterId = -1;
        this.currentFilterType = defaultType;
        this.filterItems = {};
        this.filterNames = {};
        this.filterEntities = {};
        /**
         * Disables dynamic filtering. A way to apply filters on Save only
         */
        this.disableDynamicFiltering = false;
        /**
         * If true, closes the filter panel when clicking outside
         */
        this.closeOnOutsideClick = true;
        this.debouncedApplyFilter = debounce(() => {
            this.filterChange.emit(this.filterItems);
        }, 400);
    }
    onMouseDown(e) {
        // click on anything then select drops values to default
        if (!this.changes) {
            return;
        }
        const path = e.composedPath();
        const select = document.getElementById(FILTER_ID);
        if (select instanceof HTMLSelectElement) {
            // click on select should be skipped
            if (path.includes(select)) {
                return;
            }
            select.value = defaultType;
        }
        this.currentFilterType = defaultType;
        if (this.changes) {
            this.changes.type = defaultType;
        }
        this.currentFilterId = -1;
        const isOutside = !path.includes(this.element);
        if (e.target instanceof HTMLElement &&
            isOutside &&
            !isFilterBtn(e.target) &&
            this.closeOnOutsideClick) {
            this.changes = undefined;
        }
    }
    async show(newEntity) {
        this.changes = newEntity;
        this.filterItems = (newEntity === null || newEntity === void 0 ? void 0 : newEntity.filterItems) || {};
        if (this.changes) {
            this.changes.type = this.changes.type || defaultType;
        }
    }
    async getChanges() {
        return this.changes;
    }
    componentWillRender() {
        if (!this.isFilterIdSet) {
            this.isFilterIdSet = true;
            const filterItems = Object.keys(this.filterItems);
            for (const prop of filterItems) {
                // we set the proper filterId so there won't be any conflict when removing filters
                this.filterId += this.filterItems[prop].length;
            }
        }
    }
    getFilterItemsList() {
        var _a, _b;
        const prop = (_a = this.changes) === null || _a === void 0 ? void 0 : _a.prop;
        if (typeof prop === 'undefined')
            return '';
        const propFilters = (_b = this.filterItems[prop]) !== null && _b !== void 0 ? _b : [];
        const visibleFilterCount = propFilters.filter(filter => !filter.hidden).length;
        const capts = Object.assign(Object.assign({}, this.filterCaptionsInternal), this.filterCaptions);
        return (h("div", { key: this.filterId }, h("ul", { class: "multi-filter-list-container" }, propFilters.map((filter, index) => {
            let andOrButton;
            if (filter.hidden) {
                return;
            }
            // hide toggle button if there is only one filter and the last one
            if (index !== this.filterItems[prop].length - 1) {
                andOrButton = (h(AndOrButton, { text: filter.relation === 'and' ? capts.and : capts.or, onClick: () => this.toggleFilterAndOr(filter.id) }));
            }
            const extra = this.renderExtra(prop, index);
            const isDragging = this.draggedFilterId === filter.id;
            const isDragOver = this.dragOverFilterId === filter.id && !isDragging;
            const canReorder = visibleFilterCount > 1;
            return (h("li", { key: filter.id, class: FILTER_LIST_CLASS, "aria-label": `${capts.filterCondition} ${index + 1}` }, h("div", { class: {
                    'multi-filter-list-row': true,
                    'filter-row-drop-active': this.draggedFilterId !== undefined && !isDragging,
                    'filter-row-dragging': isDragging,
                    'filter-row-drag-over': isDragOver,
                } }, canReorder ? (h("button", { type: "button", class: "filter-row-drop-target", tabIndex: -1, "aria-label": `${capts.filterCondition} ${index + 1}`, onDragOver: e => this.onFilterDragOver(e, filter.id), onDragLeave: () => this.onFilterDragLeave(filter.id), onDrop: e => this.onFilterDrop(e, prop, filter.id) })) : '', canReorder ? (h(ReorderButton, { ariaLabel: capts.reorderFilter, dragging: isDragging, dragOver: isDragOver, onDragStart: e => this.onFilterDragStart(e, filter.id), onDragEnd: () => this.onFilterDragEnd(), onKeyDown: e => this.onFilterReorderKeyDown(e, prop, filter.id) })) : '', h("div", { class: { 'select-input': true } }, h("select", { class: "select-css select-filter", onChange: e => this.onFilterTypeChange(e, prop, index) }, this.renderSelectOptions(this.filterItems[prop][index].type, true)), extra ? h("div", { class: "filter-extra" }, extra) : ''), h("div", { class: FILTER_LIST_CLASS_ACTION }, andOrButton, h(TrashButton, { ariaLabel: capts.removeFilter, onClick: () => this.onRemoveFilter(filter.id) })))));
        })), propFilters.filter(f => !f.hidden).length > 0 ? h("div", { class: "add-filter-divider" }) : ''));
    }
    componentDidRender() {
        this.syncDialog();
    }
    syncDialog() {
        if (!this.dialog) {
            return;
        }
        if (!this.changes) {
            if (this.dialog.open) {
                this.dialog.close();
            }
            return;
        }
        if (!this.dialog.open) {
            this.dialog.show();
        }
        if (this.changes.autoCorrect !== false) {
            this.autoCorrect(this.dialog);
            requestAnimationFrame(() => this.autoCorrect(this.dialog));
        }
    }
    autoCorrect(el) {
        var _a;
        if (!el || !this.changes) {
            return;
        }
        el.style.maxHeight = '';
        el.style.left = `${this.changes.x}px`;
        el.style.top = `${this.changes.y}px`;
        const pos = el.getBoundingClientRect();
        const anchorTop = (_a = this.changes.anchorY) !== null && _a !== void 0 ? _a : this.changes.y;
        const anchorBottom = this.changes.y;
        const spaceAbove = Math.max(0, anchorTop - VIEWPORT_PADDING);
        const spaceBelow = Math.max(0, window.innerHeight - anchorBottom - VIEWPORT_PADDING);
        const openAbove = pos.height > spaceBelow && spaceAbove > spaceBelow;
        const availableHeight = Math.max(VIEWPORT_PADDING, openAbove ? spaceAbove : spaceBelow);
        el.style.maxHeight = `${availableHeight}px`;
        const adjustedPos = el.getBoundingClientRect();
        const maxLeft = Math.max(VIEWPORT_PADDING, window.innerWidth - adjustedPos.width - VIEWPORT_PADDING);
        const maxTop = Math.max(VIEWPORT_PADDING, window.innerHeight - adjustedPos.height - VIEWPORT_PADDING);
        const left = Math.min(Math.max(VIEWPORT_PADDING, this.changes.x), maxLeft);
        const top = openAbove
            ? Math.min(Math.max(VIEWPORT_PADDING, anchorTop - adjustedPos.height), maxTop)
            : Math.min(Math.max(VIEWPORT_PADDING, anchorBottom), maxTop);
        el.style.left = `${left}px`;
        el.style.top = `${top}px`;
    }
    onFilterTypeChange(e, prop, index) {
        if (!(e.target instanceof HTMLSelectElement)) {
            return;
        }
        this.filterItems[prop][index].type = e.target.value;
        // this re-renders the input to know if we need extra input
        this.filterId++;
        // adding setTimeout will wait for the next tick DOM update then focus on input
        setTimeout(() => {
            const input = document.getElementById('filter-input-' + this.filterItems[prop][index].id);
            if (input instanceof HTMLInputElement) {
                input.focus();
            }
        }, 0);
        if (!this.disableDynamicFiltering) {
            this.debouncedApplyFilter();
        }
    }
    onAddNewFilter(e) {
        const el = e.target;
        this.currentFilterType = el.value;
        this.addNewFilterToProp();
        // reset value after adding new filter
        const select = document.getElementById('add-filter');
        if (select) {
            select.value = defaultType;
            this.currentFilterType = defaultType;
        }
        if (!this.disableDynamicFiltering) {
            this.debouncedApplyFilter();
        }
    }
    addNewFilterToProp() {
        var _a;
        const prop = (_a = this.changes) === null || _a === void 0 ? void 0 : _a.prop;
        if (!(prop || prop === 0))
            return;
        if (!this.filterItems[prop]) {
            this.filterItems[prop] = [];
        }
        if (this.currentFilterType === 'none')
            return;
        this.filterId++;
        this.currentFilterId = this.filterId;
        this.filterItems[prop].push({
            id: this.currentFilterId,
            type: this.currentFilterType,
            value: '',
            relation: 'and',
        });
        // adding setTimeout will wait for the next tick DOM update then focus on input
        setTimeout(() => {
            const input = document.getElementById('filter-input-' + this.currentFilterId);
            if (input)
                input.focus();
        }, 0);
    }
    onSave() {
        this.filterChange.emit(this.filterItems);
    }
    onCancel() {
        this.changes = undefined;
    }
    onReset() {
        var _a;
        this.assertChanges();
        this.resetChange.emit((_a = this.changes) === null || _a === void 0 ? void 0 : _a.prop);
        // this updates the DOM which is used by getFilterItemsList() key
        this.filterId++;
    }
    onRemoveFilter(id) {
        var _a;
        this.assertChanges();
        // this is for reactivity issues for getFilterItemsList()
        this.filterId++;
        const prop = (_a = this.changes) === null || _a === void 0 ? void 0 : _a.prop;
        const items = this.filterItems[prop !== null && prop !== void 0 ? prop : ''];
        if (!items)
            return;
        const index = items.findIndex(d => d.id === id);
        if (index === -1)
            return;
        items.splice(index, 1);
        // let's remove the prop if no more filters so the filter icon will be removed
        if (items.length === 0)
            delete this.filterItems[prop !== null && prop !== void 0 ? prop : ''];
        if (!this.disableDynamicFiltering) {
            this.debouncedApplyFilter();
        }
    }
    onFilterDragStart(e, id) {
        this.draggedFilterId = id;
        setFilterReorderData(e.dataTransfer, id);
    }
    onFilterDragOver(e, id) {
        if (this.draggedFilterId === undefined || this.draggedFilterId === id) {
            return;
        }
        e.preventDefault();
        if (e.dataTransfer) {
            e.dataTransfer.dropEffect = 'move';
        }
        this.dragOverFilterId = id;
    }
    onFilterDragLeave(id) {
        if (this.dragOverFilterId === id) {
            this.dragOverFilterId = undefined;
        }
    }
    onFilterDrop(e, prop, targetId) {
        var _a;
        e.preventDefault();
        const sourceId = (_a = this.draggedFilterId) !== null && _a !== void 0 ? _a : getFilterReorderId(e.dataTransfer);
        this.onFilterDragEnd();
        if (sourceId === undefined) {
            return;
        }
        const items = this.filterItems[prop];
        if (!items) {
            return;
        }
        if (!moveFilterItem(items, sourceId, targetId)) {
            return;
        }
        this.filterId++;
        if (!this.disableDynamicFiltering) {
            this.debouncedApplyFilter();
        }
    }
    onFilterDragEnd() {
        this.draggedFilterId = undefined;
        this.dragOverFilterId = undefined;
    }
    onFilterReorderKeyDown(e, prop, sourceId) {
        let direction = 0;
        if (e.key === 'ArrowUp') {
            direction = -1;
        }
        else if (e.key === 'ArrowDown') {
            direction = 1;
        }
        else {
            return;
        }
        const items = this.filterItems[prop];
        if (!items) {
            return;
        }
        const visibleItems = items.filter(item => !item.hidden);
        const sourceIndex = visibleItems.findIndex(item => item.id === sourceId);
        if (sourceIndex === -1) {
            return;
        }
        e.preventDefault();
        e.stopPropagation();
        const target = visibleItems[sourceIndex + direction];
        if (!target || !moveFilterItem(items, sourceId, target.id)) {
            return;
        }
        this.filterId++;
        if (!this.disableDynamicFiltering) {
            this.debouncedApplyFilter();
        }
    }
    toggleFilterAndOr(id) {
        var _a;
        this.assertChanges();
        // this is for reactivity issues for getFilterItemsList()
        this.filterId++;
        const prop = (_a = this.changes) === null || _a === void 0 ? void 0 : _a.prop;
        const items = this.filterItems[prop !== null && prop !== void 0 ? prop : ''];
        if (!items)
            return;
        const index = items.findIndex(d => d.id === id);
        if (index === -1)
            return;
        items[index].relation = items[index].relation === 'and' ? 'or' : 'and';
        if (!this.disableDynamicFiltering) {
            this.debouncedApplyFilter();
        }
    }
    assertChanges() {
        if (!this.changes) {
            throw new Error('Changes required per edit');
        }
    }
    renderSelectOptions(type, isDefaultTypeRemoved = false) {
        if (!this.changes) {
            return;
        }
        const options = [];
        const prop = this.changes.prop;
        const hidden = new Set();
        Object.keys(this.filterItems).forEach((prop) => {
            const values = this.filterItems[prop];
            values.forEach((filter) => {
                if (filter.hidden) {
                    hidden.add(filter.type);
                }
            });
        });
        if (!isDefaultTypeRemoved) {
            const capts = Object.assign(Object.assign({}, this.filterCaptionsInternal), this.filterCaptions);
            options.push(h("option", { selected: this.currentFilterType === defaultType, value: defaultType }, prop && this.filterItems[prop] && this.filterItems[prop].length > 0
                ? capts.add
                : this.filterNames[defaultType]));
        }
        for (let gIndex in this.changes.filterTypes) {
            const group = this.changes.filterTypes[gIndex].filter(k => !hidden.has(k));
            if (group.length) {
                options.push(...group.map(k => (h("option", { value: k, selected: type === k }, this.filterNames[k]))));
                options.push(h("option", { disabled: true }));
            }
        }
        return options;
    }
    renderExtra(prop, index) {
        const currentFilter = this.filterItems[prop];
        if (!currentFilter)
            return '';
        const applyFilter = (value) => {
            this.filterItems[prop][index].value = value;
            if (!this.disableDynamicFiltering) {
                this.debouncedApplyFilter();
            }
        };
        const focusNext = () => {
            const select = document.getElementById('add-filter');
            if (select) {
                select.value = defaultType;
                this.currentFilterType = defaultType;
                this.addNewFilterToProp();
                select.focus();
            }
        };
        const capts = Object.assign(Object.assign({}, this.filterCaptionsInternal), this.filterCaptions);
        const extra = this.filterEntities[currentFilter[index].type].extra;
        if (typeof extra === 'function') {
            return extra(h, {
                value: currentFilter[index].value,
                filter: currentFilter[index],
                prop,
                index,
                placeholder: capts.placeholder,
                onInput: (value) => {
                    applyFilter(value);
                },
                onFocus: () => {
                    focusNext();
                }
            });
        }
        if (extra !== 'input' && extra !== 'datepicker') {
            return '';
        }
        return (h("input", { id: `filter-input-${currentFilter[index].id}`, placeholder: capts.placeholder, type: extra === 'datepicker' ? 'date' : 'text', value: currentFilter[index].value, onInput: (e) => {
                if (e.target instanceof HTMLInputElement) {
                    applyFilter(e.target.value);
                }
            }, onKeyDown: e => {
                if (e.key.toLowerCase() === 'enter') {
                    const select = document.getElementById('add-filter');
                    if (select) {
                        focusNext();
                    }
                    return;
                }
                // keep event local, don't escalate farther to dom
                e.stopPropagation();
            } }));
    }
    render() {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j;
        const style = {
            left: `${(_b = (_a = this.changes) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0}px`,
            top: `${(_d = (_c = this.changes) === null || _c === void 0 ? void 0 : _c.y) !== null && _d !== void 0 ? _d : 0}px`,
        };
        const capts = Object.assign(Object.assign({}, this.filterCaptionsInternal), this.filterCaptions);
        return (h(Host, { key: '4e969f8b7033363c885c94b1f7770917786669f2' }, h("dialog", { key: '79b2713724684fa99f56621fe43645a0b0ad54bf', class: "filter-panel-dialog", style: style, ref: el => (this.dialog = el), onCancel: e => {
                e.preventDefault();
                this.onCancel();
            } }, this.changes && [
            h("slot", { key: "header-slot", slot: "header" }),
            ((_f = (_e = this.changes).extraContent) === null || _f === void 0 ? void 0 : _f.call(_e, this.changes)) || '',
            ((_g = this.changes) === null || _g === void 0 ? void 0 : _g.hideDefaultFilters) !== true && [
                h("label", { key: "filter-title" }, capts.title),
                h("div", { key: "filter-holder", class: "filter-holder" }, this.getFilterItemsList()),
                h("div", { key: "add-filter", class: "add-filter" }, h("select", { key: 'fc894bdfae1ff35d5c49adc7646dd3182b7bd49e', id: FILTER_ID, class: "select-css", onChange: e => this.onAddNewFilter(e) }, this.renderSelectOptions(this.currentFilterType))),
            ],
            h("slot", { key: "default-slot" }),
            ((_j = (_h = this.changes).extraBottomContent) === null || _j === void 0 ? void 0 : _j.call(_h, this.changes)) || '',
            h("div", { key: "filter-actions", class: "filter-actions" }, this.disableDynamicFiltering && [
                h("button", { key: "save", id: "revo-button-save", "aria-label": "save", class: "revo-button green", onClick: () => this.onSave() }, capts.save),
                h("button", { key: "cancel", id: "revo-button-ok", "aria-label": "ok", class: "revo-button green", onClick: () => this.onCancel() }, capts.cancel),
            ], !this.disableDynamicFiltering && [
                h("button", { key: "ok", id: "revo-button-ok", "aria-label": "ok", class: "revo-button green", onClick: () => this.onCancel() }, capts.ok),
                h("button", { key: "reset", id: "revo-button-reset", "aria-label": "reset", class: "revo-button outline", onClick: () => this.onReset() }, capts.reset),
            ]),
            h("slot", { key: "footer-slot", slot: "footer" }),
        ])));
    }
    static get is() { return "revogr-filter-panel"; }
    static get originalStyleUrls() {
        return {
            "$": ["filter.style.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["filter.style.css"]
        };
    }
    static get properties() {
        return {
            "filterNames": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "Record<string, string>",
                    "resolved": "string",
                    "references": {
                        "Record": {
                            "location": "global",
                            "id": "global::Record"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": ""
                },
                "getter": false,
                "setter": false,
                "defaultValue": "{}"
            },
            "filterEntities": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "Record<string, LogicFunction>",
                    "resolved": "LogicFunction<any, LogicFunctionExtraParam> | string",
                    "references": {
                        "Record": {
                            "location": "global",
                            "id": "global::Record"
                        },
                        "LogicFunction": {
                            "location": "import",
                            "path": "./filter.types",
                            "id": "src/plugins/filter/filter.types.ts::LogicFunction",
                            "referenceLocation": "LogicFunction"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": ""
                },
                "getter": false,
                "setter": false,
                "defaultValue": "{}"
            },
            "filterCaptions": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "Partial<FilterCaptions> | undefined",
                    "resolved": "FilterCaptions | undefined",
                    "references": {
                        "Partial": {
                            "location": "global",
                            "id": "global::Partial"
                        },
                        "FilterCaptions": {
                            "location": "import",
                            "path": "./filter.types",
                            "id": "src/plugins/filter/filter.types.ts::FilterCaptions",
                            "referenceLocation": "FilterCaptions"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": ""
                },
                "getter": false,
                "setter": false
            },
            "disableDynamicFiltering": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Disables dynamic filtering. A way to apply filters on Save only"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "disable-dynamic-filtering",
                "defaultValue": "false"
            },
            "closeOnOutsideClick": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "If true, closes the filter panel when clicking outside"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "close-on-outside-click",
                "defaultValue": "true"
            }
        };
    }
    static get states() {
        return {
            "isFilterIdSet": {},
            "filterId": {},
            "currentFilterId": {},
            "currentFilterType": {},
            "changes": {},
            "filterItems": {},
            "draggedFilterId": {},
            "dragOverFilterId": {}
        };
    }
    static get events() {
        return [{
                "method": "filterChange",
                "name": "filterChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": ""
                },
                "complexType": {
                    "original": "MultiFilterItem",
                    "resolved": "MultiFilterItem",
                    "references": {
                        "MultiFilterItem": {
                            "location": "import",
                            "path": "./filter.types",
                            "id": "src/plugins/filter/filter.types.ts::MultiFilterItem",
                            "referenceLocation": "MultiFilterItem"
                        }
                    }
                }
            }, {
                "method": "resetChange",
                "name": "resetChange",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": ""
                },
                "complexType": {
                    "original": "ColumnProp",
                    "resolved": "number | string",
                    "references": {
                        "ColumnProp": {
                            "location": "import",
                            "path": "@type",
                            "id": "src/types/index.ts::ColumnProp",
                            "referenceLocation": "ColumnProp"
                        }
                    }
                }
            }];
    }
    static get methods() {
        return {
            "show": {
                "complexType": {
                    "signature": "(newEntity?: ShowData) => Promise<void>",
                    "parameters": [{
                            "name": "newEntity",
                            "type": "ShowData | undefined",
                            "docs": ""
                        }],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "ShowData": {
                            "location": "import",
                            "path": "./filter.types",
                            "id": "src/plugins/filter/filter.types.ts::ShowData",
                            "referenceLocation": "ShowData"
                        }
                    },
                    "return": "Promise<void>"
                },
                "docs": {
                    "text": "",
                    "tags": []
                }
            },
            "getChanges": {
                "complexType": {
                    "signature": "() => Promise<ShowData | undefined>",
                    "parameters": [],
                    "references": {
                        "Promise": {
                            "location": "global",
                            "id": "global::Promise"
                        },
                        "ShowData": {
                            "location": "import",
                            "path": "./filter.types",
                            "id": "src/plugins/filter/filter.types.ts::ShowData",
                            "referenceLocation": "ShowData"
                        }
                    },
                    "return": "Promise<ShowData | undefined>"
                },
                "docs": {
                    "text": "",
                    "tags": []
                }
            }
        };
    }
    static get elementRef() { return "element"; }
    static get listeners() {
        return [{
                "name": "mousedown",
                "method": "onMouseDown",
                "target": "document",
                "capture": false,
                "passive": true
            }];
    }
}
