Go to most recent revision | Blame | Last modification | View Log | RSS feed
// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
* CellActions plugin for Ext grid
*
* Contains renderer for an icon and fires events when icon is clicked
*
* @author Ing. Jozef Sakáloš
* @date 22. March 2008
* @version $Id: Ext.ux.grid.CellActions.js 253 2008-05-11 09:31:48Z jozo $
*
* @license Ext.ux.grid.CellActions is licensed under the terms of
* the Open Source LGPL 3.0 license. Commercial use is permitted to the extent
* that the code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/
/**
* The following css is required:
*
* .ux-cell-value {
* position:relative;
* zoom:1;
* }
* .ux-cell-actions {
* position:absolute;
* right:0;
* top:-2px;
* }
* .ux-cell-actions-left {
* left:0;
* top:-2px;
* }
* .ux-cell-action {
* width:16px;
* height:16px;
* float:left;
* cursor:pointer;
* margin: 0 0 0 4px;
* }
* .ux-cell-actions-left .ux-cell-action {
* margin: 0 4px 0 0;
* }
*/
/*global Ext */
Ext.ns('Ext.ux.grid');
// constructor and cellActions documentation
// {{{
/**
* @class Ext.ux.grid.CellActions
* @extends Ext.util.Observable
* @constructor
*
* CellActions plugin causes that column model recognizes the config property cellAcions
* that is the array of configuration objects for that column. The documentationi follows.
*
* THE FOLLOWING CONFIG OPTIONS ARE FOR COLUMN MODEL COLUMN, NOT FOR CellActions ITSELF.
*
* @cfg {Array} cellActions Mandatory. Array of action configuration objects. The following
* configuration options of action are recognized:
*
* - @cfg {Function} callback Optional. Function to call if the action icon is clicked.
* This function is called with same signature as action event and in its original scope.
* If you need to call it in different scope or with another signature use
* createCallback or createDelegate functions. Works for statically defined actions. Use
* callbacks configuration options for store bound actions.
*
* - @cfg {Function} cb Shortcut for callback.
*
* - @cfg {String} iconIndex Optional, however either iconIndex or iconCls must be
* configured. Field name of the field of the grid store record that contains
* css class of the icon to show. If configured, shown icons can vary depending
* of the value of this field.
*
* - @cfg {String} iconCls. css class of the icon to show. It is ignored if iconIndex is
* configured. Use this if you want static icons that are not base on the values in the record.
*
* - @cfg {String} qtipIndex Optional. Field name of the field of the grid store record that
* contains tooltip text. If configured, the tooltip texts are taken from the store.
*
* - @cfg {String} tooltip Optional. Tooltip text to use as icon tooltip. It is ignored if
* qtipIndex is configured. Use this if you want static tooltips that are not taken from the store.
*
* - @cfg {String} qtip Synonym for tooltip
*
* - @cfg {String} style Optional. Style to apply to action icon container.
*/
Ext.ux.grid.CellActions = function(config) {
Ext.apply(this, config);
this.addEvents(
/**
* @event action
* Fires when user clicks a cell action
* @param {Ext.grid.GridPanel} grid
* @param {Ext.data.Record} record Record containing data of clicked cell
* @param {String} action Action clicked (equals iconCls);
* @param {Mixed} value Value of the clicke cell
* @param {String} dataIndex as specified in column model
* @param {Number} rowIndex Index of row clicked
* @param {Number} colIndex Incex of col clicked
*/
'action'
/**
* @event beforeaction
* Fires when user clicks a cell action but before action event is fired. Return false to cancel the action;
* @param {Ext.grid.GridPanel} grid
* @param {Ext.data.Record} record Record containing data of clicked cell
* @param {String} action Action clicked (equals iconCls);
* @param {Mixed} value Value of the clicke cell
* @param {String} dataIndex as specified in column model
* @param {Number} rowIndex Index of row clicked
* @param {Number} colIndex Incex of col clicked
*/
,'beforeaction'
);
// call parent
Ext.ux.grid.CellActions.superclass.constructor.call(this);
}; // eo constructor
// }}}
Ext.extend(Ext.ux.grid.CellActions, Ext.util.Observable, {
/**
* @cfg {String} actionEvnet Event to trigger actions, e.g. click, dblclick, mouseover (defaults to 'click')
*/
actionEvent:'click'
/**
* @cfg {Number} actionWidth Width of action icon in pixels. Has effect only if align:'left'
*/
,actionWidth:20
/**
* @cfg {String} align Set to 'left' to put action icons before the cell text. (defaults to undefined, meaning right)
*/
/**
* @private
* @cfg {String} tpl Template for cell with actions
*/
,tpl:'<div class="ux-cell-value" style="padding-left:{padding}px">'
+'<tpl if="\'left\'!==align">{value}</tpl>'
+'<div class="ux-cell-actions<tpl if="\'left\'===align"> ux-cell-actions-left</tpl>" style="width:{width}px">'
+'<tpl for="actions"><div class="ux-cell-action {cls}" qtip="{qtip}" style="{style}"> </div></tpl>'
+'</div>'
+'<tpl if="\'left\'===align">{value}</tpl>'
+'<div>'
/**
* Called at the end of processActions. Override this if you need it.
* @param {Object} c Column model configuration object
* @param {Object} data See this.processActions method for details
*/
,userProcessing:Ext.emptyFn
// {{{
/**
* Init function
* @param {Ext.grid.GridPanel} grid Grid this plugin is in
*/
,init:function(grid) {
this.grid = grid;
// grid.on({scope:this, render:this.onRenderGrid});
grid.afterRender = grid.afterRender.createSequence(this.onRenderGrid, this);
var cm = this.grid.getColumnModel();
Ext.each(cm.config, function(c, idx) {
if('object' === typeof c.cellActions) {
c.origRenderer = cm.getRenderer(idx);
c.renderer = this.renderActions.createDelegate(this);
}
}, this);
} // eo function init
// }}}
// {{{
/**
* grid render event handler, install actionEvent handler on view.mainBody
* @private
*/
,onRenderGrid:function() {
// install click event handler on view mainBody
this.view = this.grid.getView();
var cfg = {scope:this};
cfg[this.actionEvent] = this.onClick;
this.view.mainBody.on(cfg);
} // eo function onRender
// }}}
// {{{
/**
* Returns data to apply to template. Override this if needed
* @param {Mixed} value
* @param {Object} cell object to set some attributes of the grid cell
* @param {Ext.data.Record} record from which the data is extracted
* @param {Number} row row index
* @param {Number} col col index
* @param {Ext.data.Store} store object from which the record is extracted
* @returns {Object} data to apply to template
*/
,getData:function(value, cell, record, row, col, store) {
return record.data || {};
}
// }}}
// {{{
/**
* replaces (but calls) the original renderer from column model
* @private
* @param {Mixed} value
* @param {Object} cell object to set some attributes of the grid cell
* @param {Ext.data.Record} record from which the data is extracted
* @param {Number} row row index
* @param {Number} col col index
* @param {Ext.data.Store} store object from which the record is extracted
* @returns {String} markup of cell content
*/
,renderActions:function(value, cell, record, row, col, store) {
// get column config from column model
var c = this.grid.getColumnModel().config[col];
// get output of the original renderer
var val = c.origRenderer(value, cell, record, row, col, store);
// get actions template if we need but don't have one
if(c.cellActions && !c.actionsTpl) {
c.actionsTpl = this.processActions(c);
c.actionsTpl.compile();
}
// return original renderer output if we don't have actions
else if(!c.cellActions) {
return val;
}
// get and return final markup
var data = this.getData.apply(this, arguments);
data.value = val;
return c.actionsTpl.apply(data);
} // eo function renderActions
// }}}
// {{{
/**
* processes the actions configs from column model column, saves callbacks and creates template
* @param {Object} c column model config of one column
* @private
*/
,processActions:function(c) {
// callbacks holder
this.callbacks = this.callbacks || {};
// data for intermediate template
var data = {
align:this.align || 'right'
,width:this.actionWidth * c.cellActions.length
,padding:'left' === this.align ? this.actionWidth * c.cellActions.length : 0
,value:'{value}'
,actions:[]
};
// cellActions loop
Ext.each(c.cellActions, function(a, i) {
// save callback
if(a.iconCls && 'function' === typeof (a.callback || a.cb)) {
this.callbacks[a.iconCls] = a.callback || a.cb;
}
// data for intermediate xtemplate action
var o = {
cls:a.iconIndex ? '{' + a.iconIndex + '}' : (a.iconCls ? a.iconCls : '')
,qtip:a.qtipIndex ? '{' + a.qtipIndex + '}' : (a.tooltip || a.qtip ? a.tooltip || a.qtip : '')
,style:a.style ? a.style : ''
};
data.actions.push(o);
}, this); // eo cellActions loop
this.userProcessing(c, data);
// get and return final template
var xt = new Ext.XTemplate(this.tpl);
return new Ext.Template(xt.apply(data));
} // eo function processActions
// }}}
// {{{
/**
* Grid body actionEvent event handler
* @private
*/
,onClick:function(e, target) {
// collect all variables for callback and/or events
var t = e.getTarget('div.ux-cell-action');
var row = e.getTarget('.x-grid3-row');
var col = this.view.findCellIndex(target.parentNode.parentNode);
var c = this.grid.getColumnModel().config[col];
var record, dataIndex, value, action;
if(t) {
record = this.grid.store.getAt(row.rowIndex);
dataIndex = c.dataIndex;
value = record.get(dataIndex);
action = t.className.replace(/ux-cell-action /, '');
}
// check if we've collected all necessary variables
if(false !== row && false !== col && record && dataIndex && action) {
// call callback if any
if(this.callbacks && 'function' === typeof this.callbacks[action]) {
this.callbacks[action](this.grid, record, action, value, row.rowIndex, col);
}
// fire events
if(true !== this.eventsSuspended && false === this.fireEvent('beforeaction', this.grid, record, action, value, dataIndex, row.rowIndex, col)) {
return;
}
else if(true !== this.eventsSuspended) {
this.fireEvent('action', this.grid, record, action, value, dataIndex, row.rowIndex, col);
}
}
} // eo function onClick
// }}}
});
// register xtype
Ext.reg('cellactions', Ext.ux.grid.CellActions);
// eof