handsontable多选下拉框编辑器扩展

一、效果截图

 

 二、文件引用

多选下拉框扩展自handsontable的BaseEditor。

多选下拉框组件由两个文件构成,

  • 一个下拉框样式表MultiSelect.css
  • 一个组件实现脚本MultiSelect.js

使用时引用这两个文件即可,当然,要先在你的页面中引用handsontable的核心js文件,再引用我这两个文件。

其中js以模块类型引用(type="module"

<link href="../../Scripts/MultiSelect.css" rel="stylesheet" />
<script type="module" src="../../Scripts/MultiSelect.js"></script>

三、使用范例

var columns = [
               { data: 'field1', title: 'title1',  120, className: 'htCenter htMiddle', editor: 'MultiSelect', selectOptions: ['1#', '2#', '3#', '4#', '5#', '6#', '7#', '8#'] }
            ];

四、源代码

1.MultiSelect.js

/// <reference path="handsontable.full.min.js" />

//封闭在IIFE中
(Handsontable => {
    class MultiSelectEditor extends Handsontable.editors.BaseEditor {

        // ...rest of the editor code
        /**
        * Initializes editor instance, DOM Element and mount hooks.
        * 初始化编辑器实例,dom元素和挂载钩子函数
        */
        init() {
            // Create detached node, add CSS class and make sure its not visible
            //增加触发节点,增加class类并确保节点隐藏隐藏
            var MultiSelectDomContainer = document.createElement("div");
            MultiSelectDomContainer.style.paddingTop = "30px";
            this.select = MultiSelectDomContainer;
            Handsontable.dom.addClass(this.select, 'htMultiSelectEditor');
            this.select.style.display = 'none';

            // Attach node to DOM, by appending it to the container holding the table
            this.hot.rootElement.appendChild(this.select);
        }

        //编辑器的值加载到单元格
        getValue() {
            var selects = "";
            var childs = this.select.children;
            for (var i = 0; i < childs.length; i++) {
                if (childs[i].children[0].checked == true) {
                    selects += "["+childs[i].children[1].innerHTML+"]";
                }
            }
            return selects;
            return this.select;
            return "";
        }

        //单元格的值加载到编辑器
        setValue(value) {
            var selectCount = 0;
            var selects = value.split("][");
            var childs = this.select.children;
            for (var i = 0; i < selects.length; i++) {
                selects[i]=selects[i].replace("[", "").replace("]", "");

                for (var j = 0; j < childs.length; j++) {
                    if (childs[j].value == selects[i]) {
                        selectCount += 1;
                        childs[j].children[0].checked = true;
                    }
                }
            }
            //this.select.style.paddingTop = selectCount*30+"px";
        }

        //打开编辑器
        open() {
            this._opened = true;
            this.refreshDimensions();
            this.select.style.display = '';
        }

        //编辑器样式计算
        refreshDimensions() {
            this.TD = this.getEditedCell();

            // TD is outside of the viewport.
            if (!this.TD) {
                this.close();

                return;
            }
            //调整弹出框位置,使其位于单元格下面
            this.select.style.paddingTop = this.TD.clientHeight + "px";

            const { wtOverlays } = this.hot.view.wt;
            const currentOffset = Handsontable.dom.offset(this.TD);
            const containerOffset = Handsontable.dom.offset(this.hot.rootElement);
            const scrollableContainer = wtOverlays.scrollableElement;
            const editorSection = this.checkEditorSection();
            let width = Handsontable.dom.outerWidth(this.TD) + 1;
            let height = Handsontable.dom.outerHeight(this.TD) + 1;
            let editTop = currentOffset.top - containerOffset.top - 1 - (scrollableContainer.scrollTop || 0);
            let editLeft = currentOffset.left - containerOffset.left - 1 - (scrollableContainer.scrollLeft || 0);
            let cssTransformOffset;

            switch (editorSection) {
                case 'top':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topOverlay.clone.wtTable.holder.parentNode);
                    break;
                case 'left':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.leftOverlay.clone.wtTable.holder.parentNode);
                    break;
                case 'top-left-corner':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topLeftCornerOverlay.clone.wtTable.holder.parentNode);
                    break;
                case 'bottom-left-corner':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomLeftCornerOverlay.clone.wtTable.holder.parentNode);
                    break;
                case 'bottom':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomOverlay.clone.wtTable.holder.parentNode);
                    break;
                default:
                    break;
            }

            if (this.hot.getSelectedLast()[0] === 0) {
                editTop += 1;
            }
            if (this.hot.getSelectedLast()[1] === 0) {
                editLeft += 1;
            }

            const selectStyle = this.select.style;

            if (cssTransformOffset && cssTransformOffset !== -1) {
                selectStyle[cssTransformOffset[0]] = cssTransformOffset[1];
            } else {
                Handsontable.dom.resetCssTransform(this.select);
            }

            const cellComputedStyle = Handsontable.dom.getComputedStyle(this.TD, this.hot.rootWindow);

            if (parseInt(cellComputedStyle.borderTopWidth, 10) > 0) {
                height -= 1;
            }
            if (parseInt(cellComputedStyle.borderLeftWidth, 10) > 0) {
                width -= 1;
            }

            selectStyle.height = `${height}px`;
            selectStyle.minWidth = `${width}px`;
            selectStyle.top = `${editTop}px`;
            selectStyle.left = `${editLeft}px`;
            selectStyle.margin = '0px';
        }

        //获取当前单元格
        getEditedCell() {
            const { wtOverlays } = this.hot.view.wt;
            const editorSection = this.checkEditorSection();
            let editedCell;

            switch (editorSection) {
                case 'top':
                    editedCell = wtOverlays.topOverlay.clone.wtTable.getCell({
                        row: this.row,
                        col: this.col
                    });
                    this.select.style.zIndex = 101;
                    break;
                case 'corner':
                    editedCell = wtOverlays.topLeftCornerOverlay.clone.wtTable.getCell({
                        row: this.row,
                        col: this.col
                    });
                    this.select.style.zIndex = 103;
                    break;
                case 'left':
                    editedCell = wtOverlays.leftOverlay.clone.wtTable.getCell({
                        row: this.row,
                        col: this.col
                    });
                    this.select.style.zIndex = 102;
                    break;
                default:
                    editedCell = this.hot.getCell(this.row, this.col);
                    this.select.style.zIndex = '';
                    break;
            }

            return editedCell < 0 ? void 0 : editedCell;
        }

        focus() {
            this.select.focus();
        }

        close() {
            this._opened = false;
            this.select.style.display = 'none';
        }

        //读取选项配置到编辑器
        prepare(row, col, prop, td, originalValue, cellProperties) {
            // Remember to invoke parent's method
            super.prepare(row, col, prop, td, originalValue, cellProperties);
            const selectOptions = this.cellProperties.selectOptions;
            let options;

            if (typeof selectOptions === 'function') {
                options = this.prepareOptions(selectOptions(this.row, this.col, this.prop));
            } else {
                options = this.prepareOptions(selectOptions);
            }

            Handsontable.dom.empty(this.select);

            Handsontable.helper.objectEach(options, (value, key) => {
                const optionElement = this.hot.rootDocument.createElement('div');

                const checkbox = this.hot.rootDocument.createElement('input');
                checkbox.type = "checkbox";
                optionElement.appendChild(checkbox);

                var textSpan = this.hot.rootDocument.createElement('span');
                textSpan.innerHTML = value;
                optionElement.appendChild(textSpan);

                optionElement.value = key;

                Handsontable.dom.addClass(optionElement, 'MultiSelectOption');

                //Handsontable.dom.fastInnerHTML(optionElement, value);
                this.select.appendChild(optionElement);
            });
        }
        prepareOptions(optionsToPrepare) {
            let preparedOptions = {};

            if (Array.isArray(optionsToPrepare)) {
                for (let i = 0, len = optionsToPrepare.length; i < len; i++) {
                    preparedOptions[optionsToPrepare[i]] = optionsToPrepare[i];
                }

            } else if (typeof optionsToPrepare === 'object') {
                preparedOptions = optionsToPrepare;
            }

            return preparedOptions;
        }
    }


    // Put editor in dedicated namespace
    //将编辑器添加到专用命名空间
    Handsontable.editors.MultiSelectEditor = MultiSelectEditor;
    // Register alias
    //编辑器注册别名
    Handsontable.editors.registerEditor('MultiSelect', MultiSelectEditor);

})(Handsontable);
MultiSelect.js

2.MultiSelect.css

.htMultiSelectEditor {
    /*
  * This hack enables to change <select> dimensions in WebKit browsers
  */
    -webkit-appearance: menulist-button !important;
    position: absolute;
    width: auto;
}
.MultiSelectOption:hover{
    background-color:antiquewhite;
    border:1px solid gray;
}
MultiSelect.css

 五、更新

日期:2022-01-10

问题

  • 修改样式
  • 解决在表格底部,弹出的下拉框被遮挡的问题

1.MutiSelect.js

/// <reference path="handsontable.full.min.js" />

//封闭在IIFE中
(Handsontable => {
    class MultiSelectEditor extends Handsontable.editors.BaseEditor {

        // ...rest of the editor code
        /**
        * Initializes editor instance, DOM Element and mount hooks.
        * 初始化编辑器实例,dom元素和挂载钩子函数
        */
        init() {
            // Create detached node, add CSS class and make sure its not visible
            //增加触发节点,增加class类并确保节点隐藏隐藏
            var MultiSelectDomContainer = document.createElement("div");
            MultiSelectDomContainer.style.paddingTop = "30px";
            this.select = MultiSelectDomContainer;
            Handsontable.dom.addClass(this.select, 'htMultiSelectEditor');
            this.select.style.display = 'none';

            // Attach node to DOM, by appending it to the container holding the table
            //this.hot.rootElement.appendChild(this.select);//当前单元格的下拉框子节点(会被遮挡)
            this.hot.rootDocument.body.appendChild(this.select);//文档的下拉框子节点(不会被遮挡)
            this.select.addEventListener("mousedown", (
                function (e) {
                    return e.stopPropagation();
                }));//阻止单光获得值在下拉框元素上面,editermanager的点击事件执行(关闭下拉框),让单元格能获得值
            //document.body.appendChild(this.select);
        }

        //编辑器的值加载到单元格
        getValue() {
            var selects = "";
            var childs = this.select.children;
            for (var i = 0; i < childs.length; i++) {
                if (childs[i].children[0].checked == true) {
                    selects += "["+childs[i].children[1].innerHTML+"]";
                }
            }
            return selects;
            return this.select;
            return "";
        }

        //单元格的值加载到编辑器
        setValue(value) {
            var selectCount = 0;
            var selects = value.split("][");
            var childs = this.select.children;
            for (var i = 0; i < selects.length; i++) {
                selects[i]=selects[i].replace("[", "").replace("]", "");

                for (var j = 0; j < childs.length; j++) {
                    if (childs[j].value == selects[i]) {
                        selectCount += 1;
                        childs[j].children[0].checked = true;
                    }
                }
            }
            //this.select.style.paddingTop = selectCount*30+"px";
        }

        //打开编辑器
        open() {
            this._opened = true;
            this.refreshDimensions();
            this.select.style.display = '';
        }

        //编辑器样式计算
        refreshDimensions() {
            this.TD = this.getEditedCell();

            // TD is outside of the viewport.
            if (!this.TD) {
                this.close();

                return;
            }
            //调整弹出框位置,使其位于单元格下面
            this.select.style.paddingTop = this.TD.clientHeight + "px";

            const { wtOverlays } = this.hot.view.wt;
            const currentOffset = Handsontable.dom.offset(this.TD);
            const containerOffset = Handsontable.dom.offset(this.hot.rootElement);
            const scrollableContainer = wtOverlays.scrollableElement;
            const editorSection = this.checkEditorSection();
            let width = Handsontable.dom.outerWidth(this.TD) + 1;
            let height = Handsontable.dom.outerHeight(this.TD) + 1;
            let editTop = currentOffset.top - containerOffset.top - 1 - (scrollableContainer.scrollTop || 0);
            let editLeft = currentOffset.left - containerOffset.left - 0 - (scrollableContainer.scrollLeft || 0);
            let cssTransformOffset;

            switch (editorSection) {
                case 'top':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topOverlay.clone.wtTable.holder.parentNode);
                    break;
                case 'left':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.leftOverlay.clone.wtTable.holder.parentNode);
                    break;
                case 'top-left-corner':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.topLeftCornerOverlay.clone.wtTable.holder.parentNode);
                    break;
                case 'bottom-left-corner':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomLeftCornerOverlay.clone.wtTable.holder.parentNode);
                    break;
                case 'bottom':
                    cssTransformOffset = Handsontable.dom.getCssTransform(wtOverlays.bottomOverlay.clone.wtTable.holder.parentNode);
                    break;
                default:
                    break;
            }

            if (this.hot.getSelectedLast()[0] === 0) {
                editTop += 1;
            }
            if (this.hot.getSelectedLast()[1] === 0) {
                editLeft += 1;
            }

            const selectStyle = this.select.style;

            if (cssTransformOffset && cssTransformOffset !== -1) {
                selectStyle[cssTransformOffset[0]] = cssTransformOffset[1];
            } else {
                Handsontable.dom.resetCssTransform(this.select);
            }

            const cellComputedStyle = Handsontable.dom.getComputedStyle(this.TD, this.hot.rootWindow);

            if (parseInt(cellComputedStyle.borderTopWidth, 10) > 0) {
                height -= 1;
            }
            if (parseInt(cellComputedStyle.borderLeftWidth, 10) > 0) {
                width -= 1;
            }

            //selectStyle.height = `${height}px`;
            selectStyle.height = "auto";
            selectStyle.minWidth = `${width}px`;
            selectStyle.top = `${editTop}px`;
            selectStyle.left = `${editLeft}px`;
            selectStyle.margin = '0px';
        }

        //获取当前单元格
        getEditedCell() {
            const { wtOverlays } = this.hot.view.wt;
            const editorSection = this.checkEditorSection();
            let editedCell;

            switch (editorSection) {
                case 'top':
                    editedCell = wtOverlays.topOverlay.clone.wtTable.getCell({
                        row: this.row,
                        col: this.col
                    });
                    this.select.style.zIndex = 101;
                    break;
                case 'corner':
                    editedCell = wtOverlays.topLeftCornerOverlay.clone.wtTable.getCell({
                        row: this.row,
                        col: this.col
                    });
                    this.select.style.zIndex = 103;
                    break;
                case 'left':
                    editedCell = wtOverlays.leftOverlay.clone.wtTable.getCell({
                        row: this.row,
                        col: this.col
                    });
                    this.select.style.zIndex = 102;
                    break;
                default:
                    editedCell = this.hot.getCell(this.row, this.col);
                    this.select.style.zIndex = '';
                    break;
            }

            return editedCell < 0 ? void 0 : editedCell;
        }

        focus() {
            this.select.focus();
        }

        close() {
            this._opened = false;
            this.select.style.display = 'none';
        }

        //读取选项配置到编辑器
        prepare(row, col, prop, td, originalValue, cellProperties) {
            // Remember to invoke parent's method
            super.prepare(row, col, prop, td, originalValue, cellProperties);
            const selectOptions = this.cellProperties.selectOptions;
            let options;

            if (typeof selectOptions === 'function') {
                options = this.prepareOptions(selectOptions(this.row, this.col, this.prop));
            } else {
                options = this.prepareOptions(selectOptions);
            }

            Handsontable.dom.empty(this.select);

            Handsontable.helper.objectEach(options, (value, key) => {
                const optionElement = this.hot.rootDocument.createElement('div');

                const checkbox = this.hot.rootDocument.createElement('input');
                checkbox.type = "checkbox";
                optionElement.appendChild(checkbox);

                var textSpan = this.hot.rootDocument.createElement('span');
                textSpan.innerHTML = value;
                optionElement.appendChild(textSpan);

                optionElement.value = key;

                Handsontable.dom.addClass(optionElement, 'MultiSelectOption');

                //Handsontable.dom.fastInnerHTML(optionElement, value);
                this.select.appendChild(optionElement);
            });
        }
        prepareOptions(optionsToPrepare) {
            let preparedOptions = {};

            if (Array.isArray(optionsToPrepare)) {
                for (let i = 0, len = optionsToPrepare.length; i < len; i++) {
                    preparedOptions[optionsToPrepare[i]] = optionsToPrepare[i];
                }

            } else if (typeof optionsToPrepare === 'object') {
                preparedOptions = optionsToPrepare;
            }

            return preparedOptions;
        }

    }


    // Put editor in dedicated namespace
    //将编辑器添加到专用命名空间
    Handsontable.editors.MultiSelectEditor = MultiSelectEditor;
    // Register alias
    //编辑器注册别名
    Handsontable.editors.registerEditor('MultiSelect', MultiSelectEditor);

})(Handsontable);
MutiSelect.js

2.MutiSelect.css

.htMultiSelectEditor {
    /*
  * This hack enables to change <select> dimensions in WebKit browsers
  */
    -webkit-appearance: menulist-button !important;
    position: absolute;
     auto;
    height:100px;
    z-index:9999;
    background-color:white;
    border:1px solid gray;
}
.MultiSelectOption:hover{
    background-color:antiquewhite;
}
MultiSelect.css
原文地址:https://www.cnblogs.com/ggtc/p/15584200.html