Benutzerdefinierte overflowHandler-Implementierung in ExtJS 4
Frage
Ich habe Panel mit zwei toolbars
.Wie kann ich eine benutzerdefinierte Klasse für die Verwendung als implementieren overflowHandler
, wodurch Komponenten beim Überlauf der ersten Symbolleiste in die zweite Symbolleiste verschoben werden?
Ich habe versucht, Code von zu verwenden Ext.layout.container.boxOverflow.Menu
, aber meine zweite Symbolleiste wird einfach ausgeblendet.
Hier ist mein Code, der gemischt wurde mit toolbar overflow
beispiel aus ExtJS 4
Distributiv.
Ext.require(['Ext.window.Window', 'Ext.toolbar.Toolbar', 'Ext.menu.ColorPicker', 'Ext.form.field.Date']);
Ext.onReady(function(){
/**
* Override for implementing tbar2
*/
Ext.override(Ext.panel.Panel, {
bridgeToolbars : function () {
var toolbar;
this.callParent(arguments);
if (this.tbar2) {
if (Ext.isArray(this.tbar2)) {
toolbar = {
xtype : 'toolbar',
items : this.tbar2
};
}
else if (!toolbar.xtype) {
toolbar.xtype = 'toolbar';
}
toolbar.dock = 'top';
toolbar.isTbar2 = true;
this.dockedItems = this.dockedItems.concat(toolbar);
this.tbar2 = null;
}
},
onRender : function () {
this.callParent(arguments);
var topBars = this.getDockedItems('toolbar[dock="top"]'),
i,
len;
for (i = 0, len = topBars.length; i < len; i++) {
if (topBars[i].isTbar2) {
this.tbar2 = topBars[i];
break;
}
}
},
/**
* Lazy creates new toolbar and returns it
* @param {Ext.panel.Panel} panel
* @param {String} position
* @return {Ext.toolbar.Toolbar}
*/
getDynamicTBar : function (position) {
var panel = this,
params,
tb;
position = position || 'top';
if (position === 'tbar2') {
tb = panel.tbar2;
params = {dock : 'top', isTbar2 : true};
}
else {
tb = panel.getDockedItems('toolbar[dock="' + position + '"]');
params = {dock : position};
if (tb.length > 0) {
tb = tb[0];
}
}
if (!tb) {
console.log('created tb at ' + position);
tb = Ext.create('Ext.toolbar.Toolbar', params);
panel.addDocked(tb);
}
return tb;
}
});
Ext.define('Ext.layout.container.boxOverflow.TBar2', {
extend : 'Ext.layout.container.boxOverflow.None',
constructor : function () {
this.tbar2Items = [];
return this.callParent(arguments);
},
beginLayout : function (ownerContext) {
this.callParent(arguments);
this.clearOverflow(ownerContext);
},
beginLayoutCycle : function (ownerContext, firstCycle) {
this.callParent(arguments);
if (!firstCycle) {
this.clearOverflow(ownerContext);
this.layout.cacheChildItems(ownerContext);
}
},
getOverflowCls : function () {
return Ext.baseCSSPrefix + this.layout.direction + '-box-overflow-body';
},
_asLayoutRoot : { isRoot : true },
clearOverflow : function () {
if (this.tbar2) {
this.tbar2.suspendLayouts();
this.tbar2.hide();
this.tbar2.resumeLayouts(this._asLayoutRoot);
}
this.tbar2Items.length = 0;
},
handleOverflow : function (ownerContext) {
var me = this,
layout = me.layout,
owner = layout.owner,
names = layout.getNames(),
startProp = names.x,
sizeProp = names.width,
plan = ownerContext.state.boxPlan,
available = plan.targetSize[sizeProp],
childItems = ownerContext.childItems,
len = childItems.length,
childContext,
comp, i, props,
tbarOwner = owner.ownerCt;
owner.suspendLayouts();
// Hide all items which are off the end, and store them to allow them to be restored
// before each layout operation.
me.tbar2Items.length = 0;
for (i = 0; i < len; i++) {
childContext = childItems[i];
props = childContext.props;
if (props[startProp] + props[sizeProp] > available) {
comp = childContext.target;
me.tbar2Items.push(comp);
owner.remove(comp, false);
}
}
owner.resumeLayouts();
if (!me.tbar2 && (tbarOwner instanceof Ext.panel.Panel)) {
me.tbar2 = tbarOwner.getDynamicTBar('tbar2');
}
me.tbar2.suspendLayouts();
me.tbar2.show();
Ext.each(me.tbar2Items, function(item, index) {
me.tbar2.add(item);
});
me.tbar2.resumeLayouts(me._asLayoutRoot);
}
});
var handleAction = function(action){
Ext.example.msg('<b>Action</b>', 'You clicked "' + action + '"');
};
var colorMenu = Ext.create('Ext.menu.ColorPicker', {
handler: function(cm, color){
Ext.example.msg('Color Selected', '<span style="color:#' + color + ';">You choose {0}.</span>', color);
}
});
var showDate = function(d, value) {
Ext.example.msg('<b>Action date</b>', 'You picked ' + Ext.Date.format(value, d.format));
};
var fromPicker = false;
Ext.create('Ext.window.Window', {
title: 'Standard',
closable: false,
height:250,
width: 500,
bodyStyle: 'padding:10px',
contentEl: 'content',
autoScroll: true,
tbar: Ext.create('Ext.toolbar.Toolbar', {
layout: {
overflowHandler: 'TBar2'
},
items: [{
xtype:'splitbutton',
text: 'Menu Button',
iconCls: 'add16',
handler: Ext.Function.pass(handleAction, 'Menu Button'),
menu: [{text: 'Menu Item 1', handler: Ext.Function.pass(handleAction, 'Menu Item 1')}]
},'-',{
xtype:'splitbutton',
text: 'Cut',
iconCls: 'add16',
handler: Ext.Function.pass(handleAction, 'Cut'),
menu: [{text: 'Cut menu', handler: Ext.Function.pass(handleAction, 'Cut menu')}]
},{
text: 'Copy',
iconCls: 'add16',
handler: Ext.Function.pass(handleAction, 'Copy')
},{
text: 'Paste',
iconCls: 'add16',
menu: [{text: 'Paste menu', handler: Ext.Function.pass(handleAction, 'Paste menu')}]
},'-',{
text: 'Format',
iconCls: 'add16',
handler: Ext.Function.pass(handleAction, 'Format')
},'->', {
fieldLabel: 'Action',
labelWidth: 70,
width: 180,
xtype: 'datefield',
labelSeparator: '',
enableKeyEvents: true,
listeners: {
expand: function(){
fromPicker = true;
},
collapse: function(){
fromPicker = false;
},
change: function(d, newVal, oldVal) {
if (fromPicker || !d.isVisible()) {
showDate(d, newVal);
}
},
keypress: {
buffer: 500,
fn: function(field){
var value = field.getValue();
if (value !== null && field.isValid()) {
showDate(field, value);
}
}
}
}
}, {
text: 'Sell',
iconCls: 'money-down',
enableToggle: true,
toggleHandler: function(button, pressed) {
Ext.example.msg('<b>Action</b>', 'Right ToggleButton ' + (pressed ? 'Buy' : 'Sell'));
button.setText(pressed ? 'Buy' : 'Sell')
button.setIconCls(pressed ? 'money-up' : 'money-down')
}
}, {
text: 'Choose a Color',
menu: colorMenu // <-- submenu by reference
}]
})
}).show();
});
Lösung
Wir müssen overflowHandler für das Symbolleistenlayout implementieren, das eine zweite Symbolleiste hinzufügt und Komponenten von der ersten in die zweite Symbolleiste umschließt, wenn sie nicht angezeigt werden können, weil der Container der Symbolleiste nicht breit genug ist.Zuerst müssen wir Ext überschreiben.Panel.Panel:
Ext4.override(Ext4.panel.Panel, {
bridgeToolbars: function () {
var toolbar;
this.callParent(arguments);
if (this.tbar2) {
if (Ext4.isArray(this.tbar2)) {
toolbar = {
xtype: 'toolbar',
items: this.tbar2
};
} else if (!toolbar.xtype) {
toolbar.xtype = 'toolbar';
}
toolbar.dock = 'top';
toolbar.isTbar2 = true;
this.dockedItems = this.dockedItems.concat(toolbar);
this.tbar2 = null;
}
},
onRender: function () {
this.callParent(arguments);
var topBars = this.getDockedItems('toolbar[dock="top"]'),
i,
len;
for (i = 0, len = topBars.length; i < len; i++) {
if (topBars[i].isTbar2) {
this.tbar2 = topBars[i];
break;
}
}
},
/**
* Creates, if not exists, and returns toolbar at passed position
* @param {Ext.panel.Panel} panel
* @param {String} position
* @return {Ext.toolbar.Toolbar}
*/
getDynamicTBar: function (position) {
var panel = this,
params,
tb;
position = position || 'top';
if (position === 'tbar2') {
tb = panel.tbar2;
params = {
dock: 'top',
isTbar2: true,
layout: {
overflowHandler: 'Scroller'
}
};
} else {
tb = panel.getDockedItems('toolbar[dock="' + position + '"]');
params = {
dock: position
};
if (tb.length > 0) {
tb = tb[0];
}
}
if (!tb) {
tb = Ext4.create('Ext4.toolbar.Toolbar', params);
panel.addDocked(tb);
if (position === 'tbar2') {
panel.tbar2 = tb;
}
}
return tb;
}
});
Als nächstes müssen wir die Klasse Ext4 erstellen.Layout.Container.boxÜberlauf.TBar2 (es hat einen so langen Namen, weil ExtJS handleOverflow (eine Instanz von String ) in fest codiertem Ext4 durchsucht.Layout.Container.boxOverflow-Namensraum).
/**
* @class Ext4.layout.container.boxOverflow.TBar2
* Class for using as overflowHandler
* @extends Ext.layout.container.boxOverflow.None
*/
Ext4.define('Ext4.layout.container.boxOverflow.TBar2', {
extend: 'Ext4.layout.container.boxOverflow.None',
/**
* @private
* @property {Boolean} initialized
*/
initialized: false,
constructor: function () {
/**
* @private
* @property {Array} tbar2Items
* List of moved components
*/
this.tbar2Items = [];
return this.callParent(arguments);
},
beginLayout: function (ownerContext) {
if (!this.initialized) {
this.layout.owner.ownerCt.on('afterlayout', this.createTBar2, this);
this.initialized = true;
}
this.callParent(arguments);
this.clearOverflow(ownerContext);
},
beginLayoutCycle: function (ownerContext, firstCycle) {
this.callParent(arguments);
if (!firstCycle) {
this.clearOverflow(ownerContext);
this.layout.cacheChildItems(ownerContext);
}
},
getOverflowCls: function () {
return Ext4.baseCSSPrefix + this.layout.direction + '-box-overflow-body';
},
/**
* @private
* @property {Object} _asLayoutRoot
*/
_asLayoutRoot: {
isRoot: true
},
clearOverflow: function () {
// If afterlayout is not processing and tbar2 already created,
// we can to move components from tbar2 to first toolbar, because now
// there isn't any overflow
if (!this.processing && this.tbar2) {
this.tbar2.items.each(function (item) {
this.tbar1.add(item);
// change ui from saved property, or CSS will be incorrect
item.ui = item.origUI;
}, this);
this.tbar2.removeAll(false);
this.tbar2.hide();
}
this.tbar2Items.length = 0;
},
/**
* @private
* Creates tbar2 and does other routines
*/
createTBar2: function () {
// if afterlayout event is still processing,
// or we don't need to handle toolbar overflow, just return from function
if (this.processing || !this.doTbar2) {
return;
}
this.processing = true;
this.doTbar2 = false;
var me = this,
ownerContext = this.mOwnerContext,
layout = me.layout,
owner = layout.owner,
names = layout.getNames(),
startProp = names.x,
sizeProp = names.width,
plan = ownerContext.state.boxPlan,
available = plan.targetSize[sizeProp],
childItems = ownerContext.childItems,
len = childItems.length,
childContext,
comp, i, props,
tbarOwner = owner.ownerCt;
me.tbar1 = owner;
// save components which will be moved
owner.suspendLayouts();
me.tbar2Items.length = 0;
for (i = 0; i < len; i++) {
childContext = childItems[i];
props = childContext.props;
if (props[startProp] + props[sizeProp] > available) {
comp = childContext.target;
me.tbar2Items.push(comp);
// save original ui property
comp.origUI = comp.ui;
owner.remove(comp, false);
}
}
owner.resumeLayouts();
// add tbar2 to the layout cycle (you can see very similar code in clearOverflow
// method of Ext.layout.container.boxOverflow.Menu)
tbarOwner.suspendLayouts();
me.tbar2 = tbarOwner.getDynamicTBar('tbar2');
me.tbar2.show();
// moving components
Ext4.each(me.tbar2Items, function (item, index) {
me.tbar2.insert(index, item);
item.ui = item.origUI;
});
tbarOwner.resumeLayouts(this._asLayoutRoot);
this.processing = false;
},
handleOverflow: function (ownerContext) {
// set flag and store context for later using
this.doTbar2 = true;
this.mOwnerContext = ownerContext;
}
});
Bitte beachten Sie, dass ich ExtJS 4 Bootstrapping verwende (weil es in meiner Web-App auch ExtJD 2.3 gibt), also müssen Sie in Ihrem Code wahrscheinlich verwenden Ext.
statt Ext4.