Skip to content

Commit

Permalink
feat(component): new builder paginag control component
Browse files Browse the repository at this point in the history
  • Loading branch information
tnadkarni2 committed Sep 30, 2024
1 parent 927bcf6 commit 9186658
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 0 deletions.
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>
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

View workflow job for this annotation

GitHub Actions / build (16.x)

Missing JSDoc @param "newPageNumber" description

Check warning on line 179 in force-app/main/default/lwc/builderSearchPagingControl/builderSearchPagingControl.js

View workflow job for this annotation

GitHub Actions / build (18.x)

Missing JSDoc @param "newPageNumber" description
*/
dispatchUpdateCurrentPageEvent(newPageNumber) {
dispatchAction(this, createSearchFiltersUpdateAction({ page: newPageNumber }));
}
}
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 force-app/main/default/lwc/builderSearchPagingControl/constants.js
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 force-app/main/default/lwc/builderSearchPagingControl/labels.js
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 };
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;
}

0 comments on commit 9186658

Please sign in to comment.