X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/grid/property/Grid.js diff --git a/src/grid/property/Grid.js b/src/grid/property/Grid.js new file mode 100644 index 00000000..8227d164 --- /dev/null +++ b/src/grid/property/Grid.js @@ -0,0 +1,353 @@ +/** + * @class Ext.grid.property.Grid + * @extends Ext.grid.Panel + * A specialized grid implementation intended to mimic the traditional property grid as typically seen in + * development IDEs. Each row in the grid represents a property of some object, and the data is stored + * as a set of name/value pairs in {@link Ext.grid.property.Property Properties}. Example usage: + *

+var grid = new Ext.grid.property.Grid({
+    title: 'Properties Grid',
+    width: 300,
+    renderTo: 'grid-ct',
+    source: {
+        "(name)": "My Object",
+        "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'),
+        "Available": false,
+        "Version": .01,
+        "Description": "A test object"
+    }
+});
+
+ * @constructor + * @param {Object} config The grid config object + */ +Ext.define('Ext.grid.property.Grid', { + + extend: 'Ext.grid.Panel', + + alternateClassName: 'Ext.grid.PropertyGrid', + + uses: [ + 'Ext.grid.plugin.CellEditing', + 'Ext.grid.property.Store', + 'Ext.grid.property.HeaderContainer', + 'Ext.XTemplate', + 'Ext.grid.CellEditor', + 'Ext.form.field.Date', + 'Ext.form.field.Text', + 'Ext.form.field.Number' + ], + + /** + * @cfg {Object} propertyNames An object containing custom property name/display name pairs. + * If specified, the display name will be shown in the name column instead of the property name. + */ + + /** + * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details). + */ + + /** + * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow + * the grid to support additional types of editable fields. By default, the grid supports strongly-typed editing + * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and + * associated with a custom input control by specifying a custom editor. The name of the editor + * type should correspond with the name of the property that will use the editor. Example usage: + *

+var grid = new Ext.grid.property.Grid({
+
+    // Custom editors for certain property names
+    customEditors: {
+        evtStart: Ext.create('Ext.form.TimeField' {selectOnFocus:true})
+    },
+
+    // Displayed name for property names in the source
+    propertyNames: {
+        evtStart: 'Start Time'
+    },
+
+    // Data object containing properties to edit
+    source: {
+        evtStart: '10:00 AM'
+    }
+});
+
+ */ + + /** + * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details). + */ + + /** + * @cfg {Object} customRenderers An object containing name/value pairs of custom renderer type definitions that allow + * the grid to support custom rendering of fields. By default, the grid supports strongly-typed rendering + * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and + * associated with the type of the value. The name of the renderer type should correspond with the name of the property + * that it will render. Example usage: + *

+var grid = Ext.create('Ext.grid.property.Grid', {
+    customRenderers: {
+        Available: function(v){
+            if (v) {
+                return 'Yes';
+            } else {
+                return 'No';
+            }
+        }
+    },
+    source: {
+        Available: true
+    }
+});
+
+ */ + + /** + * @cfg {String} valueField + * Optional. The name of the field from the property store to use as the value field name. Defaults to 'value' + * This may be useful if you do not configure the property Grid from an object, but use your own store configuration. + */ + valueField: 'value', + + /** + * @cfg {String} nameField + * Optional. The name of the field from the property store to use as the property field name. Defaults to 'name' + * This may be useful if you do not configure the property Grid from an object, but use your own store configuration. + */ + nameField: 'name', + + // private config overrides + enableColumnMove: false, + columnLines: true, + stripeRows: false, + trackMouseOver: false, + clicksToEdit: 1, + enableHdMenu: false, + + // private + initComponent : function(){ + var me = this; + + me.addCls(Ext.baseCSSPrefix + 'property-grid'); + me.plugins = me.plugins || []; + + // Enable cell editing. Inject a custom startEdit which always edits column 1 regardless of which column was clicked. + me.plugins.push(Ext.create('Ext.grid.plugin.CellEditing', { + clicksToEdit: me.clicksToEdit, + + // Inject a startEdit which always edits the value column + startEdit: function(record, column) { + // Maintainer: Do not change this 'this' to 'me'! It is the CellEditing object's own scope. + Ext.grid.plugin.CellEditing.prototype.startEdit.call(this, record, me.headerCt.child('#' + me.valueField)); + } + })); + + me.selModel = { + selType: 'cellmodel', + onCellSelect: function(position) { + if (position.column != 1) { + position.column = 1; + Ext.selection.CellModel.prototype.onCellSelect.call(this, position); + } + } + }; + me.customRenderers = me.customRenderers || {}; + me.customEditors = me.customEditors || {}; + + // Create a property.Store from the source object unless configured with a store + if (!me.store) { + me.propStore = me.store = Ext.create('Ext.grid.property.Store', me, me.source); + } + + me.store.sort('name', 'ASC'); + me.columns = Ext.create('Ext.grid.property.HeaderContainer', me, me.store); + + me.addEvents( + /** + * @event beforepropertychange + * Fires before a property value changes. Handlers can return false to cancel the property change + * (this will internally call {@link Ext.data.Record#reject} on the property's record). + * @param {Object} source The source data object for the grid (corresponds to the same object passed in + * as the {@link #source} config property). + * @param {String} recordId The record's id in the data store + * @param {Mixed} value The current edited property value + * @param {Mixed} oldValue The original property value prior to editing + */ + 'beforepropertychange', + /** + * @event propertychange + * Fires after a property value has changed. + * @param {Object} source The source data object for the grid (corresponds to the same object passed in + * as the {@link #source} config property). + * @param {String} recordId The record's id in the data store + * @param {Mixed} value The current edited property value + * @param {Mixed} oldValue The original property value prior to editing + */ + 'propertychange' + ); + me.callParent(); + + // Inject a custom implementation of walkCells which only goes up or down + me.getView().walkCells = this.walkCells; + + // Set up our default editor set for the 4 atomic data types + me.editors = { + 'date' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Date', {selectOnFocus: true})}), + 'string' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Text', {selectOnFocus: true})}), + 'number' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Number', {selectOnFocus: true})}), + 'boolean' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.ComboBox', { + editable: false, + store: [[ true, me.headerCt.trueText ], [false, me.headerCt.falseText ]] + })}) + }; + + // Track changes to the data so we can fire our events. + this.store.on('update', me.onUpdate, me); + }, + + // private + onUpdate : function(store, record, operation) { + var me = this, + v, oldValue; + + if (operation == Ext.data.Model.EDIT) { + v = record.get(me.valueField); + oldValue = record.modified.value; + if (me.fireEvent('beforepropertychange', me.source, record.id, v, oldValue) !== false) { + if (me.source) { + me.source[record.id] = v; + } + record.commit(); + me.fireEvent('propertychange', me.source, record.id, v, oldValue); + } else { + record.reject(); + } + } + }, + + // Custom implementation of walkCells which only goes up and down. + walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) { + if (direction == 'left') { + direction = 'up'; + } else if (direction == 'right') { + direction = 'down'; + } + var pos = Ext.view.Table.prototype.walkCells.call(this, pos, direction, e, preventWrap, verifierFn, scope); + if (!pos.column) { + pos.column = 1; + } + return pos; + }, + + // private + // returns the correct editor type for the property type, or a custom one keyed by the property name + getCellEditor : function(record, column) { + var me = this, + propName = record.get(me.nameField), + val = record.get(me.valueField), + editor = me.customEditors[propName]; + + // A custom editor was found. If not already wrapped with a CellEditor, wrap it, and stash it back + // If it's not even a Field, just a config object, instantiate it before wrapping it. + if (editor) { + if (!(editor instanceof Ext.grid.CellEditor)) { + if (!(editor instanceof Ext.form.field.Base)) { + editor = Ext.ComponentManager.create(editor, 'textfield'); + } + editor = me.customEditors[propName] = Ext.create('Ext.grid.CellEditor', { field: editor }); + } + } else if (Ext.isDate(val)) { + editor = me.editors.date; + } else if (Ext.isNumber(val)) { + editor = me.editors.number; + } else if (Ext.isBoolean(val)) { + editor = me.editors['boolean']; + } else { + editor = me.editors.string; + } + + // Give the editor a unique ID because the CellEditing plugin caches them + editor.editorId = propName; + return editor; + }, + + beforeDestroy: function() { + var me = this; + me.callParent(); + me.destroyEditors(me.editors); + me.destroyEditors(me.customEditors); + delete me.source; + }, + + destroyEditors: function (editors) { + for (var ed in editors) { + Ext.destroy(editors[ed]); + } + }, + + /** + * Sets the source data object containing the property data. The data object can contain one or more name/value + * pairs representing all of the properties of an object to display in the grid, and this data will automatically + * be loaded into the grid's {@link #store}. The values should be supplied in the proper data type if needed, + * otherwise string type will be assumed. If the grid already contains data, this method will replace any + * existing data. See also the {@link #source} config value. Example usage: + *

+grid.setSource({
+    "(name)": "My Object",
+    "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'),  // date type
+    "Available": false,  // boolean type
+    "Version": .01,      // decimal type
+    "Description": "A test object"
+});
+
+ * @param {Object} source The data object + */ + setSource: function(source) { + this.source = source; + this.propStore.setSource(source); + }, + + /** + * Gets the source data object containing the property data. See {@link #setSource} for details regarding the + * format of the data object. + * @return {Object} The data object + */ + getSource: function() { + return this.propStore.getSource(); + }, + + /** + * Sets the value of a property. + * @param {String} prop The name of the property to set + * @param {Mixed} value The value to test + * @param {Boolean} create (Optional) True to create the property if it doesn't already exist. Defaults to false. + */ + setProperty: function(prop, value, create) { + this.propStore.setValue(prop, value, create); + }, + + /** + * Removes a property from the grid. + * @param {String} prop The name of the property to remove + */ + removeProperty: function(prop) { + this.propStore.remove(prop); + } + + /** + * @cfg store + * @hide + */ + /** + * @cfg colModel + * @hide + */ + /** + * @cfg cm + * @hide + */ + /** + * @cfg columns + * @hide + */ +}); \ No newline at end of file