Сортировка drag'n'drop в свойствах товаров

Зачастую в некоторых проектах требуется поддержка сортировки в комбобоксах. Требуется это по разным причинам, в частности, это и хотелки заказчика, и требования сортировки в ручном режиме для каждого товара, и т.п. странные пожелания.



Представляю сообществу решение данной задачи, которую также кинул в pr в репозиторий minishop2:
— создаем директорию assets/components/minishop2/js/mgr/vendor/ и копируем в неё файл Sortable.js( взять можно по ссылке )
— открываем и редактируем файлы core/components/minishop2/controllers/product/create.class.php и core/components/minishop2/controllers/product/update.class.php, вставляем после
$this->addJavascript($assetsUrl . 'js/mgr/minishop2.js');
строку
$this->addJavascript($assetsUrl . 'js/mgr/vendor/Sortable.js');
— редактируем файл assets/components/minishop2/js/mgr/misc/ms2.combo.js, а именно меняем:
miniShop2.combo.Options = function (config) {
    config = config || {};
    Ext.applyIf(config, {
        xtype: 'superboxselect',
        allowBlank: true,
        msgTarget: 'under',
        allowAddNewData: true,
        addNewDataOnBlur: true,
        pinList: false,
        resizable: true,
        name: config.name || 'tags',
        anchor: '100%',
        minChars: 1,
        store: new Ext.data.JsonStore({
            id: (config.name || 'tags') + '-store',
            root: 'results',
            autoLoad: false,
            autoSave: false,
            totalProperty: 'total',
            fields: ['value'],
            url: miniShop2.config['connector_url'],
            baseParams: {
                action: 'mgr/product/getoptions',
                key: config.name
            }
        }),
        mode: 'remote',
        displayField: 'value',
        valueField: 'value',
        triggerAction: 'all',
        extraItemCls: 'x-tag',
        expandBtnCls: 'x-form-trigger',
        clearBtnCls: 'x-form-trigger',
    });
    config.name += '[]';

    Ext.apply(config, {
        listeners: {
            newitem: function(bs, v) {
                bs.addNewItem({value: v});
            },
        },
    });

    miniShop2.combo.Options.superclass.constructor.call(this, config);
};
Ext.extend(miniShop2.combo.Options, Ext.ux.form.SuperBoxSelect);
Ext.reg('minishop2-combo-options', miniShop2.combo.Options);
на следующее:
miniShop2.combo.Options = function (config) {
    config = config || {};
    Ext.applyIf(config, {
        xtype: 'superboxselect',
        allowBlank: true,
        msgTarget: 'under',
        allowAddNewData: true,
        addNewDataOnBlur: true,
        pinList: false,
        resizable: true,
        name: config.name || 'tags',
        anchor: '100%',
        minChars: 1,
        store: new Ext.data.JsonStore({
            id: (config.name || 'tags') + '-store',
            root: 'results',
            autoLoad: false,
            autoSave: false,
            totalProperty: 'total',
            fields: ['value'],
            url: miniShop2.config['connector_url'],
            baseParams: {
                action: 'mgr/product/getoptions',
                key: config.name
            }
        }),
        mode: 'remote',
        displayField: 'value',
        valueField: 'value',
        triggerAction: 'all',
        extraItemCls: 'x-tag',
        expandBtnCls: 'x-form-trigger',
        clearBtnCls: 'x-form-trigger',
    });
    config.name += '[]';

    Ext.apply(config, {
        listeners: {
            afterrender: {
                fn: this.afterrender,
                scope: this
            },
            newitem: {
                fn: this.newitem,
                scope: this
            }
        }
    });

    miniShop2.combo.Options.superclass.constructor.call(this, config);
};

miniShop2.combo.Options = Ext.extend(miniShop2.combo.Options, Ext.ux.form.SuperBoxSelect, {
    newitem: function(bs, v) {
        bs.addNewItem({value: v});
    },
    afterrender: function () {
        var _this = this;
        console.log(_this);
        var item = document.querySelectorAll("#" + this.outerWrapEl.id + " ul")[0];
        if (item) {
            item.setAttribute("data-xcomponentid", this.id);
            new Sortable(item, {
                onEnd: function (evt) {
                    if (evt.currentTarget) {
                        var cmpId = evt.currentTarget.getAttribute("data-xcomponentid");
                        var cmp = Ext.getCmp(cmpId);
                        if (cmp) {
                            _this.refreshSorting(cmp);
                            MODx.fireResourceFormChange();
                        } else {
                            console.log("Unable to reference xComponentContext.");
                        }
                    }
                }
            });
        } else {
            console.log("Unable to find select element");
        }
    },
    refreshSorting: function (cmp) {
        var viewList = cmp.items.items;
        var dataInputList = document.querySelectorAll("#" + cmp.outerWrapEl.dom.id + " .x-superboxselect-input");
        var getElementIndex = function (item) {
            var nodeList = Array.prototype.slice.call(item.parentElement.children);
            return nodeList.indexOf(item);
        };
        var getElementByIndex = function (index) {
            return nodeList[index];
        };
        var getElementByValue = function (val, list) {
            for (var i = 0; i < list.length; i += 1) {
                if (list[i].value == val) {
                    return list[i];
                }
            }
        };
        var sortElementsByListIndex = function (list, callback) {
            list.sort(compare);
            if (callback instanceof Function) {
                callback();
            }
        };
        var syncElementsByValue = function (list1, list2, callback) {
            var targetListRootElement = list2[0].parentElement;
            if (targetListRootElement) {
                for (var i = 0; i < list1.length; i += 1) {
                    var targetItemIndex;
                    var item = list1[i];
                    var targetItem = getElementByValue(item.value, list2);
                    var initialTargetElement = list2[i];
                    if (targetItem !== null && initialTargetElement !== undefined) {
                        targetListRootElement.insertBefore(targetItem, initialTargetElement);
                    }
                }
            } else {
                console.debug("syncElementsByValue(), Unable to reference list root element.");
                return false;
            }
            if (callback instanceof Function) {
                callback();
            }
        };
        var compare = function (a, b) {
            var aIndex = getElementIndex(a.el.dom);
            var bIndex = getElementIndex(b.el.dom);
            if (aIndex < bIndex) {
                return -1;
            }
            if (aIndex > bIndex) {
                return 1;
            }
            return 0;
        };
        sortElementsByListIndex(viewList);
        syncElementsByValue(viewList, dataInputList[0].children);
        cmp.value = cmp.getValue();
    },
});
Ext.reg('minishop2-combo-options', miniShop2.combo.Options);
Пишу сюда в рецепты для случая если pull request отклонят, тогда данное решение останется и всегда будет доступно для поиска заинтересованным.

PS
второй мой готовый рецепт размещаю в Вопросах, т.к. не хватает рейтинга для размещения в Готовых рецептах :( Можно сказать это первые мои посты в сообществе.
lexikon
30 января 2017, 15:54
modx.pro
13
2 852
+19

Комментарии: 1

Володя
30 января 2017, 20:16
+4
ну по уму то делать без сторонних плагинов. Есть же service.cnxcorp.com/ext-4.1.1/docs/index.html#!/api/Ext.dd.DD
а так молодец! )
    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
    1