-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(component): new builder paginag control component
- Loading branch information
1 parent
927bcf6
commit 9186658
Showing
6 changed files
with
392 additions
and
0 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<template lwc:render-mode="light"> | ||
<section | ||
class="limitHitTextSection" | ||
if:true={showMessageForResultsLimit}> | ||
<p>{label.resultsLimitHitText}</p> | ||
</section> | ||
|
||
<nav class="pageControlPart"> | ||
<button | ||
class="slds-m-horizontal_small slds-button slds-button_neutral nav-direction" | ||
disabled={disablePaginationPrevious} | ||
aria-disabled={disablePaginationPrevious} | ||
onclick={handlePaginationPrevious}> | ||
<lightning-icon | ||
icon-name="utility:chevronleft" | ||
size="xx-small" | ||
alternative-text={label.previous}></lightning-icon> | ||
</button> | ||
|
||
<template | ||
for:each={pageNumbers} | ||
for:item="pageNumObj"> | ||
<template if:true={pageNumObj.isRange}> | ||
<div | ||
key={pageNumObj.id} | ||
class="slds-m-horizontal_small range-symbol-container"> | ||
{rangeSymbol} | ||
</div> | ||
</template> | ||
<template if:false={pageNumObj.isRange}> | ||
<template if:true={pageNumObj.isCurrentPage}> | ||
<span | ||
class="slds-m-horizontal_small slds-button slds-button_brand nav-button-current" | ||
key={pageNumObj.id} | ||
aria-current="page"> | ||
{pageNumObj.pageNumber} | ||
</span> | ||
</template> | ||
<template if:false={pageNumObj.isCurrentPage}> | ||
<button | ||
class="slds-m-horizontal_small slds-button slds-button_neutral nav-button" | ||
key={pageNumObj.id} | ||
value={pageNumObj.pageNumber} | ||
onclick={handlePaginationPage}> | ||
{pageNumObj.pageNumber} | ||
</button> | ||
</template> | ||
</template> | ||
</template> | ||
|
||
<button | ||
class="slds-m-horizontal_small slds-button slds-button_neutral nav-direction" | ||
disabled={disablePaginationNext} | ||
aria-disabled={disablePaginationNext} | ||
onclick={handlePaginationNext}> | ||
<lightning-icon | ||
icon-name="utility:chevronright" | ||
size="xx-small" | ||
alternative-text={label.next}></lightning-icon> | ||
</button> | ||
</nav> | ||
</template> |
184 changes: 184 additions & 0 deletions
184
force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* For full license text, see the LICENSE file in the repo | ||
* root or https://opensource.org/licenses/apache-2-0/ | ||
*/ | ||
import { LightningElement, api } from 'lwc'; | ||
import { generatePagesForRange } from './pagingControlHelper'; | ||
import { PAGING_RANGE_SYMBOL, MAX_RESULTS_OFFSET } from './constants'; | ||
import { previous, next, resultsLimitHitText } from './labels'; | ||
import { createSearchFiltersUpdateAction, dispatchAction } from 'commerce/actionApi'; | ||
/** | ||
* @param {*} value The value to check. | ||
* @param {number} min The minimum value that _`value`_ needs to have. | ||
* @returns {boolean} Whether the given _`value`_ is a number greater than _`min`_. | ||
*/ | ||
function isNumber(value, min) { | ||
return typeof value === 'number' && !Number.isNaN(value) && value > min; | ||
} | ||
|
||
/** | ||
* A page object to render the page item. | ||
* @typedef {object} PageItem | ||
* @property {number} id | ||
* Identifier used as the key for the rendering element. | ||
* @property {?number} pageNumber | ||
* Page number | ||
* @property {boolean} isCurrentPage | ||
* Whether this page is the current page. | ||
* @property {boolean} isRange | ||
* Whether this page is a range element. | ||
*/ | ||
|
||
/** | ||
* A builder pagination UI control for any record visualization controls. | ||
*/ | ||
export default class SearchPagingControl extends LightningElement { | ||
static renderMode = 'light'; | ||
|
||
/** | ||
* Current page number. | ||
*/ | ||
@api | ||
currentPageNumber; | ||
|
||
/** | ||
* Number of items per page. | ||
*/ | ||
@api | ||
pageSize; | ||
|
||
/** | ||
* Total number of items. | ||
*/ | ||
@api | ||
totalItemCount; | ||
|
||
/** | ||
* The maximum quantity of numbered pages displayed to the user. | ||
* This includes numbers and range symbol. | ||
*/ | ||
@api | ||
maximumPagesDisplayed; | ||
|
||
/** | ||
* Gets the required i18n labels | ||
* @readonly | ||
* @private | ||
*/ | ||
label = { | ||
previous, | ||
next, | ||
resultsLimitHitText, | ||
}; | ||
get normalizedPageNumber() { | ||
return isNumber(this.currentPageNumber, 1) ? this.currentPageNumber : 1; | ||
} | ||
get normalizedPageSize() { | ||
return isNumber(this.pageSize, 1) ? this.pageSize : 1; | ||
} | ||
get normalizedItemCount() { | ||
return isNumber(this.totalItemCount, 0) ? this.totalItemCount : 0; | ||
} | ||
|
||
/** | ||
* Disable previous page navigation? | ||
* @type {boolean} | ||
* @readonly | ||
* @private | ||
*/ | ||
get disablePaginationPrevious() { | ||
return this.normalizedPageNumber === 1; | ||
} | ||
|
||
/** | ||
* Disable next page navigation? | ||
* @type {boolean} | ||
* @readonly | ||
* @private | ||
*/ | ||
get disablePaginationNext() { | ||
return this.normalizedPageNumber >= this.totalPages; | ||
} | ||
|
||
/** | ||
* only show a message if this is the last page we could possibly show while there are more results due to API limitation: | ||
* true if totalItemCount > 5000 + pageSize and this is the last page (aNumber to 5000+pageSize) | ||
* @type {boolean} | ||
* @readonly | ||
* @private | ||
*/ | ||
get showMessageForResultsLimit() { | ||
const pageSize = this.normalizedPageSize; | ||
return ( | ||
this.normalizedItemCount > MAX_RESULTS_OFFSET + pageSize && | ||
this.normalizedPageNumber >= Math.ceil((MAX_RESULTS_OFFSET + pageSize) / pageSize) | ||
); | ||
} | ||
|
||
/** | ||
* Gets total number of pages. | ||
* @type {number} | ||
* @readonly | ||
* @private | ||
*/ | ||
get totalPages() { | ||
return Math.ceil(this.normalizedItemCount / this.normalizedPageSize); | ||
} | ||
|
||
/** | ||
* Gets page numbers as an array of objects. | ||
* @type {PageItem[]} | ||
* @readonly | ||
* @private | ||
*/ | ||
get pageNumbers() { | ||
const max = isNumber(this.maximumPagesDisplayed, 0) ? this.maximumPagesDisplayed : 5; | ||
return generatePagesForRange(this.normalizedPageNumber, this.totalPages, max); | ||
} | ||
|
||
/** | ||
* Gets the symbol for range symbol. | ||
* @type {string} | ||
* @readonly | ||
* @private | ||
*/ | ||
get rangeSymbol() { | ||
return PAGING_RANGE_SYMBOL; | ||
} | ||
|
||
/** | ||
* Handler for the 'click' event from the previous button. | ||
*/ | ||
handlePaginationPrevious() { | ||
const previousPageNumber = Number(this.currentPageNumber) - 1; | ||
this.dispatchUpdateCurrentPageEvent(previousPageNumber); | ||
} | ||
|
||
/** | ||
* Handler for the 'click' event from the next button. | ||
*/ | ||
handlePaginationNext() { | ||
const nextPageNumber = Number(this.currentPageNumber) + 1; | ||
this.dispatchUpdateCurrentPageEvent(nextPageNumber); | ||
} | ||
|
||
/** | ||
* Handler for the 'click' event from the page number button. | ||
* @param {Event} event The event object | ||
*/ | ||
handlePaginationPage(event) { | ||
let pageNumber = parseInt(event.target.value, 10); | ||
this.dispatchUpdateCurrentPageEvent(pageNumber); | ||
} | ||
|
||
/** | ||
* Dispatch filterChange action on search data provider. | ||
* @param {number} newPageNumber | ||
Check warning on line 179 in force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js GitHub Actions / build (16.x)
Check warning on line 179 in force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js GitHub Actions / build (18.x)
|
||
*/ | ||
dispatchUpdateCurrentPageEvent(newPageNumber) { | ||
dispatchAction(this, createSearchFiltersUpdateAction({ page: newPageNumber })); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js-meta.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?xml version="1.0" encoding="UTF-8" ?> | ||
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> | ||
<isExposed>true</isExposed> | ||
<targets> | ||
<target>lightningCommunity__Page</target> | ||
<target>lightningCommunity__Default</target> | ||
</targets> | ||
<masterLabel>Custom Search Paging Control</masterLabel> | ||
<description>Displays pagination control for search page.</description> | ||
<targetConfigs> | ||
<targetConfig targets="lightningCommunity__Default"> | ||
<property name="currentPageNumber" type="String" default="{!Search.Pagination.currentPage}" /> | ||
<property name="pageSize" type="String" default="{!Search.Pagination.totalPages}" /> | ||
<property name="totalItemCount" type="String" default="{!Search.Results.total}" /> | ||
<property name="maximumPagesDisplayed" type="String" default="5" /> | ||
</targetConfig> | ||
</targetConfigs> | ||
</LightningComponentBundle> |
18 changes: 18 additions & 0 deletions
18
force-app/main/default/lwc/builderSearchPagingControl/constants.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* For full license text, see the LICENSE file in the repo | ||
* root or https://opensource.org/licenses/apache-2-0/ | ||
*/ | ||
export const PAGING_RANGE_SYMBOL = '...'; | ||
|
||
export const EVENT = { | ||
PAGE_CHANGE_PREVIOUS_EVT: 'pageprevious', | ||
|
||
PAGE_CHANGE_NEXT_EVT: 'pagenext', | ||
|
||
PAGE_CHANGE_GOTOPAGE_EVT: 'pagegoto', | ||
}; | ||
|
||
export const MAX_RESULTS_OFFSET = 5000; |
11 changes: 11 additions & 0 deletions
11
force-app/main/default/lwc/builderSearchPagingControl/labels.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* For full license text, see the LICENSE file in the repo | ||
* root or https://opensource.org/licenses/apache-2-0/ | ||
*/ | ||
import previous from '@salesforce/label/c.Search_Results_previous'; | ||
import next from '@salesforce/label/c.Search_Results_next'; | ||
import resultsLimitHitText from '@salesforce/label/c.Search_Results_resultsLimitHitText'; | ||
export { previous, next, resultsLimitHitText }; |
99 changes: 99 additions & 0 deletions
99
force-app/main/default/lwc/builderSearchPagingControl/pagingControlHelper.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright (c) 2024, salesforce.com, inc. | ||
* All rights reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* For full license text, see the LICENSE file in the repo | ||
* root or https://opensource.org/licenses/apache-2-0/ | ||
*/ | ||
/** | ||
* @typedef {import('./searchPagingControl').PageItem} PageItem | ||
*/ | ||
|
||
/** | ||
* Construct a PageItem object with given parameters and add to the given array. | ||
* @param {PageItem[]} pageObjArray reference array to add the PageItem object | ||
* @param {boolean} isRange is this a range? | ||
* @param {number} [pageNumber] page number | ||
* @param {number} [currentPage] current page number | ||
*/ | ||
function addPageObject(pageObjArray, isRange, pageNumber, currentPage) { | ||
const id = pageObjArray.length; | ||
pageObjArray.push({ | ||
id, | ||
pageNumber, | ||
isRange, | ||
isCurrentPage: pageNumber != null && pageNumber === currentPage, | ||
}); | ||
} | ||
|
||
/** | ||
* @param {number} length length of the result array | ||
* @param {number} start starting number of the range | ||
* @returns {Array<number>} An array filled with given range values | ||
*/ | ||
function getRangeArray(length, start) { | ||
return Array.from( | ||
{ | ||
length, | ||
}, | ||
(v, k) => k + start | ||
); | ||
} | ||
|
||
/** | ||
* @param {number} currentPage current page number | ||
* @param {number} totalPage total number of pages | ||
* @param {number} maxPageButtons max no. of page buttons to show in this page range. | ||
* @returns {PageItem[]} An array of {@link PageItem}s with relevant information | ||
* such as `id`, `pageNumber`, `isCurrentPage`, `isRange` | ||
*/ | ||
export function generatePagesForRange(currentPage, totalPage, maxPageButtons) { | ||
let pages = []; | ||
let balanceLength; | ||
let lastPageNumber; | ||
const pageRange = []; | ||
|
||
balanceLength = maxPageButtons - 2; | ||
if (maxPageButtons > totalPage) { | ||
pages = getRangeArray(totalPage, 1); | ||
} else if (currentPage < balanceLength) { | ||
pages = getRangeArray(balanceLength, 1); | ||
|
||
pages.push(totalPage); | ||
} else if (currentPage > totalPage - balanceLength + 1) { | ||
pages = getRangeArray(balanceLength, totalPage - balanceLength + 1); | ||
|
||
pages.unshift(1); | ||
} else { | ||
balanceLength = maxPageButtons - 4; | ||
|
||
let beginIndex = 0; | ||
const halfRB = balanceLength >> 1; | ||
if (halfRB) { | ||
beginIndex = currentPage - halfRB; | ||
|
||
if (balanceLength === 2) { | ||
beginIndex += 1; | ||
} | ||
} else { | ||
beginIndex = currentPage; | ||
} | ||
pages = getRangeArray(balanceLength, beginIndex); | ||
|
||
pages.unshift(1); | ||
pages.push(totalPage); | ||
} | ||
|
||
pages.forEach((pageNumber) => { | ||
if (lastPageNumber) { | ||
if (pageNumber - lastPageNumber === 2) { | ||
addPageObject(pageRange, false, lastPageNumber + 1, currentPage); | ||
} else if (pageNumber - lastPageNumber !== 1) { | ||
addPageObject(pageRange, true, undefined, undefined); | ||
} | ||
} | ||
addPageObject(pageRange, false, pageNumber, currentPage); | ||
lastPageNumber = pageNumber; | ||
}); | ||
return pageRange; | ||
} |