<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-ux-form-MultiSelect'>/**
|
</span> * A control that allows selection of multiple items in a list.
|
*/
|
Ext.define('Ext.ux.form.MultiSelect', {
|
|
extend: 'Ext.form.FieldContainer',
|
|
mixins: {
|
bindable: 'Ext.util.Bindable',
|
field: 'Ext.form.field.Field'
|
},
|
|
alternateClassName: 'Ext.ux.Multiselect',
|
alias: ['widget.multiselectfield', 'widget.multiselect'],
|
|
requires: ['Ext.panel.Panel', 'Ext.view.BoundList', 'Ext.layout.container.Fit'],
|
|
uses: ['Ext.view.DragZone', 'Ext.view.DropZone'],
|
|
<span id='Ext-ux-form-MultiSelect-cfg-layout'> layout: 'anchor',
|
</span>
|
<span id='Ext-ux-form-MultiSelect-cfg-dragGroup'> /**
|
</span> * @cfg {String} [dragGroup=""] The ddgroup name for the MultiSelect DragZone.
|
*/
|
|
<span id='Ext-ux-form-MultiSelect-cfg-dropGroup'> /**
|
</span> * @cfg {String} [dropGroup=""] The ddgroup name for the MultiSelect DropZone.
|
*/
|
|
<span id='Ext-ux-form-MultiSelect-cfg-title'> /**
|
</span> * @cfg {String} [title=""] A title for the underlying panel.
|
*/
|
|
<span id='Ext-ux-form-MultiSelect-cfg-ddReorder'> /**
|
</span> * @cfg {Boolean} [ddReorder=false] Whether the items in the MultiSelect list are drag/drop reorderable.
|
*/
|
ddReorder: false,
|
|
<span id='Ext-ux-form-MultiSelect-cfg-tbar'> /**
|
</span> * @cfg {Object/Array} tbar An optional toolbar to be inserted at the top of the control's selection list.
|
* This can be a {@link Ext.toolbar.Toolbar} object, a toolbar config, or an array of buttons/button configs
|
* to be added to the toolbar. See {@link Ext.panel.Panel#tbar}.
|
*/
|
|
<span id='Ext-ux-form-MultiSelect-cfg-appendOnly'> /**
|
</span> * @cfg {String} [appendOnly=false] `true` if the list should only allow append drops when drag/drop is enabled.
|
* This is useful for lists which are sorted.
|
*/
|
appendOnly: false,
|
|
<span id='Ext-ux-form-MultiSelect-cfg-displayField'> /**
|
</span> * @cfg {String} [displayField="text"] Name of the desired display field in the dataset.
|
*/
|
displayField: 'text',
|
|
<span id='Ext-ux-form-MultiSelect-cfg-valueField'> /**
|
</span> * @cfg {String} [valueField="text"] Name of the desired value field in the dataset.
|
*/
|
|
<span id='Ext-ux-form-MultiSelect-cfg-allowBlank'> /**
|
</span> * @cfg {Boolean} [allowBlank=true] `false` to require at least one item in the list to be selected, `true` to allow no
|
* selection.
|
*/
|
allowBlank: true,
|
|
<span id='Ext-ux-form-MultiSelect-cfg-minSelections'> /**
|
</span> * @cfg {Number} [minSelections=0] Minimum number of selections allowed.
|
*/
|
minSelections: 0,
|
|
<span id='Ext-ux-form-MultiSelect-cfg-maxSelections'> /**
|
</span> * @cfg {Number} [maxSelections=Number.MAX_VALUE] Maximum number of selections allowed.
|
*/
|
maxSelections: Number.MAX_VALUE,
|
|
<span id='Ext-ux-form-MultiSelect-cfg-blankText'> /**
|
</span> * @cfg {String} [blankText="This field is required"] Default text displayed when the control contains no items.
|
*/
|
blankText: 'This field is required',
|
|
<span id='Ext-ux-form-MultiSelect-cfg-minSelectionsText'> /**
|
</span> * @cfg {String} [minSelectionsText="Minimum {0}item(s) required"]
|
* Validation message displayed when {@link #minSelections} is not met.
|
* The {0} token will be replaced by the value of {@link #minSelections}.
|
*/
|
minSelectionsText: 'Minimum {0} item(s) required',
|
|
<span id='Ext-ux-form-MultiSelect-cfg-maxSelectionsText'> /**
|
</span> * @cfg {String} [maxSelectionsText="Maximum {0}item(s) allowed"]
|
* Validation message displayed when {@link #maxSelections} is not met
|
* The {0} token will be replaced by the value of {@link #maxSelections}.
|
*/
|
maxSelectionsText: 'Maximum {0} item(s) required',
|
|
<span id='Ext-ux-form-MultiSelect-cfg-delimiter'> /**
|
</span> * @cfg {String} [delimiter=","] The string used to delimit the selected values when {@link #getSubmitValue submitting}
|
* the field as part of a form. If you wish to have the selected values submitted as separate
|
* parameters rather than a single delimited parameter, set this to `null`.
|
*/
|
delimiter: ',',
|
|
<span id='Ext-ux-form-MultiSelect-cfg-String'> /**
|
</span> * @cfg String [dragText="{0} Item{1}"] The text to show while dragging items.
|
* {0} will be replaced by the number of items. {1} will be replaced by the plural
|
* form if there is more than 1 item.
|
*/
|
dragText: '{0} Item{1}',
|
|
<span id='Ext-ux-form-MultiSelect-cfg-store'> /**
|
</span> * @cfg {Ext.data.Store/Array} store The data source to which this MultiSelect is bound (defaults to `undefined`).
|
* Acceptable values for this property are:
|
* <div class="mdetail-params"><ul>
|
* <li><b>any {@link Ext.data.Store Store} subclass</b></li>
|
* <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally.
|
* <div class="mdetail-params"><ul>
|
* <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
|
* A 1-dimensional array will automatically be expanded (each array item will be the combo
|
* {@link #valueField value} and {@link #displayField text})</div></li>
|
* <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
|
* For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
|
* {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}.
|
* </div></li></ul></div></li></ul></div>
|
*/
|
|
ignoreSelectChange: 0,
|
|
<span id='Ext-ux-form-MultiSelect-cfg-listConfig'> /**
|
</span> * @cfg {Object} listConfig
|
* An optional set of configuration properties that will be passed to the {@link Ext.view.BoundList}'s constructor.
|
* Any configuration that is valid for BoundList can be included.
|
*/
|
|
initComponent: function(){
|
var me = this;
|
|
me.bindStore(me.store, true);
|
if (me.store.autoCreated) {
|
me.valueField = me.displayField = 'field1';
|
if (!me.store.expanded) {
|
me.displayField = 'field2';
|
}
|
}
|
|
if (!Ext.isDefined(me.valueField)) {
|
me.valueField = me.displayField;
|
}
|
me.items = me.setupItems();
|
|
|
me.callParent();
|
me.initField();
|
me.addEvents('drop');
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-setupItems'> setupItems: function() {
|
</span> var me = this;
|
|
me.boundList = Ext.create('Ext.view.BoundList', Ext.apply({
|
anchor: 'none 100%',
|
deferInitialRefresh: false,
|
border: 1,
|
multiSelect: true,
|
store: me.store,
|
displayField: me.displayField,
|
disabled: me.disabled
|
}, me.listConfig));
|
me.boundList.getSelectionModel().on('selectionchange', me.onSelectChange, me);
|
|
// Only need to wrap the BoundList in a Panel if we have a title.
|
if (!me.title) {
|
return me.boundList;
|
}
|
|
// Wrap to add a title
|
me.boundList.border = false;
|
return {
|
border: true,
|
anchor: 'none 100%',
|
layout: 'anchor',
|
title: me.title,
|
tbar: me.tbar,
|
items: me.boundList
|
};
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-onSelectChange'> onSelectChange: function(selModel, selections){
|
</span> if (!this.ignoreSelectChange) {
|
this.setValue(selections);
|
}
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-getSelected'> getSelected: function(){
|
</span> return this.boundList.getSelectionModel().getSelection();
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-isEqual'> // compare array values
|
</span> isEqual: function(v1, v2) {
|
var fromArray = Ext.Array.from,
|
i = 0,
|
len;
|
|
v1 = fromArray(v1);
|
v2 = fromArray(v2);
|
len = v1.length;
|
|
if (len !== v2.length) {
|
return false;
|
}
|
|
for(; i < len; i++) {
|
if (v2[i] !== v1[i]) {
|
return false;
|
}
|
}
|
|
return true;
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-afterRender'> afterRender: function(){
|
</span> var me = this,
|
records;
|
|
me.callParent();
|
if (me.selectOnRender) {
|
records = me.getRecordsForValue(me.value);
|
if (records.length) {
|
++me.ignoreSelectChange;
|
me.boundList.getSelectionModel().select(records);
|
--me.ignoreSelectChange;
|
}
|
delete me.toSelect;
|
}
|
|
if (me.ddReorder && !me.dragGroup && !me.dropGroup){
|
me.dragGroup = me.dropGroup = 'MultiselectDD-' + Ext.id();
|
}
|
|
if (me.draggable || me.dragGroup){
|
me.dragZone = Ext.create('Ext.view.DragZone', {
|
view: me.boundList,
|
ddGroup: me.dragGroup,
|
dragText: me.dragText
|
});
|
}
|
if (me.droppable || me.dropGroup){
|
me.dropZone = Ext.create('Ext.view.DropZone', {
|
view: me.boundList,
|
ddGroup: me.dropGroup,
|
handleNodeDrop: function(data, dropRecord, position) {
|
var view = this.view,
|
store = view.getStore(),
|
records = data.records,
|
index;
|
|
// remove the Models from the source Store
|
data.view.store.remove(records);
|
|
index = store.indexOf(dropRecord);
|
if (position === 'after') {
|
index++;
|
}
|
store.insert(index, records);
|
view.getSelectionModel().select(records);
|
me.fireEvent('drop', me, records);
|
}
|
});
|
}
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-isValid'> isValid : function() {
|
</span> var me = this,
|
disabled = me.disabled,
|
validate = me.forceValidation || !disabled;
|
|
|
return validate ? me.validateValue(me.value) : disabled;
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-validateValue'> validateValue: function(value) {
|
</span> var me = this,
|
errors = me.getErrors(value),
|
isValid = Ext.isEmpty(errors);
|
|
if (!me.preventMark) {
|
if (isValid) {
|
me.clearInvalid();
|
} else {
|
me.markInvalid(errors);
|
}
|
}
|
|
return isValid;
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-markInvalid'> markInvalid : function(errors) {
|
</span> // Save the message and fire the 'invalid' event
|
var me = this,
|
oldMsg = me.getActiveError();
|
me.setActiveErrors(Ext.Array.from(errors));
|
if (oldMsg !== me.getActiveError()) {
|
me.updateLayout();
|
}
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-clearInvalid'> /**
|
</span> * Clear any invalid styles/messages for this field.
|
*
|
* __Note:__ this method does not cause the Field's {@link #validate} or {@link #isValid} methods to return `true`
|
* if the value does not _pass_ validation. So simply clearing a field's errors will not necessarily allow
|
* submission of forms submitted with the {@link Ext.form.action.Submit#clientValidation} option set.
|
*/
|
clearInvalid : function() {
|
// Clear the message and fire the 'valid' event
|
var me = this,
|
hadError = me.hasActiveError();
|
me.unsetActiveError();
|
if (hadError) {
|
me.updateLayout();
|
}
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-getSubmitData'> getSubmitData: function() {
|
</span> var me = this,
|
data = null,
|
val;
|
if (!me.disabled && me.submitValue && !me.isFileUpload()) {
|
val = me.getSubmitValue();
|
if (val !== null) {
|
data = {};
|
data[me.getName()] = val;
|
}
|
}
|
return data;
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-getSubmitValue'> /**
|
</span> * Returns the value that would be included in a standard form submit for this field.
|
*
|
* @return {String} The value to be submitted, or `null`.
|
*/
|
getSubmitValue: function() {
|
var me = this,
|
delimiter = me.delimiter,
|
val = me.getValue();
|
|
return Ext.isString(delimiter) ? val.join(delimiter) : val;
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-getValue'> getValue: function(){
|
</span> return this.value || [];
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-getRecordsForValue'> getRecordsForValue: function(value){
|
</span> var me = this,
|
records = [],
|
all = me.store.getRange(),
|
valueField = me.valueField,
|
i = 0,
|
allLen = all.length,
|
rec,
|
j,
|
valueLen;
|
|
for (valueLen = value.length; i < valueLen; ++i) {
|
for (j = 0; j < allLen; ++j) {
|
rec = all[j];
|
if (rec.get(valueField) == value[i]) {
|
records.push(rec);
|
}
|
}
|
}
|
|
return records;
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-setupValue'> setupValue: function(value){
|
</span> var delimiter = this.delimiter,
|
valueField = this.valueField,
|
i = 0,
|
out,
|
len,
|
item;
|
|
if (Ext.isDefined(value)) {
|
if (delimiter && Ext.isString(value)) {
|
value = value.split(delimiter);
|
} else if (!Ext.isArray(value)) {
|
value = [value];
|
}
|
|
for (len = value.length; i < len; ++i) {
|
item = value[i];
|
if (item && item.isModel) {
|
value[i] = item.get(valueField);
|
}
|
}
|
out = Ext.Array.unique(value);
|
} else {
|
out = [];
|
}
|
return out;
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-setValue'> setValue: function(value){
|
</span> var me = this,
|
selModel = me.boundList.getSelectionModel(),
|
store = me.store;
|
|
// Store not loaded yet - we cannot set the value
|
if (!store.getCount()) {
|
store.on({
|
load: Ext.Function.bind(me.setValue, me, [value]),
|
single: true
|
});
|
return;
|
}
|
|
value = me.setupValue(value);
|
me.mixins.field.setValue.call(me, value);
|
|
if (me.rendered) {
|
++me.ignoreSelectChange;
|
selModel.deselectAll();
|
selModel.select(me.getRecordsForValue(value));
|
--me.ignoreSelectChange;
|
} else {
|
me.selectOnRender = true;
|
}
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-clearValue'> clearValue: function(){
|
</span> this.setValue([]);
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-onEnable'> onEnable: function(){
|
</span> var list = this.boundList;
|
this.callParent();
|
if (list) {
|
list.enable();
|
}
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-onDisable'> onDisable: function(){
|
</span> var list = this.boundList;
|
this.callParent();
|
if (list) {
|
list.disable();
|
}
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-getErrors'> getErrors : function(value) {
|
</span> var me = this,
|
format = Ext.String.format,
|
errors = [],
|
numSelected;
|
|
value = Ext.Array.from(value || me.getValue());
|
numSelected = value.length;
|
|
if (!me.allowBlank && numSelected < 1) {
|
errors.push(me.blankText);
|
}
|
if (numSelected < me.minSelections) {
|
errors.push(format(me.minSelectionsText, me.minSelections));
|
}
|
if (numSelected > me.maxSelections) {
|
errors.push(format(me.maxSelectionsText, me.maxSelections));
|
}
|
return errors;
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-onDestroy'> onDestroy: function(){
|
</span> var me = this;
|
|
me.bindStore(null);
|
Ext.destroy(me.dragZone, me.dropZone);
|
me.callParent();
|
},
|
|
<span id='Ext-ux-form-MultiSelect-method-onBindStore'> onBindStore: function(store){
|
</span> var boundList = this.boundList;
|
|
if (boundList) {
|
boundList.bindStore(store);
|
}
|
}
|
|
});
|
</pre>
|
</body>
|
</html>
|