commit 854420ebd9ff5f450765ab93923a6e2cf8a11b86
parent ffa1cfe3cb01844f2a1f1a3a3f054ddfeba885b8
Author: Felicitus <felicitus@felicitus.org>
Date: Tue, 10 Jul 2012 03:59:01 +0200
Reworked the RemotePartComboBox, Fixes #189 and #226
Diffstat:
2 files changed, 179 insertions(+), 49 deletions(-)
diff --git a/src/frontend/js/Components/Project/ProjectPartGrid.js b/src/frontend/js/Components/Project/ProjectPartGrid.js
@@ -20,13 +20,18 @@ Ext.define('PartKeepr.ProjectPartGrid', {
xtype: 'RemotePartComboBox'
},
renderer: function (val,p,rec) {
- return rec.get("part_name");
+ return Ext.util.Format.htmlEncode(rec.get("part_name"));
}
},{
header: i18n("Remarks"), dataIndex: 'remarks',
flex: 1,
editor: {
- xtype: 'textfield'
+ xtype: 'textfield',
+ listeners: {
+ focus: function () {
+ console.log("TEXTFIELDFOCUS");
+ }
+ }
}
}],
@@ -36,7 +41,19 @@ Ext.define('PartKeepr.ProjectPartGrid', {
initComponent: function () {
this.editing = Ext.create('Ext.grid.plugin.CellEditing', {
- clicksToEdit: 1
+ clicksToEdit: 1,
+ listeners: {
+ beforeedit: function (editor, e, eOpts) {
+ if (e.field == "part_id") {
+ e.value = e.record.get("part_name");
+ var header = this.headerCt.getHeaderAtIndex(e.colIdx);
+ var edit = this.editing.getEditor(editor.record, header);
+
+ edit.field.setDisplayValue(e.record.get("part_name"));
+ }
+ },
+ scope: this
+ }
});
this.plugins = [ this.editing ];
diff --git a/src/frontend/js/Components/Widgets/RemotePartComboBox.js b/src/frontend/js/Components/Widgets/RemotePartComboBox.js
@@ -1,52 +1,164 @@
/**
- * Represents a part chooser which supports type-ahead and remote querying.
+ * A part picker with an attached grid.
*/
-Ext.define("PartKeepr.RemotePartComboBox", {
- extend : "Ext.form.field.ComboBox",
- alias : 'widget.RemotePartComboBox',
-
- // Fixed configurations for the data
- displayField : 'name',
- valueField : 'id',
- queryMode : 'remote',
- triggerAction : 'all',
-
- // Typing configuration
- typeAhead : true,
- typeAheadDelay : 100,
- minChars : 2,
-
- // Misc settings
- pageSize : 30,
- forceSelection : true,
-
- /**
- * Initializes the component. Applies a custom list configuration and an own store, then calls the ComboBox
- * superclass.
- */
- initComponent : function() {
+Ext.define("PartKeepr.RemotePartComboBox",{
+ extend:"Ext.form.field.Picker",
+ alias: 'widget.RemotePartComboBox',
+ requires:["Ext.grid.Panel"],
+ selectedValue: null,
+
+ /**
+ * Initializes the component.
+ */
+ initComponent: function(){
+ Ext.apply(this,{
+ pickerAlign:"tl-bl?",
+ editable: false
+ });
- // Custom list configuration to display additional information about the part
- this.listConfig = {
- loadingText : i18n('Searching...'),
- emptyText : i18n('No matching parts found.'),
+ /**
+ * Create the store with the default sorter "name ASC"
+ */
+ this.createStore({
+ model: 'PartKeepr.Part',
+ proxy: PartKeepr.getRESTProxy("Part"),
+ groupField: 'categoryPath',
+ sorters: [{
+ property: 'name',
+ direction:'ASC'
+ }]
+ });
+
+ this.callParent();
+ this.createPicker();
+
+ // @todo This is currently buggy due to EXTJSIV-5364.
+ this.on("focus", function () { this.onTriggerClick();}, this);
+ },
+ // Creates a store. To be called from child's initComponent
+ createStore: function (config) {
+ Ext.Object.merge(config, {
+ autoLoad: true,
+ autoSync: false, // Do not change. If true, new (empty) records would be immediately commited to the database.
+ remoteFilter: true,
+ remoteSort: true,
+ pageSize: 15});
+
+ this.store = Ext.create('Ext.data.Store', config);
+
+ // Workaround for bug http://www.sencha.com/forum/showthread.php?133767-Store.sync()-does-not-update-dirty-flag&p=607093#post607093
+ this.store.on('write', function(store, operation) {
+ var success=operation.wasSuccessful();
+ if (success) {
+ Ext.each(operation.records, function(record){
+ if (record.dirty) {
+ record.commit();
+ }
+ });
+ }
+ });
+ },
+ onTrigger1Click: function () {
+ this.onTriggerClick();
+ },
+ createPicker: function(){
+ this.partsGrid = Ext.create("PartKeepr.PartsGrid", {
+ enableTopToolbar: true,
+ editingEnabled: false,
+ store: this.store,
+ region: 'center'
+ });
+
+ this.filter = Ext.create("PartKeepr.PartFilterPanel", {
+ region: 'south',
+ floatable: false,
+ titleCollapse: true,
+ height: 225,
+ autoScroll: true,
+ store: this.store,
+ title: i18n("Part Filter"),
+ split: true,
+ collapsed: true,
+ collapsible: true
+ });
+
+ this.picker = Ext.create("Ext.panel.Panel", {
+ layout: 'border',
+ floating: true,
+ focusOnToFront: false,
+ height:300,
+ minWidth: 350,
+ shadow: false,
+ ownerCt: this.ownerCt,
+ items: [ this.partsGrid, this.filter ]
+ });
- itemTpl : Ext.create( 'Ext.XTemplate',
- '<div style="margin-bottom: 5px;">',
- '<h2>{name}</h2>',
- '<p>{categoryPath}</p>',
- '<p>{description}</p>',
- '<p>{footprintName}</p>',
- '</div>')
- };
+ this.picker.on({
+ show: function () {
+ this.partsGrid.searchField.setValue(this.getDisplayValue());
+ this.partsGrid.searchField.startSearch();
+ },
+ scope: this
+ });
+
+ this.partsGrid.on("select",
+ function (selModel, record) {
+ this.setSelectedValue(record.get("id"));
+ this.setDisplayValue(record.get("name"));
+ this.collapse();
+ }, this);
+
+ return self.picker;
+ },
+ getDisplayValue: function () {
+ return this.displayValue;
+ },
+ setSelectedValue: function (id) {
+ this.selectedValue = id;
+ },
+ getValue: function () {
+ return this.selectedValue;
+ },
+ setDisplayValue: function (value) {
+ this.setRawValue(value);
+ this.displayValue = value;
+ },
+ setValue: function () {
+
+ },
+ _selectRecords: function (r) {
+ this.picker.getView().select(r);
+ this.picker.getView().ensureVisible(r);
+ this.picker.getView().scrollIntoView(r);
+ },
+ alignPicker: function() {
+ // override the original method because otherwise the height of the treepanel would be always 0
+ var me = this,
+ picker, isAbove,
+ aboveSfx = '-above';
- // Create a separate store used for querying parts
- this.store = Ext.create("Ext.data.Store", {
- model : 'PartKeepr.Part',
- proxy : PartKeepr.getRESTProxy("Part"),
- autoLoad : true
- });
+ if (this.isExpanded) {
+ picker = me.getPicker();
+ if (me.matchFieldWidth) {
+ // Auto the height (it will be constrained by min and max width) unless there are no records to display.
+ picker.setWidth( me.bodyEl.getWidth());
+ }
+ if (picker.isFloating()) {
+ picker.alignTo(me.inputEl, me.pickerAlign, me.pickerOffset);
- this.callParent();
- }
-});
+ // add the {openCls}-above class if the picker was aligned above
+ // the field due to hitting the bottom of the viewport
+ isAbove = picker.el.getY() < me.inputEl.getY();
+ me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
+ picker.el[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
+ }
+ }
+ },
+ getErrors: function(value) {
+ if (this.getValue() === null) {
+ return [ i18n("You need to select a category")];
+ }
+
+ return [];
+ }
+});+
\ No newline at end of file