@atian25
Thanks for idea, below my solution:
GroupingList
Code:
Ext.define('Ext.ux.view.GroupingList', {
extend: 'Ext.view.View',
alias: 'widget.groupinlist',
requires: ['Ext.layout.component.BoundList', 'Ext.toolbar.Paging'],
pageSize: 0,
autoScroll: true,
baseCls: Ext.baseCSSPrefix + 'boundlist',
listItemCls: '',
shadow: false,
trackOver: true,
refreshed: 0,
ariaRole: 'listbox',
componentLayout: 'boundlist',
renderTpl: ['<div class="list-ct"></div>'],
initComponent: function() {
var me = this,
baseCls = me.baseCls,
itemCls = baseCls + '-item';
me.itemCls = itemCls;
me.selectedItemCls = baseCls + '-selected';
me.overItemCls = baseCls + '-item-over';
me.itemSelector = "." + itemCls;
if (me.floating) {
me.addCls(baseCls + '-floating');
}
var tpl = [
'<ul>',
'<tpl for=".">'
];
var padding = 1;
if (Ext.isArray(me.groupField)) {
padding = me.groupField.length;
for (var i = 0; i < me.groupField.length; i++) {
tpl.push(
'<tpl if="xindex == 1 || parent[xindex - 2][\'' + me.groupField[i] + '\'] != values[\'' + me.groupField[i] + '\']">',
'<li class="x-combo-list-group" style="padding-left:' + (i * 16) + 'px;">{[values["' + me.groupField[i] + '"]]}</li>',
'</tpl>'
);
}
}
else {
tpl.push(
'<tpl if="xindex == 1 || parent[xindex - 2][\'' + me.groupField + '\'] != values[\'' + me.groupField + '\']">',
'<li class="x-combo-list-group">{[values["' + me.groupField + '"]]}</li>',
'</tpl>'
);
}
tpl.push(
'<li role="option" class="' + itemCls + '" style="padding-left:' + (padding * 16) + 'px;">{[values["' + me.displayField + '"]]}</li>',
'</tpl>',
'</ul>'
);
me.tpl = Ext.create('Ext.XTemplate', tpl);
if (me.pageSize) {
me.pagingToolbar = me.createPagingToolbar();
}
me.callParent();
Ext.applyIf(me.renderSelectors, {
listEl: '.list-ct'
});
},
createPagingToolbar: function() {
return Ext.widget('pagingtoolbar', {
pageSize: this.pageSize,
store: this.store,
border: false
});
},
onRender: function() {
var me = this,
toolbar = me.pagingToolbar;
me.callParent(arguments);
if (toolbar) {
toolbar.render(me.el);
}
},
bindStore : function(store, initial) {
var me = this,
toolbar = me.pagingToolbar;
me.callParent(arguments);
if (toolbar) {
toolbar.bindStore(store, initial);
}
},
getTargetEl: function() {
return this.listEl || this.el;
},
getInnerTpl: function(displayField) {
return '{' + displayField + '}';
},
refresh: function() {
var me = this;
me.callParent();
if (me.isVisible()) {
me.refreshed++;
me.doComponentLayout();
me.refreshed--;
}
},
initAria: function() {
this.callParent();
var selModel = this.getSelectionModel(),
mode = selModel.getSelectionMode(),
actionEl = this.getActionEl();
if (mode !== 'SINGLE') {
actionEl.dom.setAttribute('aria-multiselectable', true);
}
},
onDestroy: function() {
Ext.destroyMembers(this, 'pagingToolbar', 'listEl');
this.callParent();
}
});
GroupingComboBox
Code:
Ext.define('Ext.ux.form.GroupingComboBox', {
extend: 'Ext.form.field.ComboBox',
requires: ['Ext.ux.view.GroupingList'],
alias: ['widget.groupingcombobox', 'widget.groupingcombo'],
initComponent: function() {
var me = this;
if (!me.displayTpl) {
var display = [],
tpl = '<tpl for=".">{0}</tpl>';
if (Ext.isArray(me.groupField)) {
for (var i = 0; i < me.groupField.length; i++) {
display.push('{[values["' + me.groupField[i] + '"]]}');
}
}
else {
display.push('{[values["' + me.groupField + '"]]}');
}
display.push('{[values["' + me.displayField + '"]]}');
me.displayTpl = Ext.String.format(tpl, display.join(this.displaySeparator || ' '));
}
me.callParent();
},
createPicker: function() {
var me = this,
picker,
menuCls = Ext.baseCSSPrefix + 'menu',
opts = Ext.apply({
selModel: {
mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
},
floating: true,
hidden: true,
ownerCt: me.ownerCt,
cls: me.el.up('.' + menuCls) ? menuCls : '',
store: me.store,
groupField: me.groupField,
displayField: me.displayField,
focusOnToFront: false,
pageSize: me.pageSize
}, me.listConfig, me.defaultListConfig);
//picker = me.picker = Ext.create('Ext.view.BoundList', opts);
picker = me.picker = Ext.create('Ext.ux.view.GroupingList', opts);
me.mon(picker, {
itemclick: me.onItemClick,
refresh: me.onListRefresh,
scope: me
});
me.mon(picker.getSelectionModel(), 'selectionchange', me.onListSelectionChange, me);
return picker;
}
});
CSS
Code:
.x-combo-list-group {
font : bold 12px tahoma,arial,helvetica,sans-serif;
padding : 2px;
border : 1px solid #fff;
white-space : nowrap;
overflow : hidden;
text-overflow : ellipsis;
}
Example
Code:
{
anchor: '100%',
fieldLabel: 'My Grouping ComboBox',
xtype: 'groupingcombobox',
name: 'name',
allowBlank: false,
groupField: ['nivel1', 'nivel2'],// string or array
valueField: 'value',
displayField: 'display',
queryMode: 'local',
typeAhead: true,
store: {
fields: [
{ name: 'value', type: 'int' },
{ name: 'nivel1', type: 'string' },
{ name: 'nivel2', type: 'string' },
{ name: 'display', type: 'string' }
],
sorters: [
{ property: 'nivel1', direction: 'ASC' },
{ property: 'nivel2', direction: 'ASC' },
{ property: 'display', direction: 'ASC' }
],
data: [
{ value: 1, nivel1: 'nivel 1', nivel2: 'nivel 1.1', display: 'display 1' },
{ value: 2, nivel1: 'nivel 1', nivel2: 'nivel 1.1', display: 'display 2' },
{ value: 3, nivel1: 'nivel 1', nivel2: 'nivel 1.1', display: 'display 3' },
{ value: 4, nivel1: 'nivel 2', nivel2: 'nivel 2.1', display: 'display 4' },
{ value: 5, nivel1: 'nivel 2', nivel2: 'nivel 2.1', display: 'display 5' },
{ value: 6, nivel1: 'nivel 2', nivel2: 'nivel 2.1', display: 'display 6' },
{ value: 7, nivel1: 'nivel 3', nivel2: 'nivel 3.1', display: 'display 7' },
{ value: 8, nivel1: 'nivel 3', nivel2: 'nivel 3.1', display: 'display 8' },
{ value: 9, nivel1: 'nivel 3', nivel2: 'nivel 3.1', display: 'display 9' }
]
}
}