react-responsive-pagination
Advanced tools
Comparing version 2.2.4 to 2.3.0-beta.1
# React Responsive Pagination Changelog | ||
# [2.3.0-beta.1](https://github.com/jonelantha/react-responsive-pagination/compare/v2.2.4...v2.3.0-beta.1) (2023-12-11) | ||
### Features | ||
* NarrowBehaviour combine helper, for using multiple NarrowBehaviours ([3c040ec](https://github.com/jonelantha/react-responsive-pagination/commit/3c040ec0060fcb9aeaa284b66f1d35376ed748f2)) | ||
* new dropFirstAndLast NarrowBehaviour ([f0b0a54](https://github.com/jonelantha/react-responsive-pagination/commit/f0b0a54d1886fded1a1aa8d25921a4f8f3a622ed)) | ||
## [2.2.4](https://github.com/jonelantha/react-responsive-pagination/compare/v2.2.3...v2.2.4) (2023-11-24) | ||
@@ -4,0 +12,0 @@ |
@@ -29,3 +29,7 @@ type CompositionPage = { | ||
export declare function isEllipsis(item: CompositionItem): item is CompositionEllipsis; | ||
export declare function containsEllipsis(composition: CompositionItem[]): boolean; | ||
export declare function containsEllipsis(composition: ReadonlyArray<CompositionItem>): boolean; | ||
export declare function isPageWithNumber(item: CompositionItem, page: number): item is CompositionPage; | ||
export declare function getLastPage(composition: ReadonlyArray<CompositionItem>): number; | ||
export declare function compositionMatches(composition: ReadonlyArray<CompositionItem>, startIndex: number, pattern: (number | '#' | '…' | '*')[]): boolean | undefined; | ||
export declare function compositionMatchesEnd(composition: ReadonlyArray<CompositionItem>, endIndex: number, pattern: (number | '#' | '…' | '*')[]): boolean | undefined; | ||
export {}; |
@@ -0,1 +1,2 @@ | ||
import { UnsupportedValueError } from './helpers/util.js'; | ||
export function createActivePage(page) { | ||
@@ -25,1 +26,36 @@ return { type: 'active', page }; | ||
} | ||
export function isPageWithNumber(item, page) { | ||
return item.type === 'page' && item.page === page; | ||
} | ||
export function getLastPage(composition) { | ||
return Math.max(...composition | ||
.filter((item) => item.type === 'active' || item.type === 'page') | ||
.map(item => item.page)); | ||
} | ||
export function compositionMatches(composition, startIndex, pattern) { | ||
if (startIndex < 0) | ||
return; | ||
return pattern.every((matchItem, patternIndex) => { | ||
if (!composition[startIndex + patternIndex]) | ||
return false; | ||
const { type, page } = composition[startIndex + patternIndex]; | ||
if (typeof matchItem === 'number') { | ||
return type === 'page' && page === matchItem; | ||
} | ||
else if (matchItem === '#') { | ||
return type === 'page'; | ||
} | ||
else if (matchItem === '…') { | ||
return type === '…L' || type === '…R'; | ||
} | ||
else if (matchItem === '*') { | ||
return type === 'active'; | ||
} | ||
else { | ||
throw new UnsupportedValueError(matchItem); | ||
} | ||
}); | ||
} | ||
export function compositionMatchesEnd(composition, endIndex, pattern) { | ||
return compositionMatches(composition, endIndex - pattern.length + 1, pattern); | ||
} |
import { CompositionItem } from '../compositionItem.js'; | ||
import { NarrowBehaviour } from '../narrowBehaviour.js'; | ||
export type NarrowStrategy = 'dropEllipsis' | 'dropNav'; | ||
export declare function narrowToWideCompositions({ current, total, narrowBehaviour, renderNav, }: { | ||
@@ -5,0 +4,0 @@ current: number; |
@@ -5,1 +5,5 @@ export declare function isNumber(val: any): val is number; | ||
export declare function sanatizeBoolean(maybeBoolean: unknown): boolean | undefined; | ||
export declare function findLastIndex<T>(array: ReadonlyArray<T>, predicate: (item: T) => boolean): number; | ||
export declare class UnsupportedValueError extends Error { | ||
constructor(value: never); | ||
} |
@@ -15,1 +15,14 @@ export function isNumber(val) { | ||
} | ||
// TODO: use native findLastIndex in next major release | ||
export function findLastIndex(array, predicate) { | ||
for (let k = array.length - 1; k >= 0; k--) { | ||
if (predicate(array[k])) | ||
return k; | ||
} | ||
return -1; | ||
} | ||
export class UnsupportedValueError extends Error { | ||
constructor(value) { | ||
super('Unsupported value: ' + value); | ||
} | ||
} |
import { CompositionItem } from './compositionItem.js'; | ||
export type NarrowBehaviour = (composition: CompositionItem[]) => Generator<CompositionItem[]>; | ||
export declare function dropEllipsis(initialComposition: CompositionItem[]): Generator<CompositionItem[], void, unknown>; | ||
export declare function dropNav(initialComposition: CompositionItem[]): Generator<CompositionItem[], void, unknown>; | ||
export declare function dropEllipsisThenNav(initialComposition: CompositionItem[]): Generator<CompositionItem[], void, unknown>; | ||
export declare function dropNavThenEllipsis(initialComposition: CompositionItem[]): Generator<CompositionItem[], void, unknown>; | ||
/** | ||
* NarrowBehaviours will yield their narrowest composition first and then | ||
* yield a less narrow composition | ||
* They should not yield the initialComposition | ||
*/ | ||
export type NarrowBehaviour = (composition: ReadonlyArray<CompositionItem>, metaData?: NarrowBehaviourMetaData) => Generator<CompositionItem[]>; | ||
type NarrowBehaviourMetaData = { | ||
appliedBehaviours?: NarrowBehaviour[]; | ||
}; | ||
export declare function dropEllipsis(initialComposition: ReadonlyArray<CompositionItem>, metaData?: NarrowBehaviourMetaData): Generator<CompositionItem[], void, unknown>; | ||
export declare function dropNav(initialComposition: ReadonlyArray<CompositionItem>): Generator<CompositionItem[], void, unknown>; | ||
export declare function dropFirstAndLast(initialComposition: ReadonlyArray<CompositionItem>, metaData?: NarrowBehaviourMetaData): Generator<CompositionItem[], void, unknown>; | ||
export declare function dropEllipsisThenNav(initialComposition: ReadonlyArray<CompositionItem>): Generator<CompositionItem[], void, unknown>; | ||
export declare function dropNavThenEllipsis(initialComposition: ReadonlyArray<CompositionItem>): Generator<CompositionItem[], void, unknown>; | ||
/** | ||
* When combining NarrowBehaviours the behaviours will be applied in order: | ||
* the first behaviour will be used before subsequent behaviours | ||
* Compositions yielded from combineBehaviours will initially have | ||
* all behaviours applied in their narrowest form and then work through | ||
* each behaviour in turn (from last to first) | ||
*/ | ||
/** | ||
* Combine two or more narrowBehaviours | ||
*/ | ||
export declare const combine: (...behaviours: ReadonlyArray<NarrowBehaviour>) => NarrowBehaviour; | ||
export {}; |
@@ -1,6 +0,22 @@ | ||
import { containsEllipsis, isEllipsis, isNav, } from './compositionItem.js'; | ||
export function* dropEllipsis(initialComposition) { | ||
if (containsEllipsis(initialComposition)) { | ||
yield initialComposition.filter(item => !isEllipsis(item)); | ||
import { containsEllipsis, createEllipsis, getLastPage, isEllipsis, isNav, isPageWithNumber, compositionMatches, compositionMatchesEnd, } from './compositionItem.js'; | ||
import { findLastIndex } from './helpers/util.js'; | ||
export function* dropEllipsis(initialComposition, metaData) { | ||
const indicesToDrop = []; | ||
if (metaData?.appliedBehaviours?.includes(dropFirstAndLast)) { | ||
// 1, 2 => …, 2 => 2 | ||
const firstPageIndex = initialComposition.findIndex(item => isPageWithNumber(item, 1)); | ||
if (compositionMatches(initialComposition, firstPageIndex, [1, 2])) { | ||
indicesToDrop.push(firstPageIndex); | ||
} | ||
// n-1, n => n-1, … => n-1 | ||
/** last page */ | ||
const n = getLastPage(initialComposition); | ||
const lastPageIndex = findLastIndex(initialComposition, item => isPageWithNumber(item, n)); | ||
if (compositionMatchesEnd(initialComposition, lastPageIndex, [n - 1, n])) { | ||
indicesToDrop.push(lastPageIndex); | ||
} | ||
} | ||
if (containsEllipsis(initialComposition) || indicesToDrop.length > 0) { | ||
yield initialComposition.filter((item, index) => !isEllipsis(item) && !indicesToDrop.includes(index)); | ||
} | ||
} | ||
@@ -10,2 +26,60 @@ export function* dropNav(initialComposition) { | ||
} | ||
export function* dropFirstAndLast(initialComposition, metaData) { | ||
const ellipsisDropped = metaData?.appliedBehaviours?.includes(dropEllipsis); | ||
let composition = initialComposition.slice(); | ||
/** | ||
* normal | ||
* 1* 1* | ||
* 1, 2* 1, 2* | ||
* 1, 2, 3* 1, 2, 3* | ||
* 1, 2, 3, 4* …, 3, 4* | ||
* 1, …, p, p+1* …, p, p+1* (p>=4) | ||
* | ||
* after dropEllipsis | ||
* 1* 1* | ||
* 1, 2* 1, 2* | ||
* 1, 2, 3* 2, 3* | ||
* 1, 2, 3, 4* 3, 4* | ||
* 1, p, p+1* p, p+1 (p>=4) | ||
*/ | ||
const firstPageIndex = composition.findIndex(item => isPageWithNumber(item, 1)); | ||
if (ellipsisDropped) { | ||
if (compositionMatches(composition, firstPageIndex, [1, 2, 3, '*'])) { | ||
composition.splice(firstPageIndex, 2); | ||
} | ||
else if (compositionMatches(composition, firstPageIndex, [1, '#', '*'])) { | ||
composition.splice(firstPageIndex, 1); | ||
} | ||
} | ||
else { | ||
if (compositionMatches(composition, firstPageIndex, [1, 2, 3, '*'])) { | ||
composition.splice(firstPageIndex, 2, createEllipsis('L')); | ||
} | ||
else if (compositionMatches(composition, firstPageIndex, [1, '…', '#'])) { | ||
composition.splice(firstPageIndex, 1); | ||
} | ||
} | ||
/** last page */ | ||
const n = getLastPage(composition); | ||
const lastPageIndex = findLastIndex(composition, item => isPageWithNumber(item, n)); | ||
if (ellipsisDropped) { | ||
if (compositionMatchesEnd(composition, lastPageIndex, ['*', n - 2, n - 1, n])) { | ||
composition.splice(lastPageIndex - 1, 2); | ||
} | ||
else if (compositionMatchesEnd(composition, lastPageIndex, ['*', '#', n])) { | ||
composition.splice(lastPageIndex, 1); | ||
} | ||
} | ||
else { | ||
if (compositionMatchesEnd(composition, lastPageIndex, ['*', n - 2, n - 1, n])) { | ||
composition.splice(lastPageIndex - 1, 2, createEllipsis('R')); | ||
} | ||
else if (compositionMatchesEnd(composition, lastPageIndex, ['#', '…', n])) { | ||
composition.splice(lastPageIndex, 1); | ||
} | ||
} | ||
if (initialComposition.length !== composition.length) { | ||
yield composition; | ||
} | ||
} | ||
export function* dropEllipsisThenNav(initialComposition) { | ||
@@ -26,1 +100,37 @@ if (containsEllipsis(initialComposition)) { | ||
} | ||
/** | ||
* When combining NarrowBehaviours the behaviours will be applied in order: | ||
* the first behaviour will be used before subsequent behaviours | ||
* Compositions yielded from combineBehaviours will initially have | ||
* all behaviours applied in their narrowest form and then work through | ||
* each behaviour in turn (from last to first) | ||
*/ | ||
/** | ||
* Combine two or more narrowBehaviours | ||
*/ | ||
export const combine = (...behaviours) => initialComposition => combineRecursive(behaviours, [], initialComposition); | ||
function* combineRecursive(behaviours, previousBehaviours, initialComposition) { | ||
// if no behaviours then we are done | ||
if (behaviours.length === 0) | ||
return; | ||
const [firstBehaviour, ...remainingBehaviours] = behaviours; | ||
const firstBehaviourCompositions = firstBehaviour(initialComposition, { | ||
appliedBehaviours: previousBehaviours, | ||
}); | ||
const firstResult = firstBehaviourCompositions.next(); | ||
if (firstResult.done) { | ||
// if this behaviour did not yield anything then just move on to the next behaviour | ||
yield* combineRecursive(remainingBehaviours, [...previousBehaviours, firstBehaviour], initialComposition); | ||
} | ||
else { | ||
const firstComposition = firstResult.value; | ||
// the first composition will be the most reduced | ||
// this will be the composition for lower priority behaviours to | ||
// be applied on top of | ||
yield* combineRecursive(remainingBehaviours, [...previousBehaviours, firstBehaviour], firstComposition); | ||
// then yield this composition without any other behaviours applied | ||
yield firstComposition; | ||
// then yield the remaining compositions from this behaviour | ||
yield* firstBehaviourCompositions; | ||
} | ||
} |
{ | ||
"name": "react-responsive-pagination", | ||
"version": "2.2.4", | ||
"version": "2.3.0-beta.1", | ||
"description": "React component for responsive pagination", | ||
@@ -5,0 +5,0 @@ "author": "jonelantha", |
@@ -122,6 +122,6 @@ # React Responsive Pagination | ||
| Prop | Description | | ||
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ||
| **renderNav**<br />`boolean`<br />(optional) | When set to `false` the nav buttons (**«**/**»**) will not be rendered. Defaults to `true` | | ||
| **narrowBehaviour**<br />`NarrowBehaviour`<br />(optional) | Specify that nav buttons (**«**/**»**) and/or the ellipsis (**…**) can be dropped for very narrow widths (useful if the component is used in narrow widths with high page numbers)<br />Valid behaviours should be imported from `react-responsive-pagination/narrowBehaviour`, [see example](https://react-responsive-pagination.elantha.com/props/#misc-props)<br /><br />`dropEllipsis` - drop the ellipsis (**…**) for narrow widths<br />`dropNav` - drop the nav (**«**/**»**) for narrow widths<br />`dropNavThenEllipsis` - drop the nav initially and then further drop the ellipsis if required<br />`dropEllipsisThenNav` - drop the ellipsis initially and then further drop the nav if required | | ||
| Prop | Description | | ||
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| **renderNav**<br />`boolean`<br />(optional) | When set to `false` the nav buttons (**«**/**»**) will not be rendered. Defaults to `true` | | ||
| **narrowBehaviour**<br />`NarrowBehaviour`<br />(optional) | Specify that nav buttons (**«**/**»**) and/or the ellipsis (**…**) can be dropped for very narrow widths (useful if the component is used in narrow widths with high page numbers)<br />Valid behaviours should be imported from `react-responsive-pagination/narrowBehaviour`, [see example](https://react-responsive-pagination.elantha.com/props/#misc-props)<br /><br />`dropEllipsis` - drop the ellipsis (**…**) for narrow widths<br />`dropNav` - drop the nav (**«**/**»**) for narrow widths<br />`dropFirstAndLast` - drop the first and last pages for narrow widths<br /><br />Use the `combine` helper to combine narrowBehaviours, example:<br />`narrowBehaviour={combine(dropNav, dropEllipsis)}` - drop the nav initially and then further drop the ellipsis if required<br />`combine` should also be imported from `react-responsive-pagination/narrowBehaviour` [see examples](https://react-responsive-pagination.elantha.com/props/#misc-props) | | ||
@@ -128,0 +128,0 @@ See [Props Reference](https://react-responsive-pagination.elantha.com/props) for the full list |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
95314
1561
1