outlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplace
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
608 lines
19 KiB
608 lines
19 KiB
/** |
|
* A container for grouping sets of fields, rendered as a HTML `fieldset` element. The {@link #title} |
|
* config will be rendered as the fieldset's `legend`. |
|
* |
|
* While FieldSets commonly contain simple groups of fields, they are general {@link Ext.container.Container Containers} |
|
* and may therefore contain any type of components in their {@link #cfg-items}, including other nested containers. |
|
* The default {@link #layout} for the FieldSet's items is `'anchor'`, but it can be configured to use any other |
|
* layout type. |
|
* |
|
* FieldSets may also be collapsed if configured to do so; this can be done in two ways: |
|
* |
|
* 1. Set the {@link #collapsible} config to true; this will result in a collapse button being rendered next to |
|
* the {@link #title legend title}, or: |
|
* 2. Set the {@link #checkboxToggle} config to true; this is similar to using {@link #collapsible} but renders |
|
* a {@link Ext.form.field.Checkbox checkbox} in place of the toggle button. The fieldset will be expanded when the |
|
* checkbox is checked and collapsed when it is unchecked. The checkbox will also be included in the |
|
* {@link Ext.form.Basic#submit form submit parameters} using the {@link #checkboxName} as its parameter name. |
|
* |
|
* # Example usage |
|
* |
|
* @example |
|
* Ext.create('Ext.form.Panel', { |
|
* title: 'Simple Form with FieldSets', |
|
* labelWidth: 75, // label settings here cascade unless overridden |
|
* url: 'save-form.php', |
|
* frame: true, |
|
* bodyStyle: 'padding:5px 5px 0', |
|
* width: 550, |
|
* renderTo: Ext.getBody(), |
|
* layout: 'column', // arrange fieldsets side by side |
|
* items: [{ |
|
* // Fieldset in Column 1 - collapsible via toggle button |
|
* xtype:'fieldset', |
|
* columnWidth: 0.5, |
|
* title: 'Fieldset 1', |
|
* collapsible: true, |
|
* defaultType: 'textfield', |
|
* defaults: {anchor: '100%'}, |
|
* layout: 'anchor', |
|
* items :[{ |
|
* fieldLabel: 'Field 1', |
|
* name: 'field1' |
|
* }, { |
|
* fieldLabel: 'Field 2', |
|
* name: 'field2' |
|
* }] |
|
* }, { |
|
* // Fieldset in Column 2 - collapsible via checkbox, collapsed by default, contains a panel |
|
* xtype:'fieldset', |
|
* title: 'Show Panel', // title or checkboxToggle creates fieldset header |
|
* columnWidth: 0.5, |
|
* checkboxToggle: true, |
|
* collapsed: true, // fieldset initially collapsed |
|
* layout:'anchor', |
|
* items :[{ |
|
* xtype: 'panel', |
|
* anchor: '100%', |
|
* title: 'Panel inside a fieldset', |
|
* frame: true, |
|
* height: 52 |
|
* }] |
|
* }] |
|
* }); |
|
*/ |
|
Ext.define('Ext.form.FieldSet', { |
|
extend: 'Ext.container.Container', |
|
mixins: { |
|
fieldAncestor: 'Ext.form.FieldAncestor' |
|
}, |
|
alias: 'widget.fieldset', |
|
uses: ['Ext.form.field.Checkbox', 'Ext.panel.Tool', 'Ext.layout.container.Anchor', 'Ext.layout.component.FieldSet'], |
|
|
|
focusable: true, |
|
|
|
/** |
|
* @cfg {String} title |
|
* A title to be displayed in the fieldset's legend. May contain HTML markup. |
|
*/ |
|
|
|
/** |
|
* @cfg {Boolean} [checkboxToggle=false] |
|
* Set to true to render a checkbox into the fieldset frame just in front of the legend to expand/collapse the |
|
* fieldset when the checkbox is toggled.. This checkbox will be included in form submits using |
|
* the {@link #checkboxName}. |
|
*/ |
|
|
|
/** |
|
* @cfg {String} checkboxName |
|
* The name to assign to the fieldset's checkbox if {@link #checkboxToggle} = true |
|
* (defaults to '[fieldset id]-checkbox'). |
|
*/ |
|
|
|
/** |
|
* @cfg {String} checkboxUI |
|
* The ui to use for the fieldset's checkbox. |
|
*/ |
|
checkboxUI: 'default', |
|
|
|
/** |
|
* @cfg {Boolean} [collapsible=false] |
|
* Set to true to make the fieldset collapsible and have the expand/collapse toggle button automatically rendered |
|
* into the legend element, false to keep the fieldset statically sized with no collapse button. |
|
* Another option is to configure {@link #checkboxToggle}. Use the {@link #collapsed} config to collapse the |
|
* fieldset by default. |
|
*/ |
|
|
|
/** |
|
* @cfg {Boolean} collapsed |
|
* Set to true to render the fieldset as collapsed by default. If {@link #checkboxToggle} is specified, the checkbox |
|
* will also be unchecked by default. |
|
*/ |
|
collapsed: false, |
|
|
|
/** |
|
* @cfg {Boolean} [toggleOnTitleClick=true] |
|
* Set to true will add a listener to the titleCmp property for the click event which will execute the |
|
* {@link #toggle} method. This option is only used when the {@link #collapsible} property is set to true. |
|
*/ |
|
toggleOnTitleClick : true, |
|
|
|
/** |
|
* @property {Ext.Component} legend |
|
* The component for the fieldset's legend. Will only be defined if the configuration requires a legend to be |
|
* created, by setting the {@link #title} or {@link #checkboxToggle} options. |
|
*/ |
|
|
|
/** |
|
* @cfg {String} [baseCls='x-fieldset'] |
|
* The base CSS class applied to the fieldset. |
|
*/ |
|
baseCls: Ext.baseCSSPrefix + 'fieldset', |
|
|
|
/** |
|
* @cfg {Ext.enums.Layout/Object} layout |
|
* The {@link Ext.container.Container#layout} for the fieldset's immediate child items. |
|
*/ |
|
layout: 'anchor', |
|
|
|
componentLayout: 'fieldset', |
|
|
|
ariaRole: null, |
|
|
|
autoEl: 'fieldset', |
|
|
|
childEls: [ |
|
'body' |
|
], |
|
|
|
renderTpl: [ |
|
'{%this.renderLegend(out,values);%}', |
|
'<div id="{id}-body" data-ref="body" class="{baseCls}-body {baseCls}-body-{ui} {bodyTargetCls}" ', |
|
'role="presentation"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>', |
|
'{%this.renderContainer(out,values);%}', |
|
'</div>' |
|
], |
|
|
|
stateEvents : [ 'collapse', 'expand' ], |
|
|
|
maskOnDisable: false, |
|
|
|
/** |
|
* @event beforeexpand |
|
* Fires before this FieldSet is expanded. Return false to prevent the expand. |
|
* @param {Ext.form.FieldSet} fieldset The FieldSet being expanded. |
|
*/ |
|
|
|
/** |
|
* @event beforecollapse |
|
* Fires before this FieldSet is collapsed. Return false to prevent the collapse. |
|
* @param {Ext.form.FieldSet} fieldset The FieldSet being collapsed. |
|
*/ |
|
|
|
/** |
|
* @event expand |
|
* Fires after this FieldSet has expanded. |
|
* @param {Ext.form.FieldSet} fieldset The FieldSet that has been expanded. |
|
*/ |
|
|
|
/** |
|
* @event collapse |
|
* Fires after this FieldSet has collapsed. |
|
* @param {Ext.form.FieldSet} fieldset The FieldSet that has been collapsed. |
|
*/ |
|
|
|
beforeDestroy: function(){ |
|
var me = this, |
|
legend = me.legend; |
|
|
|
if (legend) { |
|
// get rid of the ownerCt since it's not a proper item |
|
delete legend.ownerCt; |
|
legend.destroy(); |
|
me.legend = null; |
|
} |
|
me.callParent(); |
|
}, |
|
|
|
initComponent: function() { |
|
var me = this, |
|
baseCls = me.baseCls; |
|
|
|
me.initFieldAncestor(); |
|
|
|
me.callParent(); |
|
|
|
// Fieldsets cannot support managePadding because the managePadding config causes |
|
// the paddding to be added to the innerCt instead of the fieldset element. The |
|
// padding must be on the fieldset element because the horizontal position of the |
|
// legend is determined by the fieldset element's padding |
|
// |
|
// As a consequence of the inability to support managePadding, manageOverflow |
|
// cannot be supported either because the correct overflow cannot be calculated |
|
// without managePadding to adjust for cross-browser differences in the way |
|
// padding is handled on overflowing elements. |
|
// See Ext.layout.container.Auto for more info. |
|
me.layout.managePadding = me.layout.manageOverflow = false; |
|
|
|
if (me.collapsed) { |
|
me.addCls(baseCls + '-collapsed'); |
|
me.collapse(); |
|
} |
|
if (me.title || me.checkboxToggle || me.collapsible) { |
|
me.addTitleClasses(); |
|
me.legend = Ext.widget(me.createLegendCt()); |
|
} |
|
me.initMonitor(); |
|
}, |
|
|
|
/** |
|
* Initialized the renderData to be used when rendering the renderTpl. |
|
* @return {Object} Object with keys and values that are going to be applied to the renderTpl |
|
* @private |
|
*/ |
|
initRenderData: function() { |
|
var me = this, |
|
data = me.callParent(); |
|
|
|
data.bodyTargetCls = me.bodyTargetCls; |
|
me.protoBody.writeTo(data); |
|
delete me.protoBody; |
|
|
|
return data; |
|
}, |
|
|
|
getState: function () { |
|
var state = this.callParent(); |
|
|
|
state = this.addPropertyToState(state, 'collapsed'); |
|
|
|
return state; |
|
}, |
|
|
|
afterCollapse: Ext.emptyFn, |
|
afterExpand: Ext.emptyFn, |
|
|
|
collapsedHorizontal: function () { |
|
return true; |
|
}, |
|
|
|
collapsedVertical: function () { |
|
return true; |
|
}, |
|
|
|
createLegendCt: function () { |
|
var me = this, |
|
items = [], |
|
legend = { |
|
xtype: 'container', |
|
baseCls: me.baseCls + '-header', |
|
// use container layout so we don't get the auto layout innerCt/outerCt |
|
layout: 'container', |
|
ui: me.ui, |
|
id: me.id + '-legend', |
|
autoEl: 'legend', |
|
ariaRole: null, |
|
ariaLabelledBy: '.' + me.baseCls + '-header-text', |
|
items: items, |
|
ownerCt: me, |
|
shrinkWrap: true, |
|
ownerLayout: me.componentLayout |
|
}; |
|
|
|
// Checkbox |
|
if (me.checkboxToggle) { |
|
items.push(me.createCheckboxCmp()); |
|
} else if (me.collapsible) { |
|
// Toggle button |
|
items.push(me.createToggleCmp()); |
|
} |
|
|
|
// Title |
|
items.push(me.createTitleCmp()); |
|
|
|
return legend; |
|
}, |
|
|
|
/** |
|
* Creates the legend title component. This is only called internally, but could be overridden in subclasses to |
|
* customize the title component. If {@link #toggleOnTitleClick} is set to true, a listener for the click event |
|
* will toggle the collapsed state of the FieldSet. |
|
* @return {Ext.Component} |
|
* @protected |
|
*/ |
|
createTitleCmp: function() { |
|
var me = this, |
|
cfg = { |
|
xtype: 'component', |
|
html: me.title, |
|
ui: me.ui, |
|
cls: me.baseCls + '-header-text', |
|
id: me.id + '-legendTitle' |
|
}; |
|
|
|
if (me.collapsible && me.toggleOnTitleClick) { |
|
cfg.listeners = { |
|
click : { |
|
element: 'el', |
|
scope : me, |
|
fn : me.toggle |
|
} |
|
}; |
|
cfg.cls += ' ' + me.baseCls + '-header-text-collapsible'; |
|
} |
|
|
|
return (me.titleCmp = Ext.widget(cfg)); |
|
}, |
|
|
|
/** |
|
* @property {Ext.form.field.Checkbox} checkboxCmp |
|
* Refers to the {@link Ext.form.field.Checkbox} component that is added next to the title in the legend. Only |
|
* populated if the fieldset is configured with {@link #checkboxToggle}:true. |
|
*/ |
|
|
|
/** |
|
* Creates the checkbox component. This is only called internally, but could be overridden in subclasses to |
|
* customize the checkbox's configuration or even return an entirely different component type. |
|
* @return {Ext.Component} |
|
* @protected |
|
*/ |
|
createCheckboxCmp: function() { |
|
var me = this, |
|
suffix = '-checkbox', |
|
cls = me.baseCls + '-header' + suffix; |
|
|
|
cls += ' ' + cls + '-' + me.ui; |
|
|
|
me.checkboxCmp = Ext.widget({ |
|
xtype: 'checkbox', |
|
hideEmptyLabel: true, |
|
name: me.checkboxName || me.id + suffix, |
|
cls: cls, |
|
id: me.id + '-legendChk', |
|
ui: me.checkboxUI, |
|
checked: !me.collapsed, |
|
msgTarget: 'none', |
|
listeners: { |
|
change: me.onCheckChange, |
|
scope: me |
|
} |
|
}); |
|
return me.checkboxCmp; |
|
}, |
|
|
|
/** |
|
* @property {Ext.panel.Tool} toggleCmp |
|
* Refers to the {@link Ext.panel.Tool} component that is added as the collapse/expand button next to the title in |
|
* the legend. Only populated if the fieldset is configured with {@link #collapsible}:true. |
|
*/ |
|
|
|
/** |
|
* Creates the toggle button component. This is only called internally, but could be overridden in subclasses to |
|
* customize the toggle component. |
|
* @return {Ext.Component} |
|
* @protected |
|
*/ |
|
createToggleCmp: function() { |
|
var me = this; |
|
|
|
me.toggleCmp = Ext.widget({ |
|
xtype: 'tool', |
|
// fieldset tools may be styled differently from regular tools and so we need |
|
// to tell the layout system not to cache the height if this tool happens |
|
// to be the first one through the layout system |
|
cacheHeight: false, |
|
cls: me.baseCls + '-header-tool-' + me.ui, |
|
type: 'toggle', |
|
handler: me.toggle, |
|
id: me.id + '-legendToggle', |
|
scope: me |
|
}); |
|
return me.toggleCmp; |
|
}, |
|
|
|
doRenderLegend: function (out, renderData) { |
|
// Careful! This method is bolted on to the renderTpl so all we get for context is |
|
// the renderData! The "this" pointer is the renderTpl instance! |
|
|
|
var me = renderData.$comp, |
|
legend = me.legend, |
|
tree; |
|
|
|
// Create the Legend component if needed |
|
if (legend) { |
|
legend.ownerLayout.configureItem(legend); |
|
tree = legend.getRenderTree(); |
|
Ext.DomHelper.generateMarkup(tree, out); |
|
} |
|
}, |
|
|
|
getCollapsed: function () { |
|
return this.collapsed ? 'top' : false; |
|
}, |
|
|
|
getCollapsedDockedItems: function () { |
|
var legend = this.legend; |
|
|
|
return legend ? [ legend ] : []; |
|
}, |
|
|
|
/** |
|
* Sets the title of this fieldset. |
|
* @param {String} title The new title. |
|
* @return {Ext.form.FieldSet} this |
|
*/ |
|
setTitle: function(title) { |
|
var me = this, |
|
legend = me.legend; |
|
|
|
me.title = title; |
|
if (me.rendered) { |
|
if (!legend) { |
|
me.legend = legend = Ext.widget(me.createLegendCt()); |
|
me.addTitleClasses(); |
|
legend.ownerLayout.configureItem(legend); |
|
legend.render(me.el, 0); |
|
} |
|
me.titleCmp.update(title); |
|
} else if (legend) { |
|
me.titleCmp.update(title); |
|
} else { |
|
me.addTitleClasses(); |
|
me.legend = Ext.widget(me.createLegendCt()); |
|
} |
|
return me; |
|
}, |
|
|
|
addTitleClasses: function(){ |
|
var me = this, |
|
title = me.title, |
|
baseCls = me.baseCls; |
|
|
|
if (title) { |
|
me.addCls(baseCls + '-with-title'); |
|
} |
|
|
|
if (title || me.checkboxToggle || me.collapsible) { |
|
me.addCls(baseCls + '-with-legend'); |
|
} |
|
}, |
|
|
|
/** |
|
* Expands the fieldset. |
|
* @return {Ext.form.FieldSet} this |
|
*/ |
|
expand : function(){ |
|
return this.setExpanded(true); |
|
}, |
|
|
|
/** |
|
* Collapses the fieldset. |
|
* @return {Ext.form.FieldSet} this |
|
*/ |
|
collapse : function() { |
|
return this.setExpanded(false); |
|
}, |
|
|
|
/** |
|
* @private |
|
* Collapse or expand the fieldset. |
|
*/ |
|
setExpanded: function(expanded) { |
|
var me = this, |
|
checkboxCmp = me.checkboxCmp, |
|
operation = expanded ? 'expand' : 'collapse'; |
|
|
|
if (!me.rendered || me.fireEvent('before' + operation, me) !== false) { |
|
expanded = !!expanded; |
|
|
|
if (checkboxCmp) { |
|
checkboxCmp.setValue(expanded); |
|
} |
|
|
|
if (expanded) { |
|
me.removeCls(me.baseCls + '-collapsed'); |
|
} else { |
|
me.addCls(me.baseCls + '-collapsed'); |
|
} |
|
me.collapsed = !expanded; |
|
if (expanded) { |
|
delete me.getInherited().collapsed; |
|
} else { |
|
me.getInherited().collapsed = true; |
|
} |
|
if (me.rendered) { |
|
// say explicitly we are not root because when we have a fixed/configured height |
|
// our ownerLayout would say we are root and so would not have it's height |
|
// updated since it's not included in the layout cycle |
|
me.updateLayout({ isRoot: false }); |
|
me.fireEvent(operation, me); |
|
} |
|
} |
|
return me; |
|
}, |
|
|
|
getRefItems: function(deep) { |
|
var refItems = this.callParent(arguments), |
|
legend = this.legend; |
|
|
|
// Prepend legend items to ensure correct order |
|
if (legend) { |
|
refItems.unshift(legend); |
|
if (deep) { |
|
refItems.unshift.apply(refItems, legend.getRefItems(true)); |
|
} |
|
} |
|
return refItems; |
|
}, |
|
|
|
/** |
|
* Toggle the fieldset's collapsed state to the opposite of what it is currently. |
|
*/ |
|
toggle: function() { |
|
this.setExpanded(!!this.collapsed); |
|
}, |
|
|
|
privates: { |
|
applyTargetCls: function(targetCls) { |
|
this.bodyTargetCls = targetCls; |
|
}, |
|
|
|
finishRender: function () { |
|
var legend = this.legend; |
|
|
|
this.callParent(); |
|
|
|
if (legend) { |
|
legend.finishRender(); |
|
} |
|
}, |
|
|
|
getProtoBody: function () { |
|
var me = this, |
|
body = me.protoBody; |
|
|
|
if (!body) { |
|
me.protoBody = body = new Ext.util.ProtoElement({ |
|
styleProp: 'bodyStyle', |
|
styleIsText: true |
|
}); |
|
} |
|
|
|
return body; |
|
}, |
|
|
|
getDefaultContentTarget: function() { |
|
return this.body; |
|
}, |
|
|
|
getTargetEl : function() { |
|
return this.body || this.frameBody || this.el; |
|
}, |
|
|
|
initPadding: function(targetEl) { |
|
var me = this, |
|
body = me.getProtoBody(), |
|
padding = me.padding, |
|
bodyPadding; |
|
|
|
if (padding !== undefined) { |
|
if (Ext.isIE8) { |
|
// IE8 and below display fieldset top padding outside the border |
|
// so we transfer the top padding to the body element. |
|
padding = me.parseBox(padding); |
|
bodyPadding = Ext.Element.parseBox(0); |
|
bodyPadding.top = padding.top; |
|
padding.top = 0; |
|
body.setStyle('padding', me.unitizeBox(bodyPadding)); |
|
} |
|
|
|
targetEl.setStyle('padding', me.unitizeBox(padding)); |
|
} |
|
}, |
|
|
|
/** |
|
* @private |
|
* Handle changes in the checkbox checked state. |
|
*/ |
|
onCheckChange: function(cmp, checked) { |
|
this.setExpanded(checked); |
|
}, |
|
|
|
setupRenderTpl: function (renderTpl) { |
|
this.callParent(arguments); |
|
|
|
renderTpl.renderLegend = this.doRenderLegend; |
|
} |
|
} |
|
});
|
|
|