GroupingView.js
上传用户:shuoshiled
上传日期:2018-01-28
资源大小:10124k
文件大小:19k
源码类别:

中间件编程

开发平台:

JavaScript

  1. /*!  * Ext JS Library 3.0.0  * Copyright(c) 2006-2009 Ext JS, LLC  * licensing@extjs.com  * http://www.extjs.com/license  */ /**
  2.  * @class Ext.grid.GroupingView
  3.  * @extends Ext.grid.GridView
  4.  * Adds the ability for single level grouping to the grid. A {@link Ext.data.GroupingStore GroupingStore}
  5.  * must be used to enable grouping.  Some grouping characteristics may also be configured at the
  6.  * {@link Ext.grid.Column Column level}<div class="mdetail-params"><ul>
  7.  * <li><code>{@link Ext.grid.Column#emptyGroupText emptyGroupText}</li>
  8.  * <li><code>{@link Ext.grid.Column#groupable groupable}</li>
  9.  * <li><code>{@link Ext.grid.Column#groupName groupName}</li>
  10.  * <li><code>{@link Ext.grid.Column#groupRender groupRender}</li>
  11.  * </ul></div>
  12.  * <p>Sample usage:</p>
  13.  * <pre><code>
  14. var grid = new Ext.grid.GridPanel({
  15.     // A groupingStore is required for a GroupingView
  16.     store: new {@link Ext.data.GroupingStore}({
  17.         autoDestroy: true,
  18.         reader: reader,
  19.         data: xg.dummyData,
  20.         sortInfo: {field: 'company', direction: 'ASC'},
  21.         {@link Ext.data.GroupingStore#groupOnSort groupOnSort}: true,
  22.         {@link Ext.data.GroupingStore#remoteGroup remoteGroup}: true,
  23.         {@link Ext.data.GroupingStore#groupField groupField}: 'industry'
  24.     }),
  25.     colModel: new {@link Ext.grid.ColumnModel}({
  26.         columns:[
  27.             {id:'company',header: 'Company', width: 60, dataIndex: 'company'},
  28.             // {@link Ext.grid.Column#groupable groupable}, {@link Ext.grid.Column#groupName groupName}, {@link Ext.grid.Column#groupRender groupRender} are also configurable at column level
  29.             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price', {@link Ext.grid.Column#groupable groupable}: false},
  30.             {header: 'Change', dataIndex: 'change', renderer: Ext.util.Format.usMoney},
  31.             {header: 'Industry', dataIndex: 'industry'},
  32.             {header: 'Last Updated', renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
  33.         ],
  34.         defaults: {
  35.             sortable: true,
  36.             menuDisabled: false,
  37.             width: 20
  38.         }
  39.     }),
  40.     view: new Ext.grid.GroupingView({
  41.         {@link Ext.grid.GridView#forceFit forceFit}: true,
  42.         // custom grouping text template to display the number of items per group
  43.         {@link #groupTextTpl}: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
  44.     }),
  45.     frame:true,
  46.     width: 700,
  47.     height: 450,
  48.     collapsible: true,
  49.     animCollapse: false,
  50.     title: 'Grouping Example',
  51.     iconCls: 'icon-grid',
  52.     renderTo: document.body
  53. });
  54.  * </code></pre>
  55.  * @constructor
  56.  * @param {Object} config
  57.  */
  58. Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {
  59.     /**
  60.      * @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column
  61.      * (defaults to 'Group By This Field').
  62.      */
  63.     groupByText : 'Group By This Field',
  64.     /**
  65.      * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping
  66.      * (defaults to 'Show in Groups').
  67.      */
  68.     showGroupsText : 'Show in Groups',
  69.     /**
  70.      * @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)
  71.      */
  72.     hideGroupedColumn : false,
  73.     /**
  74.      * @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value
  75.      * in the group header line.  The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>
  76.      * (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the
  77.      * {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).
  78.      */
  79.     showGroupName : true,
  80.     /**
  81.      * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)
  82.      */
  83.     startCollapsed : false,
  84.     /**
  85.      * @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)
  86.      */
  87.     enableGrouping : true,
  88.     /**
  89.      * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)
  90.      */
  91.     enableGroupingMenu : true,
  92.     /**
  93.      * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)
  94.      */
  95.     enableNoGroups : true,
  96.     /**
  97.      * @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).
  98.      * May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.
  99.      */
  100.     emptyGroupText : '(None)',
  101.     /**
  102.      * @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)
  103.      */
  104.     ignoreAdd : false,
  105.     /**
  106.      * @cfg {String} groupTextTpl The template used to render the group header (defaults to <tt>'{text}'</tt>).
  107.      * This is used to format an object which contains the following properties:
  108.      * <div class="mdetail-params"><ul>
  109.      * <li><b>group</b> : String<p class="sub-desc">The <i>rendered</i> value of the group field.
  110.      * By default this is the unchanged value of the group field. If a <tt><b>{@link Ext.grid.Column#groupRenderer groupRenderer}</b></tt>
  111.      * is specified, it is the result of a call to that function.</p></li>
  112.      * <li><b>gvalue</b> : Object<p class="sub-desc">The <i>raw</i> value of the group field.</p></li>
  113.      * <li><b>text</b> : String<p class="sub-desc">The configured header (as described in <tt>{@link #showGroupName})</tt>
  114.      * if <tt>{@link #showGroupName}</tt> is <tt>true</tt>) plus the <i>rendered</i> group field value.</p></li>
  115.      * <li><b>groupId</b> : String<p class="sub-desc">A unique, generated ID which is applied to the
  116.      * View Element which contains the group.</p></li>
  117.      * <li><b>startRow</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
  118.      * <li><b>rs</b> : Array<p class="sub-desc">Contains a single element: The Record providing the data
  119.      * for the row which caused group change.</p></li>
  120.      * <li><b>cls</b> : String<p class="sub-desc">The generated class name string to apply to the group header Element.</p></li>
  121.      * <li><b>style</b> : String<p class="sub-desc">The inline style rules to apply to the group header Element.</p></li>
  122.      * </ul></div></p>
  123.      * See {@link Ext.XTemplate} for information on how to format data using a template. Possible usage:<pre><code>
  124. var grid = new Ext.grid.GridPanel({
  125.     ...
  126.     view: new Ext.grid.GroupingView({
  127.         groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
  128.     }),
  129. });
  130.      * </code></pre>
  131.      */
  132.     groupTextTpl : '{text}',
  133.     /**
  134.      * @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for
  135.      * each column.
  136.      */
  137.     // private
  138.     gidSeed : 1000,
  139.     // private
  140.     initTemplates : function(){
  141.         Ext.grid.GroupingView.superclass.initTemplates.call(this);
  142.         this.state = {};
  143.         var sm = this.grid.getSelectionModel();
  144.         sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',
  145.                 this.onBeforeRowSelect, this);
  146.         if(!this.startGroup){
  147.             this.startGroup = new Ext.XTemplate(
  148.                 '<div id="{groupId}" class="x-grid-group {cls}">',
  149.                     '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',
  150.                     '<div id="{groupId}-bd" class="x-grid-group-body">'
  151.             );
  152.         }
  153.         this.startGroup.compile();
  154.         this.endGroup = '</div></div>';
  155.     },
  156.     // private
  157.     findGroup : function(el){
  158.         return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);
  159.     },
  160.     // private
  161.     getGroups : function(){
  162.         return this.hasRows() ? this.mainBody.dom.childNodes : [];
  163.     },
  164.     // private
  165.     onAdd : function(){
  166.         if(this.enableGrouping && !this.ignoreAdd){
  167.             var ss = this.getScrollState();
  168.             this.refresh();
  169.             this.restoreScroll(ss);
  170.         }else if(!this.enableGrouping){
  171.             Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);
  172.         }
  173.     },
  174.     // private
  175.     onRemove : function(ds, record, index, isUpdate){
  176.         Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);
  177.         var g = document.getElementById(record._groupId);
  178.         if(g && g.childNodes[1].childNodes.length < 1){
  179.             Ext.removeNode(g);
  180.         }
  181.         this.applyEmptyText();
  182.     },
  183.     // private
  184.     refreshRow : function(record){
  185.         if(this.ds.getCount()==1){
  186.             this.refresh();
  187.         }else{
  188.             this.isUpdating = true;
  189.             Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);
  190.             this.isUpdating = false;
  191.         }
  192.     },
  193.     // private
  194.     beforeMenuShow : function(){
  195.         var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;
  196.         if((item = items.get('groupBy'))){
  197.             item.setDisabled(disabled);
  198.         }
  199.         if((item = items.get('showGroups'))){
  200.             item.setDisabled(disabled);
  201.     item.setChecked(!!this.getGroupField(), true);
  202.         }
  203.     },
  204.     // private
  205.     renderUI : function(){
  206.         Ext.grid.GroupingView.superclass.renderUI.call(this);
  207.         this.mainBody.on('mousedown', this.interceptMouse, this);
  208.         if(this.enableGroupingMenu && this.hmenu){
  209.             this.hmenu.add('-',{
  210.                 itemId:'groupBy',
  211.                 text: this.groupByText,
  212.                 handler: this.onGroupByClick,
  213.                 scope: this,
  214.                 iconCls:'x-group-by-icon'
  215.             });
  216.             if(this.enableNoGroups){
  217.                 this.hmenu.add({
  218.                     itemId:'showGroups',
  219.                     text: this.showGroupsText,
  220.                     checked: true,
  221.                     checkHandler: this.onShowGroupsClick,
  222.                     scope: this
  223.                 });
  224.             }
  225.             this.hmenu.on('beforeshow', this.beforeMenuShow, this);
  226.         }
  227.     },
  228.     // private
  229.     onGroupByClick : function(){
  230.         this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));
  231.         this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups
  232.     },
  233.     // private
  234.     onShowGroupsClick : function(mi, checked){
  235.         if(checked){
  236.             this.onGroupByClick();
  237.         }else{
  238.             this.grid.store.clearGrouping();
  239.         }
  240.     },
  241.     /**
  242.      * Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.
  243.      * @param {String} groupId The groupId assigned to the group (see getGroupId)
  244.      * @param {Boolean} expanded (optional)
  245.      */
  246.     toggleGroup : function(group, expanded){
  247.         this.grid.stopEditing(true);
  248.         group = Ext.getDom(group);
  249.         var gel = Ext.fly(group);
  250.         expanded = expanded !== undefined ?
  251.                 expanded : gel.hasClass('x-grid-group-collapsed');
  252.         this.state[gel.dom.id] = expanded;
  253.         gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');
  254.     },
  255.     /**
  256.      * Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.
  257.      * @param {Boolean} expanded (optional)
  258.      */
  259.     toggleAllGroups : function(expanded){
  260.         var groups = this.getGroups();
  261.         for(var i = 0, len = groups.length; i < len; i++){
  262.             this.toggleGroup(groups[i], expanded);
  263.         }
  264.     },
  265.     /**
  266.      * Expands all grouped rows.
  267.      */
  268.     expandAllGroups : function(){
  269.         this.toggleAllGroups(true);
  270.     },
  271.     /**
  272.      * Collapses all grouped rows.
  273.      */
  274.     collapseAllGroups : function(){
  275.         this.toggleAllGroups(false);
  276.     },
  277.     // private
  278.     interceptMouse : function(e){
  279.         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
  280.         if(hd){
  281.             e.stopEvent();
  282.             this.toggleGroup(hd.parentNode);
  283.         }
  284.     },
  285.     // private
  286.     getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){
  287.         var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);
  288.         if(g === ''){
  289.             g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;
  290.         }
  291.         return g;
  292.     },
  293.     // private
  294.     getGroupField : function(){
  295.         return this.grid.store.getGroupState();
  296.     },
  297.     
  298.     // private
  299.     afterRender : function(){
  300.         Ext.grid.GroupingView.superclass.afterRender.call(this);
  301.         if(this.grid.deferRowRender){
  302.             this.updateGroupWidths();
  303.         }
  304.     },
  305.     // private
  306.     renderRows : function(){
  307.         var groupField = this.getGroupField();
  308.         var eg = !!groupField;
  309.         // if they turned off grouping and the last grouped field is hidden
  310.         if(this.hideGroupedColumn) {
  311.             var colIndex = this.cm.findColumnIndex(groupField);
  312.             if(!eg && this.lastGroupField !== undefined) {
  313.                 this.mainBody.update('');
  314.                 this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);
  315.                 delete this.lastGroupField;
  316.             }else if (eg && this.lastGroupField === undefined) {
  317.                 this.lastGroupField = groupField;
  318.                 this.cm.setHidden(colIndex, true);
  319.             }else if (eg && this.lastGroupField !== undefined && groupField !== this.lastGroupField) {
  320.                 this.mainBody.update('');
  321.                 var oldIndex = this.cm.findColumnIndex(this.lastGroupField);
  322.                 this.cm.setHidden(oldIndex, false);
  323.                 this.lastGroupField = groupField;
  324.                 this.cm.setHidden(colIndex, true);
  325.             }
  326.         }
  327.         return Ext.grid.GroupingView.superclass.renderRows.apply(
  328.                     this, arguments);
  329.     },
  330.     // private
  331.     doRender : function(cs, rs, ds, startRow, colCount, stripe){
  332.         if(rs.length < 1){
  333.             return '';
  334.         }
  335.         var groupField = this.getGroupField(),
  336.             colIndex = this.cm.findColumnIndex(groupField),
  337.             g;
  338.         this.enableGrouping = !!groupField;
  339.         if(!this.enableGrouping || this.isUpdating){
  340.             return Ext.grid.GroupingView.superclass.doRender.apply(
  341.                     this, arguments);
  342.         }
  343.         var gstyle = 'width:'+this.getTotalWidth()+';';
  344.         var gidPrefix = this.grid.getGridEl().id;
  345.         var cfg = this.cm.config[colIndex];
  346.         var groupRenderer = cfg.groupRenderer || cfg.renderer;
  347.         var prefix = this.showGroupName ?
  348.                      (cfg.groupName || cfg.header)+': ' : '';
  349.         var groups = [], curGroup, i, len, gid;
  350.         for(i = 0, len = rs.length; i < len; i++){
  351.             var rowIndex = startRow + i,
  352.                 r = rs[i],
  353.                 gvalue = r.data[groupField];
  354.                 
  355.                 g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);
  356.             if(!curGroup || curGroup.group != g){
  357.                 gid = gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(g);
  358.                 // if state is defined use it, however state is in terms of expanded
  359. // so negate it, otherwise use the default.
  360. var isCollapsed  = typeof this.state[gid] !== 'undefined' ? !this.state[gid] : this.startCollapsed;
  361. var gcls = isCollapsed ? 'x-grid-group-collapsed' : '';
  362.                 curGroup = {
  363.                     group: g,
  364.                     gvalue: gvalue,
  365.                     text: prefix + g,
  366.                     groupId: gid,
  367.                     startRow: rowIndex,
  368.                     rs: [r],
  369.                     cls: gcls,
  370.                     style: gstyle
  371.                 };
  372.                 groups.push(curGroup);
  373.             }else{
  374.                 curGroup.rs.push(r);
  375.             }
  376.             r._groupId = gid;
  377.         }
  378.         var buf = [];
  379.         for(i = 0, len = groups.length; i < len; i++){
  380.             g = groups[i];
  381.             this.doGroupStart(buf, g, cs, ds, colCount);
  382.             buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(
  383.                     this, cs, g.rs, ds, g.startRow, colCount, stripe);
  384.             this.doGroupEnd(buf, g, cs, ds, colCount);
  385.         }
  386.         return buf.join('');
  387.     },
  388.     /**
  389.      * Dynamically tries to determine the groupId of a specific value
  390.      * @param {String} value
  391.      * @return {String} The group id
  392.      */
  393.     getGroupId : function(value){
  394.         var gidPrefix = this.grid.getGridEl().id;
  395.         var groupField = this.getGroupField();
  396.         var colIndex = this.cm.findColumnIndex(groupField);
  397.         var cfg = this.cm.config[colIndex];
  398.         var groupRenderer = cfg.groupRenderer || cfg.renderer;
  399.         var gtext = this.getGroup(value, {data:{}}, groupRenderer, 0, colIndex, this.ds);
  400.         return gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(value);
  401.     },
  402.     // private
  403.     doGroupStart : function(buf, g, cs, ds, colCount){
  404.         buf[buf.length] = this.startGroup.apply(g);
  405.     },
  406.     // private
  407.     doGroupEnd : function(buf, g, cs, ds, colCount){
  408.         buf[buf.length] = this.endGroup;
  409.     },
  410.     // private
  411.     getRows : function(){
  412.         if(!this.enableGrouping){
  413.             return Ext.grid.GroupingView.superclass.getRows.call(this);
  414.         }
  415.         var r = [];
  416.         var g, gs = this.getGroups();
  417.         for(var i = 0, len = gs.length; i < len; i++){
  418.             g = gs[i].childNodes[1].childNodes;
  419.             for(var j = 0, jlen = g.length; j < jlen; j++){
  420.                 r[r.length] = g[j];
  421.             }
  422.         }
  423.         return r;
  424.     },
  425.     // private
  426.     updateGroupWidths : function(){
  427.         if(!this.enableGrouping || !this.hasRows()){
  428.             return;
  429.         }
  430.         var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.scrollOffset) +'px';
  431.         var gs = this.getGroups();
  432.         for(var i = 0, len = gs.length; i < len; i++){
  433.             gs[i].firstChild.style.width = tw;
  434.         }
  435.     },
  436.     // private
  437.     onColumnWidthUpdated : function(col, w, tw){
  438.         Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);
  439.         this.updateGroupWidths();
  440.     },
  441.     // private
  442.     onAllColumnWidthsUpdated : function(ws, tw){
  443.         Ext.grid.GroupingView.superclass.onAllColumnWidthsUpdated.call(this, ws, tw);
  444.         this.updateGroupWidths();
  445.     },
  446.     // private
  447.     onColumnHiddenUpdated : function(col, hidden, tw){
  448.         Ext.grid.GroupingView.superclass.onColumnHiddenUpdated.call(this, col, hidden, tw);
  449.         this.updateGroupWidths();
  450.     },
  451.     // private
  452.     onLayout : function(){
  453.         this.updateGroupWidths();
  454.     },
  455.     // private
  456.     onBeforeRowSelect : function(sm, rowIndex){
  457.         if(!this.enableGrouping){
  458.             return;
  459.         }
  460.         var row = this.getRow(rowIndex);
  461.         if(row && !row.offsetParent){
  462.             var g = this.findGroup(row);
  463.             this.toggleGroup(g, true);
  464.         }
  465.     }
  466. });
  467. // private
  468. Ext.grid.GroupingView.GROUP_ID = 1000;