microsoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemail
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
696 lines
21 KiB
696 lines
21 KiB
/** |
|
* As the number of records increases, the time required for the browser to render them increases. Paging is used to |
|
* reduce the amount of data exchanged with the client. Note: if there are more records/rows than can be viewed in the |
|
* available screen area, vertical scrollbars will be added. |
|
* |
|
* Paging is typically handled on the server side (see exception below). The client sends parameters to the server side, |
|
* which the server needs to interpret and then respond with the appropriate data. |
|
* |
|
* Ext.toolbar.Paging is a specialized toolbar that is bound to a {@link Ext.data.Store} and provides automatic |
|
* paging control. This Component {@link Ext.data.Store#method-load load}s blocks of data into the {@link #store} by passing |
|
* parameters used for paging criteria. |
|
* |
|
* {@img Ext.toolbar.Paging/Ext.toolbar.Paging.png Ext.toolbar.Paging component} |
|
* |
|
* Paging Toolbar is typically used as one of the Grid's toolbars: |
|
* |
|
* var itemsPerPage = 2; // set the number of items you want per page |
|
* |
|
* Ext.create('Ext.data.Store', { |
|
* id: 'simpsonsStore', |
|
* autoLoad: false, |
|
* fields: ['name', 'email', 'phone'], |
|
* pageSize: itemsPerPage, // items per page |
|
* proxy: { |
|
* type: 'ajax', |
|
* url: 'pagingstore.js', // url that will load data with respect to start and limit params |
|
* reader: { |
|
* type: 'json', |
|
* rootProperty: 'items', |
|
* totalProperty: 'total' |
|
* } |
|
* } |
|
* }); |
|
* |
|
* // specify segment of data you want to load using params |
|
* store.load({ |
|
* params: { |
|
* start: 0, |
|
* limit: itemsPerPage |
|
* } |
|
* }); |
|
* |
|
* Ext.create('Ext.grid.Panel', { |
|
* title: 'Simpsons', |
|
* store: 'simpsonsStore', |
|
* columns: [{ |
|
* text: 'Name', |
|
* dataIndex: 'name' |
|
* }, { |
|
* text: 'Email', |
|
* dataIndex: 'email', |
|
* flex: 1 |
|
* }, { |
|
* text: 'Phone', |
|
* dataIndex: 'phone' |
|
* }], |
|
* width: 400, |
|
* height: 125, |
|
* dockedItems: [{ |
|
* xtype: 'pagingtoolbar', |
|
* store: 'simpsonsStore', // same store GridPanel is using |
|
* dock: 'bottom', |
|
* displayInfo: true |
|
* }], |
|
* renderTo: Ext.getBody() |
|
* }); |
|
* |
|
* To use paging, you need to set a pageSize configuration on the Store, and pass the paging requirements to |
|
* the server when the store is first loaded. |
|
* |
|
* store.load({ |
|
* params: { |
|
* // specify params for the first page load if using paging |
|
* start: 0, |
|
* limit: myPageSize, |
|
* // other params |
|
* foo: 'bar' |
|
* } |
|
* }); |
|
* |
|
* If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration: |
|
* |
|
* var myStore = Ext.create('Ext.data.Store', { |
|
* {@link Ext.data.Store#autoLoad autoLoad}: {start: 0, limit: 25}, |
|
* ... |
|
* }); |
|
* |
|
* The packet sent back from the server would have this form: |
|
* |
|
* { |
|
* "success": true, |
|
* "results": 2000, |
|
* "rows": [ // ***Note:** this must be an Array |
|
* { "id": 1, "name": "Bill", "occupation": "Gardener" }, |
|
* { "id": 2, "name": "Ben", "occupation": "Horticulturalist" }, |
|
* ... |
|
* { "id": 25, "name": "Sue", "occupation": "Botanist" } |
|
* ] |
|
* } |
|
* |
|
* ## Paging with Local Data |
|
* |
|
* Paging can also be accomplished with local data using extensions: |
|
* |
|
* - [Ext.ux.data.PagingStore][1] |
|
* - Paging Memory Proxy (examples/ux/PagingMemoryProxy.js) |
|
* |
|
* [1]: http://sencha.com/forum/showthread.php?t=71532 |
|
*/ |
|
Ext.define('Ext.toolbar.Paging', { |
|
extend: 'Ext.toolbar.Toolbar', |
|
xtype: 'pagingtoolbar', |
|
alternateClassName: 'Ext.PagingToolbar', |
|
requires: [ |
|
'Ext.toolbar.TextItem', |
|
'Ext.form.field.Number' |
|
], |
|
mixins: [ |
|
'Ext.util.StoreHolder' |
|
], |
|
|
|
/** |
|
* @cfg {Ext.data.Store/String} store (required) |
|
* The data source to which the paging toolbar is bound (must be the same store instance |
|
* used in the grid / tree). Acceptable values for this property are: |
|
* |
|
* - **any {@link Ext.data.Store Store} class / subclass** |
|
* - **an {@link Ext.data.Store#storeId ID of a store}** |
|
*/ |
|
|
|
/** |
|
* @cfg {Boolean} displayInfo |
|
* true to display the displayMsg |
|
*/ |
|
displayInfo: false, |
|
|
|
/** |
|
* @cfg {Boolean} prependButtons |
|
* true to insert any configured items _before_ the paging buttons. |
|
*/ |
|
prependButtons: false, |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} displayMsg |
|
* The paging status message to display. Note that this string is |
|
* formatted using the braced numbers {0}-{2} as tokens that are replaced by the values for start, end and total |
|
* respectively. These tokens should be preserved when overriding this string if showing those values is desired. |
|
*/ |
|
displayMsg : 'Displaying {0} - {1} of {2}', |
|
//</locale> |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} emptyMsg |
|
* The message to display when no records are found. |
|
*/ |
|
emptyMsg : 'No data to display', |
|
//</locale> |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} beforePageText |
|
* The text displayed before the input item. |
|
*/ |
|
beforePageText : 'Page', |
|
//</locale> |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} afterPageText |
|
* Customizable piece of the default paging text. Note that this string is formatted using |
|
* {0} as a token that is replaced by the number of total pages. This token should be preserved when overriding this |
|
* string if showing the total page count is desired. |
|
*/ |
|
afterPageText : 'of {0}', |
|
//</locale> |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} firstText |
|
* The quicktip text displayed for the first page button. |
|
* **Note**: quick tips must be initialized for the quicktip to show. |
|
*/ |
|
firstText : 'First Page', |
|
//</locale> |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} prevText |
|
* The quicktip text displayed for the previous page button. |
|
* **Note**: quick tips must be initialized for the quicktip to show. |
|
*/ |
|
prevText : 'Previous Page', |
|
//</locale> |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} nextText |
|
* The quicktip text displayed for the next page button. |
|
* **Note**: quick tips must be initialized for the quicktip to show. |
|
*/ |
|
nextText : 'Next Page', |
|
//</locale> |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} lastText |
|
* The quicktip text displayed for the last page button. |
|
* **Note**: quick tips must be initialized for the quicktip to show. |
|
*/ |
|
lastText : 'Last Page', |
|
//</locale> |
|
|
|
//<locale> |
|
/** |
|
* @cfg {String} refreshText |
|
* The quicktip text displayed for the Refresh button. |
|
* **Note**: quick tips must be initialized for the quicktip to show. |
|
*/ |
|
refreshText : 'Refresh', |
|
//</locale> |
|
|
|
/** |
|
* @cfg {Number} inputItemWidth |
|
* The width in pixels of the input field used to display and change the current page number. |
|
*/ |
|
inputItemWidth : 30, |
|
|
|
/** |
|
* @event change |
|
* Fires after the active page has been changed. |
|
* @param {Ext.toolbar.Paging} this |
|
* @param {Object} pageData An object that has these properties: |
|
* |
|
* - `total` : Number |
|
* |
|
* The total number of records in the dataset as returned by the server |
|
* |
|
* - `currentPage` : Number |
|
* |
|
* The current page number |
|
* |
|
* - `pageCount` : Number |
|
* |
|
* The total number of pages (calculated from the total number of records in the dataset as returned by the |
|
* server and the current {@link Ext.data.Store#pageSize pageSize}) |
|
* |
|
* - `toRecord` : Number |
|
* |
|
* The starting record index for the current page |
|
* |
|
* - `fromRecord` : Number |
|
* |
|
* The ending record index for the current page |
|
*/ |
|
|
|
/** |
|
* @event beforechange |
|
* Fires just before the active page is changed. Return false to prevent the active page from being changed. |
|
* @param {Ext.toolbar.Paging} this |
|
* @param {Number} page The page number that will be loaded on change |
|
*/ |
|
|
|
emptyPageData: { |
|
total: 0, |
|
currentPage: 0, |
|
pageCount: 0, |
|
toRecord: 0, |
|
fromRecord: 0 |
|
}, |
|
|
|
/** |
|
* @inheritdoc |
|
*/ |
|
defaultBindProperty: 'store', |
|
|
|
/** |
|
* Gets the standard paging items in the toolbar |
|
* @private |
|
*/ |
|
getPagingItems: function() { |
|
var me = this, |
|
inputListeners = { |
|
scope: me, |
|
blur: me.onPagingBlur |
|
}; |
|
|
|
inputListeners[Ext.supports.SpecialKeyDownRepeat ? 'keydown' : 'keypress'] = me.onPagingKeyDown; |
|
|
|
return [{ |
|
itemId: 'first', |
|
tooltip: me.firstText, |
|
overflowText: me.firstText, |
|
iconCls: Ext.baseCSSPrefix + 'tbar-page-first', |
|
disabled: true, |
|
handler: me.moveFirst, |
|
scope: me |
|
},{ |
|
itemId: 'prev', |
|
tooltip: me.prevText, |
|
overflowText: me.prevText, |
|
iconCls: Ext.baseCSSPrefix + 'tbar-page-prev', |
|
disabled: true, |
|
handler: me.movePrevious, |
|
scope: me |
|
}, |
|
'-', |
|
me.beforePageText, |
|
{ |
|
xtype: 'numberfield', |
|
itemId: 'inputItem', |
|
name: 'inputItem', |
|
cls: Ext.baseCSSPrefix + 'tbar-page-number', |
|
allowDecimals: false, |
|
minValue: 1, |
|
hideTrigger: true, |
|
enableKeyEvents: true, |
|
keyNavEnabled: false, |
|
selectOnFocus: true, |
|
submitValue: false, |
|
// mark it as not a field so the form will not catch it when getting fields |
|
isFormField: false, |
|
width: me.inputItemWidth, |
|
margin: '-1 2 3 2', |
|
listeners: inputListeners |
|
},{ |
|
xtype: 'tbtext', |
|
itemId: 'afterTextItem', |
|
text: Ext.String.format(me.afterPageText, 1) |
|
}, |
|
'-', |
|
{ |
|
itemId: 'next', |
|
tooltip: me.nextText, |
|
overflowText: me.nextText, |
|
iconCls: Ext.baseCSSPrefix + 'tbar-page-next', |
|
disabled: true, |
|
handler: me.moveNext, |
|
scope: me |
|
},{ |
|
itemId: 'last', |
|
tooltip: me.lastText, |
|
overflowText: me.lastText, |
|
iconCls: Ext.baseCSSPrefix + 'tbar-page-last', |
|
disabled: true, |
|
handler: me.moveLast, |
|
scope: me |
|
}, |
|
'-', |
|
{ |
|
itemId: 'refresh', |
|
tooltip: me.refreshText, |
|
overflowText: me.refreshText, |
|
iconCls: Ext.baseCSSPrefix + 'tbar-loading', |
|
disabled: me.store.isLoading(), |
|
handler: me.doRefresh, |
|
scope: me |
|
}]; |
|
}, |
|
|
|
initComponent : function(){ |
|
var me = this, |
|
userItems = me.items || me.buttons || [], |
|
pagingItems; |
|
|
|
me.bindStore(me.store || 'ext-empty-store', true); |
|
pagingItems = me.getPagingItems(); |
|
if (me.prependButtons) { |
|
me.items = userItems.concat(pagingItems); |
|
} else { |
|
me.items = pagingItems.concat(userItems); |
|
} |
|
delete me.buttons; |
|
|
|
if (me.displayInfo) { |
|
me.items.push('->'); |
|
me.items.push({xtype: 'tbtext', itemId: 'displayItem'}); |
|
} |
|
|
|
me.callParent(); |
|
}, |
|
|
|
beforeRender: function() { |
|
this.callParent(arguments); |
|
this.updateBarInfo(); |
|
}, |
|
|
|
updateBarInfo: function() { |
|
var me = this; |
|
if (!me.store.isLoading()) { |
|
me.calledInternal = true; |
|
me.onLoad(); |
|
me.calledInternal = false; |
|
} |
|
}, |
|
|
|
// @private |
|
updateInfo : function(){ |
|
var me = this, |
|
displayItem = me.child('#displayItem'), |
|
store = me.store, |
|
pageData = me.getPageData(), |
|
count, msg; |
|
|
|
if (displayItem) { |
|
count = store.getCount(); |
|
if (count === 0) { |
|
msg = me.emptyMsg; |
|
} else { |
|
msg = Ext.String.format( |
|
me.displayMsg, |
|
pageData.fromRecord, |
|
pageData.toRecord, |
|
pageData.total |
|
); |
|
} |
|
displayItem.setText(msg); |
|
} |
|
}, |
|
|
|
// @private |
|
onLoad : function(){ |
|
var me = this, |
|
pageData, |
|
currPage, |
|
pageCount, |
|
afterText, |
|
count, |
|
isEmpty, |
|
item; |
|
|
|
count = me.store.getCount(); |
|
isEmpty = count === 0; |
|
if (!isEmpty) { |
|
pageData = me.getPageData(); |
|
currPage = pageData.currentPage; |
|
pageCount = pageData.pageCount; |
|
|
|
// Check for invalid current page. |
|
if (currPage > pageCount) { |
|
// If the surrent page is beyond the loaded end, |
|
// jump back to the loaded end if there is a valid page count. |
|
if (pageCount > 0) { |
|
me.store.loadPage(pageCount); |
|
} |
|
// If no pages, reset the page field. |
|
else { |
|
me.getInputItem().reset(); |
|
} |
|
return; |
|
} |
|
|
|
afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount); |
|
} else { |
|
currPage = 0; |
|
pageCount = 0; |
|
afterText = Ext.String.format(me.afterPageText, 0); |
|
} |
|
|
|
Ext.suspendLayouts(); |
|
item = me.child('#afterTextItem'); |
|
if (item) { |
|
item.setText(afterText); |
|
} |
|
item = me.getInputItem(); |
|
if (item) { |
|
item.setDisabled(isEmpty).setValue(currPage); |
|
} |
|
me.setChildDisabled('#first', currPage === 1 || isEmpty); |
|
me.setChildDisabled('#prev', currPage === 1 || isEmpty); |
|
me.setChildDisabled('#next', currPage === pageCount || isEmpty); |
|
me.setChildDisabled('#last', currPage === pageCount || isEmpty); |
|
me.setChildDisabled('#refresh', false); |
|
me.updateInfo(); |
|
Ext.resumeLayouts(true); |
|
|
|
if (!me.calledInternal) { |
|
me.fireEvent('change', me, pageData || me.emptyPageData); |
|
} |
|
}, |
|
|
|
setChildDisabled: function(selector, disabled){ |
|
var item = this.child(selector); |
|
if (item) { |
|
item.setDisabled(disabled); |
|
} |
|
}, |
|
|
|
// @private |
|
getPageData : function(){ |
|
var store = this.store, |
|
totalCount = store.getTotalCount(); |
|
|
|
return { |
|
total : totalCount, |
|
currentPage : store.currentPage, |
|
pageCount: Math.ceil(totalCount / store.pageSize), |
|
fromRecord: ((store.currentPage - 1) * store.pageSize) + 1, |
|
toRecord: Math.min(store.currentPage * store.pageSize, totalCount) |
|
|
|
}; |
|
}, |
|
|
|
// @private |
|
onLoadError : function(){ |
|
this.setChildDisabled('#refresh', false); |
|
}, |
|
|
|
getInputItem: function(){ |
|
return this.child('#inputItem'); |
|
}, |
|
|
|
// @private |
|
readPageFromInput : function(pageData){ |
|
var inputItem = this.getInputItem(), |
|
pageNum = false, |
|
v; |
|
|
|
if (inputItem) { |
|
v = inputItem.getValue(); |
|
pageNum = parseInt(v, 10); |
|
if (!v || isNaN(pageNum)) { |
|
inputItem.setValue(pageData.currentPage); |
|
return false; |
|
} |
|
} |
|
return pageNum; |
|
}, |
|
|
|
// @private |
|
onPagingBlur : function(e){ |
|
var inputItem = this.getInputItem(), |
|
curPage; |
|
|
|
if (inputItem) { |
|
curPage = this.getPageData().currentPage; |
|
inputItem.setValue(curPage); |
|
} |
|
}, |
|
|
|
// @private |
|
onPagingKeyDown : function(field, e){ |
|
this.processKeyEvent(field, e); |
|
}, |
|
|
|
processKeyEvent: function(field, e) { |
|
var me = this, |
|
key = e.getKey(), |
|
pageData = me.getPageData(), |
|
increment = e.shiftKey ? 10 : 1, |
|
pageNum; |
|
|
|
if (key === e.RETURN) { |
|
e.stopEvent(); |
|
pageNum = me.readPageFromInput(pageData); |
|
if (pageNum !== false) { |
|
pageNum = Math.min(Math.max(1, pageNum), pageData.pageCount); |
|
if (pageNum !== pageData.currentPage && me.fireEvent('beforechange', me, pageNum) !== false) { |
|
me.store.loadPage(pageNum); |
|
} |
|
} |
|
} else if (key === e.HOME || key === e.END) { |
|
e.stopEvent(); |
|
pageNum = key === e.HOME ? 1 : pageData.pageCount; |
|
field.setValue(pageNum); |
|
} else if (key === e.UP || key === e.PAGE_UP || key === e.DOWN || key === e.PAGE_DOWN) { |
|
e.stopEvent(); |
|
pageNum = me.readPageFromInput(pageData); |
|
if (pageNum) { |
|
if (key === e.DOWN || key === e.PAGE_DOWN) { |
|
increment *= -1; |
|
} |
|
pageNum += increment; |
|
if (pageNum >= 1 && pageNum <= pageData.pageCount) { |
|
field.setValue(pageNum); |
|
} |
|
} |
|
} |
|
}, |
|
|
|
// @private |
|
beforeLoad : function() { |
|
this.setChildDisabled('#refresh', true); |
|
}, |
|
|
|
/** |
|
* Move to the first page, has the same effect as clicking the 'first' button. |
|
* Fires the {@link #beforechange} event. If the event returns `false`, then |
|
* the load will not be attempted. |
|
* @return {Boolean} `true` if the load was passed to the store. |
|
*/ |
|
moveFirst : function(){ |
|
if (this.fireEvent('beforechange', this, 1) !== false){ |
|
this.store.loadPage(1); |
|
return true; |
|
} |
|
return false; |
|
}, |
|
|
|
/** |
|
* Move to the previous page, has the same effect as clicking the 'previous' button. |
|
* Fires the {@link #beforechange} event. If the event returns `false`, then |
|
* the load will not be attempted. |
|
* @return {Boolean} `true` if the load was passed to the store. |
|
*/ |
|
movePrevious : function(){ |
|
var me = this, |
|
store = me.store, |
|
prev = store.currentPage - 1; |
|
|
|
if (prev > 0) { |
|
if (me.fireEvent('beforechange', me, prev) !== false) { |
|
store.previousPage(); |
|
return true; |
|
} |
|
} |
|
return false; |
|
}, |
|
|
|
/** |
|
* Move to the next page, has the same effect as clicking the 'next' button. |
|
* Fires the {@link #beforechange} event. If the event returns `false`, then |
|
* the load will not be attempted. |
|
* @return {Boolean} `true` if the load was passed to the store. |
|
*/ |
|
moveNext : function(){ |
|
var me = this, |
|
store = me.store, |
|
total = me.getPageData().pageCount, |
|
next = store.currentPage + 1; |
|
|
|
if (next <= total) { |
|
if (me.fireEvent('beforechange', me, next) !== false) { |
|
store.nextPage(); |
|
return true; |
|
} |
|
} |
|
return false; |
|
}, |
|
|
|
/** |
|
* Move to the last page, has the same effect as clicking the 'last' button. |
|
* Fires the {@link #beforechange} event. If the event returns `false`, then |
|
* the load will not be attempted. |
|
* @return {Boolean} `true` if the load was passed to the store. |
|
*/ |
|
moveLast : function(){ |
|
var me = this, |
|
last = me.getPageData().pageCount; |
|
|
|
if (me.fireEvent('beforechange', me, last) !== false) { |
|
me.store.loadPage(last); |
|
return true; |
|
} |
|
return false; |
|
}, |
|
|
|
/** |
|
* Refresh the current page, has the same effect as clicking the 'refresh' button. |
|
* Fires the {@link #beforechange} event. If the event returns `false`, then |
|
* the load will not be attempted. |
|
* @return {Boolean} `true` if the load was passed to the store. |
|
*/ |
|
doRefresh : function(){ |
|
var me = this, |
|
store = me.store, |
|
current = store.currentPage; |
|
|
|
if (me.fireEvent('beforechange', me, current) !== false) { |
|
store.loadPage(current); |
|
return true; |
|
} |
|
return false; |
|
}, |
|
|
|
getStoreListeners: function() { |
|
return { |
|
beforeload: this.beforeLoad, |
|
load: this.onLoad, |
|
exception: this.onLoadError |
|
}; |
|
}, |
|
|
|
onBindStore: function() { |
|
if (this.rendered) { |
|
this.updateBarInfo(); |
|
} |
|
}, |
|
|
|
// @private |
|
onDestroy : function(){ |
|
this.bindStore(null); |
|
this.callParent(); |
|
} |
|
});
|
|
|