"use strict";

Object.defineProperty(exports, "__esModule", {
    value: !0
}), exports.ValueFilter = void 0;

const vutils_1 = require("@visactor/vutils"), types_1 = require("./types"), styles_1 = require("./styles");

class ValueFilter {
    constructor(table, filterStateManager, pluginOptions) {
        this.uniqueKeys = new Map, this.displayToRawValueMap = new Map, this.valueFilterOptionList = new Map, 
        this.table = table, this.filterStateManager = filterStateManager, this.pluginOptions = pluginOptions, 
        this.styles = pluginOptions.styles, this.filterStateManager.subscribe((state => {
            const filterState = state.filters.get(this.selectedField);
            filterState && "byValue" === filterState.type && this.updateUI(filterState);
        }));
    }
    updateUI(filterState) {
        this.syncCheckboxesWithFilterState(filterState), this.syncSelectAllWithFilterState(filterState);
    }
    setSelectedField(fieldId) {
        this.selectedField = fieldId, this.collectUniqueColumnValues(fieldId);
    }
    collectUniqueColumnValues(fieldId, forceCollect = !1) {
        var _a, _b, _c;
        if (this.uniqueKeys.has(fieldId) && !forceCollect) return;
        const displayToRawMap = new Map;
        let targetCol = -1;
        for (let col = 0; col < this.table.colCount; col++) {
            for (let row = this.table.columnHeaderLevelCount; row < this.table.rowCount; row++) if (!this.table.internalProps.layoutMap.isHeader(col, row)) {
                const bodyInfo = this.table.internalProps.layoutMap.getBody(col, row);
                if (bodyInfo && bodyInfo.field === fieldId) {
                    targetCol = col;
                    break;
                }
            }
            if (-1 !== targetCol) break;
        }
        const records = this.table.internalProps.records, recordsLength = records.length;
        for (let i = 0; i < recordsLength; i++) {
            let rawValue, displayValue;
            if (-1 !== targetCol) {
                const row = this.table.columnHeaderLevelCount + i, currentRecord = records[i];
                if (null == currentRecord) continue;
                rawValue = currentRecord[fieldId];
                const bodyInfo = this.table.internalProps.layoutMap.getBody(targetCol, row);
                displayValue = bodyInfo && "fieldFormat" in bodyInfo && bodyInfo.fieldFormat && "function" == typeof bodyInfo.fieldFormat ? bodyInfo.fieldFormat({
                    [fieldId]: rawValue
                }) : rawValue;
            } else rawValue = records[i][fieldId], displayValue = rawValue;
            null == rawValue || displayToRawMap.has(displayValue) || (displayToRawMap.set(displayValue, rawValue), 
            this.displayToRawValueMap.has(fieldId) && !(null === (_a = this.displayToRawValueMap.get(fieldId)) || void 0 === _a ? void 0 : _a.has(displayValue)) && (null === (_c = null === (_b = this.filterStateManager.getFilterState(fieldId)) || void 0 === _b ? void 0 : _b.values) || void 0 === _c ? void 0 : _c.length) > 0 && this.filterStateManager.getFilterState(fieldId).values.push(rawValue));
        }
        this.displayToRawValueMap.set(fieldId, displayToRawMap);
        const uniqueValues = Array.from(displayToRawMap.entries()).map((([displayValue, rawValue]) => ({
            value: displayValue,
            count: 0,
            rawValue: rawValue
        })));
        this.uniqueKeys.set(fieldId, uniqueValues);
    }
    updateCandidateCounts(fieldId) {
        const uniqueValues = this.uniqueKeys.get(fieldId);
        if (!uniqueValues) return;
        const filter = this.filterStateManager.getFilterState(fieldId), dataSource = (null == filter ? void 0 : filter.enable) ? this.table.internalProps.records : this.table.internalProps.dataSource;
        let targetCol = -1;
        for (let col = 0; col < this.table.colCount; col++) {
            for (let row = this.table.columnHeaderLevelCount; row < this.table.rowCount; row++) if (!this.table.internalProps.layoutMap.isHeader(col, row)) {
                const bodyInfo = this.table.internalProps.layoutMap.getBody(col, row);
                if (bodyInfo && bodyInfo.field === fieldId) {
                    targetCol = col;
                    break;
                }
            }
            if (-1 !== targetCol) break;
        }
        const dataLength = dataSource.length, countMap = new Map;
        for (let i = 0; i < dataLength; i++) {
            let displayValue;
            if (-1 !== targetCol) {
                const row = this.table.columnHeaderLevelCount + i;
                row < this.table.rowCount && (displayValue = this.table.getCellValue(targetCol, row));
            } else displayValue = this.table.getFieldData(String(fieldId), -1 !== targetCol ? targetCol : 0, this.table.columnHeaderLevelCount + i);
            null != displayValue && countMap.set(displayValue, (countMap.get(displayValue) || 0) + 1);
        }
        uniqueValues.forEach((item => {
            item.count = countMap.get(item.value) || 0;
        }));
    }
    onValueSelect(fieldId, displayValue, selected) {
        const displayToRawMap = this.displayToRawValueMap.get(fieldId), rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue, filter = this.filterStateManager.getFilterState(fieldId);
        let updatedValues;
        filter ? (updatedValues = selected ? [ ...filter.values || [], rawValue ] : (filter.values || []).filter((v => v !== rawValue)), 
        this.filterStateManager.dispatch({
            type: types_1.FilterActionType.UPDATE_FILTER,
            payload: {
                field: fieldId,
                values: updatedValues
            }
        })) : (updatedValues = selected ? [ rawValue ] : [], this.filterStateManager.dispatch({
            type: types_1.FilterActionType.ADD_FILTER,
            payload: {
                field: fieldId,
                type: "byValue",
                values: updatedValues
            }
        }));
    }
    isValueVisible(displayValue, keyword) {
        if (!keyword) return !0;
        const filterKeywords = keyword.toUpperCase().split(" ").filter((s => s)), txtValue = String(displayValue).toUpperCase();
        return filterKeywords.some((keyword => txtValue.includes(keyword)));
    }
    toggleSelectAll(fieldId, selected) {
        const currentKeyword = this.filterStateManager.getCurrentSearchKeyword(fieldId), stableCandidates = this.filterStateManager.getStableCandidateValues(fieldId), displayToRawMap = this.displayToRawValueMap.get(fieldId), visibleRawValues = stableCandidates.filter((candidate => this.isValueVisible(candidate.value, currentKeyword))).map((candidate => displayToRawMap ? displayToRawMap.get(candidate.value) : candidate.value)), filter = this.filterStateManager.getFilterState(fieldId), currentValues = new Set((null == filter ? void 0 : filter.values) || []);
        let updatedValues;
        updatedValues = selected ? Array.from(new Set([ ...currentValues, ...visibleRawValues ])) : Array.from(currentValues).filter((value => !visibleRawValues.includes(value))), 
        filter ? this.filterStateManager.dispatch({
            type: types_1.FilterActionType.UPDATE_FILTER,
            payload: {
                field: fieldId,
                values: updatedValues
            }
        }) : this.filterStateManager.dispatch({
            type: types_1.FilterActionType.ADD_FILTER,
            payload: {
                field: fieldId,
                type: "byValue",
                values: updatedValues,
                enable: !0
            }
        });
    }
    onSearch(fieldId, value) {
        this.filterStateManager.updateSearchKeyword(fieldId, value);
        const items = this.valueFilterOptionList.get(fieldId), filterKeywords = value.toUpperCase().split(" ").filter((s => s));
        for (const item of items) {
            const txtValue = item.id.toUpperCase() || "", match = filterKeywords.some((keyword => txtValue.includes(keyword))), isVisible = 0 === filterKeywords.length || match;
            item.itemContainer.style.display = isVisible ? "flex" : "none";
        }
    }
    initFilterStateFromTableData(fieldId) {
        var _a, _b, _c, _d;
        const filter = this.filterStateManager.getFilterState(fieldId), isEnable = null == filter ? void 0 : filter.enable, syncCheckboxCheckedState = null === (_b = null === (_a = this.pluginOptions) || void 0 === _a ? void 0 : _a.syncCheckboxCheckedState) || void 0 === _b || _b;
        if (isEnable) {
            const selectedRawValues = new Set, displayToRawMap = this.displayToRawValueMap.get(fieldId);
            let targetCol = -1;
            for (let col = 0; col < this.table.colCount; col++) {
                for (let row = this.table.columnHeaderLevelCount; row < this.table.rowCount; row++) if (!this.table.internalProps.layoutMap.isHeader(col, row)) {
                    const bodyInfo = this.table.internalProps.layoutMap.getBody(col, row);
                    if (bodyInfo && bodyInfo.field === fieldId) {
                        targetCol = col;
                        break;
                    }
                }
                if (-1 !== targetCol) break;
            }
            const currentLength = this.table.internalProps.dataSource.length;
            for (let i = 0; i < currentLength; i++) {
                let displayValue, rawValue;
                if (-1 !== targetCol) {
                    const row = this.table.columnHeaderLevelCount + i;
                    row < this.table.rowCount && (displayValue = this.table.getCellValue(targetCol, row), 
                    rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue);
                } else displayValue = this.table.getFieldData(String(fieldId), -1 !== targetCol ? targetCol : 0, this.table.columnHeaderLevelCount + i), 
                rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
                null != rawValue && selectedRawValues.add(rawValue);
            }
            !(0, vutils_1.arrayEqual)(filter.values, Array.from(selectedRawValues)) && syncCheckboxCheckedState && this.filterStateManager.dispatch({
                type: types_1.FilterActionType.UPDATE_FILTER,
                payload: {
                    field: fieldId,
                    values: Array.from(selectedRawValues)
                }
            });
        } else {
            const availableRawValues = (null === (_d = null === (_c = this.uniqueKeys.get(fieldId)) || void 0 === _c ? void 0 : _c.filter((item => !syncCheckboxCheckedState || item.count > 0))) || void 0 === _d ? void 0 : _d.map((item => item.rawValue)).filter((v => null != v))) || [];
            this.filterStateManager.dispatch({
                type: types_1.FilterActionType.ADD_FILTER,
                payload: {
                    field: fieldId,
                    type: "byValue",
                    values: availableRawValues,
                    enable: !1
                }
            });
        }
    }
    syncCheckboxesWithFilterState(filter) {
        if (!filter) return;
        const selectedRawValues = filter.values || [], displayToRawMap = this.displayToRawValueMap.get(filter.field), optionDomList = this.valueFilterOptionList.get(filter.field);
        null == optionDomList || optionDomList.forEach((optionDom => {
            const displayValue = optionDom.originalValue, rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
            optionDom.checkbox.checked = selectedRawValues.some((v => v === rawValue));
        }));
    }
    syncSelectAllWithFilterState(filter) {
        var _a;
        if (!filter || !filter.values) return this.selectAllCheckbox.checked = !1, void (this.selectAllCheckbox.indeterminate = !1);
        const uniqueValuesCount = (null === (_a = this.uniqueKeys.get(filter.field)) || void 0 === _a ? void 0 : _a.length) || 0;
        0 === uniqueValuesCount || 0 === filter.values.length ? (this.selectAllCheckbox.checked = !1, 
        this.selectAllCheckbox.indeterminate = !1) : filter.values.length === uniqueValuesCount ? (this.selectAllCheckbox.checked = !0, 
        this.selectAllCheckbox.indeterminate = !1) : (this.selectAllCheckbox.checked = !1, 
        this.selectAllCheckbox.indeterminate = !0);
    }
    applyFilter(fieldId = this.selectedField) {
        var _a;
        const allSelectedValues = this.filterStateManager.getFilterState(fieldId).values;
        allSelectedValues.length >= 0 && allSelectedValues.length < (null === (_a = this.uniqueKeys.get(fieldId)) || void 0 === _a ? void 0 : _a.length) ? this.filterStateManager.dispatch({
            type: types_1.FilterActionType.APPLY_FILTERS,
            payload: {
                field: fieldId,
                type: "byValue",
                values: allSelectedValues,
                enable: !0
            }
        }) : this.filterStateManager.dispatch({
            type: types_1.FilterActionType.REMOVE_FILTER,
            payload: {
                field: fieldId
            }
        });
    }
    clearFilter(fieldId) {
        this.filterStateManager.dispatch({
            type: types_1.FilterActionType.REMOVE_FILTER,
            payload: {
                field: fieldId
            }
        }), this.hide();
    }
    render(container) {
        var _a;
        this.filterByValuePanel = document.createElement("div"), (0, styles_1.applyStyles)(this.filterByValuePanel, this.styles.filterPanel), 
        this.searchContainer = document.createElement("div"), (0, styles_1.applyStyles)(this.searchContainer, this.styles.searchContainer), 
        this.filterByValueSearchInput = document.createElement("input"), this.filterByValueSearchInput.type = "text", 
        this.filterByValueSearchInput.placeholder = (null === (_a = this.styles.searchInput) || void 0 === _a ? void 0 : _a.placeholder) || "可使用空格分隔多个关键词", 
        (0, styles_1.applyStyles)(this.filterByValueSearchInput, this.styles.searchInput), 
        this.searchContainer.appendChild(this.filterByValueSearchInput), this.optionsContainer = document.createElement("div"), 
        (0, styles_1.applyStyles)(this.optionsContainer, this.styles.optionsContainer), 
        this.selectAllItemDiv = document.createElement("div"), (0, styles_1.applyStyles)(this.selectAllItemDiv, this.styles.optionItem), 
        this.selectAllLabel = document.createElement("label"), (0, styles_1.applyStyles)(this.selectAllLabel, this.styles.optionLabel), 
        this.selectAllCheckbox = document.createElement("input"), this.selectAllCheckbox.type = "checkbox", 
        this.selectAllCheckbox.checked = !0, (0, styles_1.applyStyles)(this.selectAllCheckbox, this.styles.checkbox), 
        this.selectAllLabel.append(this.selectAllCheckbox, " 全选"), this.selectAllItemDiv.appendChild(this.selectAllLabel), 
        this.filterItemsContainer = document.createElement("div"), this.optionsContainer.append(this.selectAllItemDiv, this.filterItemsContainer), 
        this.filterByValuePanel.append(this.searchContainer, this.optionsContainer), container.appendChild(this.filterByValuePanel), 
        this.bindEventForFilterByValue();
    }
    updatePluginOptions(pluginOptions) {
        this.pluginOptions = pluginOptions, this.styles = pluginOptions.styles, this.updateStyles(pluginOptions.styles);
    }
    updateStyles(styles) {
        var _a;
        (0, styles_1.applyStyles)(this.filterByValuePanel, styles.filterPanel), (0, styles_1.applyStyles)(this.searchContainer, styles.searchContainer), 
        this.filterByValueSearchInput.placeholder = (null === (_a = styles.searchInput) || void 0 === _a ? void 0 : _a.placeholder) || "可使用空格分隔多个关键词", 
        (0, styles_1.applyStyles)(this.filterByValueSearchInput, styles.searchInput), (0, 
        styles_1.applyStyles)(this.optionsContainer, styles.optionsContainer), (0, styles_1.applyStyles)(this.selectAllItemDiv, styles.optionItem), 
        (0, styles_1.applyStyles)(this.selectAllLabel, styles.optionLabel), (0, styles_1.applyStyles)(this.selectAllCheckbox, styles.checkbox);
    }
    renderFilterOptions(field) {
        var _a, _b;
        this.filterItemsContainer.innerHTML = "", this.valueFilterOptionList.delete(field), 
        this.valueFilterOptionList.set(field, []);
        const selectedRawValues = (null === (_a = this.filterStateManager.getFilterState(field)) || void 0 === _a ? void 0 : _a.values) || [], selectedRawValueSet = new Set(selectedRawValues), itemDomList = [];
        null === (_b = this.uniqueKeys.get(field)) || void 0 === _b || _b.forEach((({value: value, count: count, rawValue: rawValue}) => {
            const itemDiv = document.createElement("div");
            (0, styles_1.applyStyles)(itemDiv, this.styles.optionItem), itemDiv.style.display = "flex";
            const label = document.createElement("label");
            (0, styles_1.applyStyles)(label, this.styles.optionLabel);
            const checkbox = document.createElement("input");
            checkbox.type = "checkbox", checkbox.value = String(value), checkbox.checked = selectedRawValueSet.has(rawValue), 
            (0, styles_1.applyStyles)(checkbox, this.styles.checkbox);
            const countSpan = document.createElement("span");
            countSpan.textContent = String(count), (0, styles_1.applyStyles)(countSpan, this.styles.countSpan), 
            label.append(checkbox, ` ${rawValue}`), itemDiv.append(label, countSpan), this.filterItemsContainer.appendChild(itemDiv);
            const itemDom = {
                id: String(value),
                originalValue: value,
                itemContainer: itemDiv,
                checkbox: checkbox,
                countSpan: countSpan
            };
            itemDomList.push(itemDom);
        })), this.valueFilterOptionList.set(field, itemDomList);
    }
    bindEventForFilterByValue() {
        this.filterByValuePanel.addEventListener("keyup", (event => {
            const target = event.target;
            if (target instanceof HTMLInputElement && "text" === target.type) {
                const value = target.value;
                this.onSearch(this.selectedField, value);
            }
        })), this.filterByValuePanel.addEventListener("change", (event => {
            const target = event.target;
            target instanceof HTMLInputElement && "checkbox" === target.type && (target === this.selectAllCheckbox ? this.valueFilterOptionList.get(this.selectedField).forEach((item => item.checkbox.checked = target.checked)) : this.updateCheckboxUI(this.selectedField));
        }));
    }
    updateCheckboxUI(field) {
        var _a, _b;
        const checkedItem = null === (_a = this.valueFilterOptionList.get(field)) || void 0 === _a ? void 0 : _a.filter((item => item.checkbox.checked)), uncheckedItem = null === (_b = this.valueFilterOptionList.get(field)) || void 0 === _b ? void 0 : _b.filter((item => !item.checkbox.checked));
        (0, vutils_1.isValid)(checkedItem) && (0, vutils_1.isValid)(uncheckedItem) && (0 !== checkedItem.length && 0 !== uncheckedItem.length ? this.selectAllCheckbox.indeterminate = !0 : (this.selectAllCheckbox.indeterminate = !1, 
        this.selectAllCheckbox.checked = 0 === uncheckedItem.length));
    }
    updateCheckboxState(field) {
        var _a;
        const originalValues = null === (_a = this.valueFilterOptionList.get(field)) || void 0 === _a ? void 0 : _a.filter((item => item.checkbox.checked)).map((item => item.originalValue)), displayToRawMap = this.displayToRawValueMap.get(field), rawValues = originalValues.map((displayValue => displayToRawMap ? displayToRawMap.get(displayValue) : displayValue));
        this.filterStateManager.dispatch({
            type: types_1.FilterActionType.ADD_FILTER,
            payload: {
                field: field,
                type: "byValue",
                values: rawValues
            }
        });
    }
    show() {
        this.collectUniqueColumnValues(this.selectedField), this.updateCandidateCounts(this.selectedField), 
        this.initFilterStateFromTableData(this.selectedField);
        const uniqueValues = this.uniqueKeys.get(this.selectedField), displayToRawMap = this.displayToRawValueMap.get(this.selectedField);
        uniqueValues && displayToRawMap && this.filterStateManager.initializeFilterMenuState(this.selectedField, uniqueValues, displayToRawMap), 
        this.filterByValueSearchInput && (this.filterByValueSearchInput.value = ""), this.renderFilterOptions(this.selectedField), 
        this.updateCheckboxUI(this.selectedField), this.filterByValuePanel.style.display = "block";
    }
    hide() {
        this.filterByValuePanel.style.display = "none";
    }
    clearSearchInputValue() {
        this.filterByValueSearchInput.value = "";
    }
}

exports.ValueFilter = ValueFilter;
//# sourceMappingURL=value-filter.js.map