| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 | <!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">// feature idea to enable Ajax loading and then the content// cache would actually make sense. Should we dictate that they use// data or support raw html as well?<span id='Ext-ux-RowExpander'>/**</span> * @class Ext.ux.RowExpander * @extends Ext.AbstractPlugin * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables * a second row body which expands/contracts.  The expand/contract behavior is configurable to react * on clicking of the column, double click of the row, and/or hitting enter while a row is selected. * * @ptype rowexpander */Ext.define('Ext.ux.RowExpander', {    extend: 'Ext.AbstractPlugin',    requires: [        'Ext.grid.feature.RowBody',        'Ext.grid.feature.RowWrap'    ],    alias: 'plugin.rowexpander',    rowBodyTpl: null,<span id='Ext-ux-RowExpander-cfg-expandOnEnter'>    /**</span>     * @cfg {Boolean} expandOnEnter     * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter     * key is pressed (defaults to <tt>true</tt>).     */    expandOnEnter: true,<span id='Ext-ux-RowExpander-cfg-expandOnDblClick'>    /**</span>     * @cfg {Boolean} expandOnDblClick     * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked     * (defaults to <tt>true</tt>).     */    expandOnDblClick: true,<span id='Ext-ux-RowExpander-cfg-selectRowOnExpand'>    /**</span>     * @cfg {Boolean} selectRowOnExpand     * <tt>true</tt> to select a row when clicking on the expander icon     * (defaults to <tt>false</tt>).     */    selectRowOnExpand: false,    rowBodyTrSelector: '.x-grid-rowbody-tr',    rowBodyHiddenCls: 'x-grid-row-body-hidden',    rowCollapsedCls: 'x-grid-row-collapsed',    renderer: function(value, metadata, record, rowIdx, colIdx) {        if (colIdx === 0) {            metadata.tdCls = 'x-grid-td-expander';        }        return '<div class="x-grid-row-expander">&#160;</div>';    },<span id='Ext-ux-RowExpander-event-expandbody'>    /**</span>     * @event expandbody     * <b<Fired through the grid's View</b>     * @param {HTMLElement} rowNode The &lt;tr> element which owns the expanded row.     * @param {Ext.data.Model} record The record providing the data.     * @param {HTMLElement} expandRow The &lt;tr> element containing the expanded data.     */<span id='Ext-ux-RowExpander-event-collapsebody'>    /**</span>     * @event collapsebody     * <b<Fired through the grid's View.</b>     * @param {HTMLElement} rowNode The &lt;tr> element which owns the expanded row.     * @param {Ext.data.Model} record The record providing the data.     * @param {HTMLElement} expandRow The &lt;tr> element containing the expanded data.     */    constructor: function() {        this.callParent(arguments);        var grid = this.getCmp();        this.recordsExpanded = {};        // <debug>        if (!this.rowBodyTpl) {            Ext.Error.raise("The 'rowBodyTpl' config is required and is not defined.");        }        // </debug>        // TODO: if XTemplate/Template receives a template as an arg, should        // just return it back!        var rowBodyTpl = Ext.create('Ext.XTemplate', this.rowBodyTpl),            features = [{                ftype: 'rowbody',                columnId: this.getHeaderId(),                recordsExpanded: this.recordsExpanded,                rowBodyHiddenCls: this.rowBodyHiddenCls,                rowCollapsedCls: this.rowCollapsedCls,                getAdditionalData: this.getRowBodyFeatureData,                getRowBodyContents: function(data) {                    return rowBodyTpl.applyTemplate(data);                }            },{                ftype: 'rowwrap'            }];        if (grid.features) {            grid.features = features.concat(grid.features);        } else {            grid.features = features;        }        // NOTE: features have to be added before init (before Table.initComponent)    },    init: function(grid) {        this.callParent(arguments);        this.grid = grid;        // Columns have to be added in init (after columns has been used to create the        // headerCt). Otherwise, shared column configs get corrupted, e.g., if put in the        // prototype.        this.addExpander();        grid.on('render', this.bindView, this, {single: true});        grid.on('reconfigure', this.onReconfigure, this);    },        onReconfigure: function(){        this.addExpander();    },        addExpander: function(){        this.grid.headerCt.insert(0, this.getHeaderConfig());    },    getHeaderId: function() {        if (!this.headerId) {            this.headerId = Ext.id();        }        return this.headerId;    },    getRowBodyFeatureData: function(data, idx, record, orig) {        var o = Ext.grid.feature.RowBody.prototype.getAdditionalData.apply(this, arguments),            id = this.columnId;        o.rowBodyColspan = o.rowBodyColspan - 1;        o.rowBody = this.getRowBodyContents(data);        o.rowCls = this.recordsExpanded[record.internalId] ? '' : this.rowCollapsedCls;        o.rowBodyCls = this.recordsExpanded[record.internalId] ? '' : this.rowBodyHiddenCls;        o[id + '-tdAttr'] = ' valign="top" rowspan="2" ';        if (orig[id+'-tdAttr']) {            o[id+'-tdAttr'] += orig[id+'-tdAttr'];        }        return o;    },    bindView: function() {        var view = this.getCmp().getView(),            viewEl;        if (!view.rendered) {            view.on('render', this.bindView, this, {single: true});        } else {            viewEl = view.getEl();            if (this.expandOnEnter) {                this.keyNav = Ext.create('Ext.KeyNav', viewEl, {                    'enter' : this.onEnter,                    scope: this                });            }            if (this.expandOnDblClick) {                view.on('itemdblclick', this.onDblClick, this);            }            this.view = view;        }    },    onEnter: function(e) {        var view = this.view,            ds   = view.store,            sm   = view.getSelectionModel(),            sels = sm.getSelection(),            ln   = sels.length,            i = 0,            rowIdx;        for (; i < ln; i++) {            rowIdx = ds.indexOf(sels[i]);            this.toggleRow(rowIdx);        }    },    toggleRow: function(rowIdx) {        var view = this.view,            rowNode = view.getNode(rowIdx),            row = Ext.get(rowNode),            nextBd = Ext.get(row).down(this.rowBodyTrSelector),            record = view.getRecord(rowNode),            grid = this.getCmp();        if (row.hasCls(this.rowCollapsedCls)) {            row.removeCls(this.rowCollapsedCls);            nextBd.removeCls(this.rowBodyHiddenCls);            this.recordsExpanded[record.internalId] = true;            view.refreshSize();            view.fireEvent('expandbody', rowNode, record, nextBd.dom);        } else {            row.addCls(this.rowCollapsedCls);            nextBd.addCls(this.rowBodyHiddenCls);            this.recordsExpanded[record.internalId] = false;            view.refreshSize();            view.fireEvent('collapsebody', rowNode, record, nextBd.dom);        }    },    onDblClick: function(view, cell, rowIdx, cellIndex, e) {        this.toggleRow(rowIdx);    },    getHeaderConfig: function() {        var me                = this,            toggleRow         = Ext.Function.bind(me.toggleRow, me),            selectRowOnExpand = me.selectRowOnExpand;        return {            id: this.getHeaderId(),            width: 24,            sortable: false,            resizable: false,            draggable: false,            hideable: false,            menuDisabled: true,            cls: Ext.baseCSSPrefix + 'grid-header-special',            renderer: function(value, metadata) {                metadata.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';                return '<div class="' + Ext.baseCSSPrefix + 'grid-row-expander">&#160;</div>';            },            processEvent: function(type, view, cell, recordIndex, cellIndex, e) {                if (type == "mousedown" && e.getTarget('.x-grid-row-expander')) {                    var row = e.getTarget('.x-grid-row');                    toggleRow(row);                    return selectRowOnExpand;                }            }        };    }});</pre></body></html>
 |