@purpurds/pagination 5.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/LICENSE.txt +73 -0
  2. package/dist/navigation.d.ts +2 -0
  3. package/dist/navigation.d.ts.map +1 -0
  4. package/dist/pagination-page-selector.d.ts +17 -0
  5. package/dist/pagination-page-selector.d.ts.map +1 -0
  6. package/dist/pagination-page-size-selector.d.ts +13 -0
  7. package/dist/pagination-page-size-selector.d.ts.map +1 -0
  8. package/dist/pagination-page-trigger.d.ts +14 -0
  9. package/dist/pagination-page-trigger.d.ts.map +1 -0
  10. package/dist/pagination-pages.d.ts +13 -0
  11. package/dist/pagination-pages.d.ts.map +1 -0
  12. package/dist/pagination-step-trigger.d.ts +14 -0
  13. package/dist/pagination-step-trigger.d.ts.map +1 -0
  14. package/dist/pagination-truncation-separator.d.ts +11 -0
  15. package/dist/pagination-truncation-separator.d.ts.map +1 -0
  16. package/dist/pagination.cjs.js +134 -0
  17. package/dist/pagination.cjs.js.map +1 -0
  18. package/dist/pagination.d.ts +24 -0
  19. package/dist/pagination.d.ts.map +1 -0
  20. package/dist/pagination.es.js +4041 -0
  21. package/dist/pagination.es.js.map +1 -0
  22. package/dist/styles.css +1 -0
  23. package/dist/types.d.ts +35 -0
  24. package/dist/types.d.ts.map +1 -0
  25. package/dist/use-page-options.hook.d.ts +8 -0
  26. package/dist/use-page-options.hook.d.ts.map +1 -0
  27. package/dist/use-page-size-options.hook.d.ts +8 -0
  28. package/dist/use-page-size-options.hook.d.ts.map +1 -0
  29. package/dist/use-pagination-page-selector-events.hook.d.ts +10 -0
  30. package/dist/use-pagination-page-selector-events.hook.d.ts.map +1 -0
  31. package/dist/use-pagination-pages.hook.d.ts +7 -0
  32. package/dist/use-pagination-pages.hook.d.ts.map +1 -0
  33. package/package.json +69 -0
  34. package/src/global.d.ts +4 -0
  35. package/src/navigation.ts +3 -0
  36. package/src/pagination-page-selector.module.scss +26 -0
  37. package/src/pagination-page-selector.test.tsx +78 -0
  38. package/src/pagination-page-selector.tsx +105 -0
  39. package/src/pagination-page-size-selector.module.scss +34 -0
  40. package/src/pagination-page-size-selector.test.tsx +73 -0
  41. package/src/pagination-page-size-selector.tsx +78 -0
  42. package/src/pagination-page-trigger.module.scss +41 -0
  43. package/src/pagination-page-trigger.test.tsx +172 -0
  44. package/src/pagination-page-trigger.tsx +93 -0
  45. package/src/pagination-pages.module.scss +8 -0
  46. package/src/pagination-pages.test.tsx +95 -0
  47. package/src/pagination-pages.tsx +72 -0
  48. package/src/pagination-step-trigger.module.scss +27 -0
  49. package/src/pagination-step-trigger.test.tsx +106 -0
  50. package/src/pagination-step-trigger.tsx +92 -0
  51. package/src/pagination-truncation-separator.module.scss +8 -0
  52. package/src/pagination-truncation-separator.test.tsx +25 -0
  53. package/src/pagination-truncation-separator.tsx +29 -0
  54. package/src/pagination.module.scss +105 -0
  55. package/src/pagination.stories.tsx +197 -0
  56. package/src/pagination.test.tsx +76 -0
  57. package/src/pagination.tsx +177 -0
  58. package/src/types.ts +43 -0
  59. package/src/use-page-options.hook.ts +21 -0
  60. package/src/use-page-size-options.hook.ts +21 -0
  61. package/src/use-pagination-page-selector-events.hook.ts +59 -0
  62. package/src/use-pagination-pages.hook.ts +41 -0
@@ -0,0 +1 @@
1
+ ._purpur-pagination_9shz4_1{align-items:center;display:flex;flex-direction:column-reverse;gap:var(--purpur-spacing-300)}@media screen and (min-width: 1024px){._purpur-pagination_9shz4_1{align-items:center;display:grid;gap:var(--purpur-spacing-100);grid-template-areas:"pagesize pagination empty";grid-template-columns:1fr 3fr 1fr}}._purpur-pagination__page-size-select-container_9shz4_16{flex:0 0 auto;width:100%}@media screen and (min-width: 1024px){._purpur-pagination__page-size-select-container_9shz4_16{grid-area:pagesize;width:auto}}._purpur-pagination__pagination-container_9shz4_26{align-items:center;display:inline-flex;gap:var(--purpur-spacing-200);margin:0 auto;max-width:calc(3 * var(--purpur-spacing-1200));width:100%}@media screen and (min-width: 600px){._purpur-pagination__pagination-container_9shz4_26{gap:var(--purpur-spacing-400);max-width:none;width:auto}}@media screen and (min-width: 1024px){._purpur-pagination__pagination-container_9shz4_26{grid-area:pagination;justify-self:center}}._purpur-pagination__step-trigger-label_9shz4_47{border:0;clip:rect(0,0,0,0);display:block;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}@media screen and (min-width: 1024px){._purpur-pagination__step-trigger-label_9shz4_47{clip:auto;display:inline;height:auto;margin:0;overflow:visible;position:static;white-space:normal;width:auto}}._purpur-pagination__step-trigger_9shz4_47{flex:0 0 auto}._purpur-pagination__page-trigger-container_9shz4_74{display:flex;flex:1 1 auto;justify-content:center}._purpur-pagination_9shz4_1 ._purpur-pagination__pages_9shz4_79{display:none}._purpur-pagination_9shz4_1 ._purpur-pagination__pages--visible_9shz4_82{display:flex}@media screen and (min-width: 600px){._purpur-pagination_9shz4_1 ._purpur-pagination__pages_9shz4_79{display:flex}}._purpur-pagination_9shz4_1 ._purpur-pagination__page-selector_9shz4_90{display:none}._purpur-pagination_9shz4_1 ._purpur-pagination__page-selector--visible_9shz4_93{display:flex}@media screen and (min-width: 600px){._purpur-pagination_9shz4_1 ._purpur-pagination__page-selector_9shz4_90{display:none}}._purpur-pagination-page-selector_1ca14_1{align-items:center;display:flex;gap:var(--purpur-spacing-150)}._purpur-pagination-page-selector__autocomplete_1ca14_6{max-width:var(--purpur-spacing-1000);width:100%}._purpur-pagination-page-selector__input_1ca14_10 input[type=number]{-moz-appearance:textfield}._purpur-pagination-page-selector__input_1ca14_10 input[type=number]::-webkit-outer-spin-button,._purpur-pagination-page-selector__input_1ca14_10 input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}._purpur-pagination-page-selector__of-total-pages-label_1ca14_18{flex:0 0 auto}._purpur-pagination-page-size-selector_l9us8_1{align-items:center;display:flex;gap:var(--purpur-spacing-150);justify-content:center}@media screen and (min-width: 1024px){._purpur-pagination-page-size-selector_l9us8_1{justify-content:flex-start}}._purpur-pagination-page-size-selector__select_l9us8_12{flex:1 0 var(--purpur-spacing-1000);max-width:var(--purpur-spacing-1000);width:100%}._purpur-pagination-page-size-selector_l9us8_1 ._purpur-pagination-page-size-selector__items-per-page-label_l9us8_17{flex:0 1 auto;display:block;font-weight:var(--purpur-typography-weight-normal);-webkit-hyphens:none;hyphens:none;line-height:var(--purpur-typography-line-height-loose);margin:0;overflow:hidden;text-overflow:ellipsis}._purpur-pagination-page-size-selector_l9us8_1 ._purpur-pagination-page-size-selector__items-per-page-label-text_l9us8_27{white-space:nowrap}._purpur-pagination-page-trigger_1gni9_1{display:block}._purpur-pagination-page-trigger__trigger-item_1gni9_4{align-items:center;border-radius:var(--purpur-border-radius-full);border-width:0;cursor:pointer;display:inline-flex;font-size:var(--purpur-typography-scale-100);font-family:var(--purpur-typography-family-default);font-weight:var(--purpur-typography-weight-medium);gap:var(--purpur-spacing-100);justify-content:center;line-height:var(--purpur-spacing-300);outline:0;position:relative;text-decoration:none;transition:all var(--purpur-motion-duration-100) ease;width:auto}._purpur-pagination-page-trigger__trigger-item_1gni9_4:focus:before{border-radius:var(--purpur-border-radius-full);box-shadow:0 0 0 var(--purpur-border-width-sm) var(--purpur-color-border-interactive-focus);content:"";display:block;inset:calc(var(--purpur-spacing-25) * -1);position:absolute}._purpur-pagination-page-trigger__trigger-item_1gni9_4:focus:not(:focus-visible):before{box-shadow:none}._purpur-pagination-page-trigger__trigger-item_1gni9_4 svg{display:block}._purpur-pagination-page-trigger__trigger-item--sm_1gni9_36{padding:var(--purpur-spacing-100) var(--purpur-spacing-250)}._purpur-pagination-page-trigger__trigger-item--sm_1gni9_36._purpur-pagination-page-trigger__trigger-item--icon-only_1gni9_39{padding:var(--purpur-spacing-100)}._purpur-pagination-page-trigger__trigger-item--md_1gni9_42{padding:var(--purpur-spacing-150) var(--purpur-spacing-300)}._purpur-pagination-page-trigger__trigger-item--md_1gni9_42._purpur-pagination-page-trigger__trigger-item--icon-only_1gni9_39{padding:var(--purpur-spacing-150)}._purpur-pagination-page-trigger__trigger-item--lg_1gni9_48{padding:var(--purpur-spacing-200) var(--purpur-spacing-400)}._purpur-pagination-page-trigger__trigger-item--lg_1gni9_48._purpur-pagination-page-trigger__trigger-item--icon-only_1gni9_39{padding:var(--purpur-spacing-200)}._purpur-pagination-page-trigger__trigger-item--full-width_1gni9_54{width:100%}._purpur-pagination-page-trigger__trigger-item--primary_1gni9_57{background-color:var(--purpur-color-background-interactive-primary);color:var(--purpur-color-text-interactive-on-primary)}._purpur-pagination-page-trigger__trigger-item--primary_1gni9_57:hover:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-primary-hover)}._purpur-pagination-page-trigger__trigger-item--primary_1gni9_57:active:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-primary-active)}._purpur-pagination-page-trigger__trigger-item--primary-negative_1gni9_67{background-color:var(--purpur-color-background-interactive-primary-negative);color:var(--purpur-color-text-interactive-on-primary-negative)}._purpur-pagination-page-trigger__trigger-item--primary-negative_1gni9_67:hover:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-primary-negative-hover);border-color:var(--purpur-color-background-interactive-primary-negative-hover)}._purpur-pagination-page-trigger__trigger-item--primary-negative_1gni9_67:active:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-primary-negative-active);border-color:var(--purpur-color-background-interactive-primary-negative-active)}._purpur-pagination-page-trigger__trigger-item--secondary_1gni9_79{background-color:var(--purpur-color-background-interactive-transparent);box-shadow:inset 0 0 0 var(--purpur-border-width-xs) var(--purpur-color-border-interactive-primary);color:var(--purpur-color-text-interactive-primary)}._purpur-pagination-page-trigger__trigger-item--secondary_1gni9_79:hover:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-transparent-hover)}._purpur-pagination-page-trigger__trigger-item--secondary_1gni9_79:active:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-transparent-active)}._purpur-pagination-page-trigger__trigger-item--secondary-negative_1gni9_90{background-color:var(--purpur-color-background-interactive-transparent);box-shadow:inset 0 0 0 var(--purpur-border-width-xs) var(--purpur-color-border-interactive-primary-negative);color:var(--purpur-color-text-interactive-primary-negative)}._purpur-pagination-page-trigger__trigger-item--secondary-negative_1gni9_90:hover:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-transparent-negative-hover)}._purpur-pagination-page-trigger__trigger-item--secondary-negative_1gni9_90:active:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-transparent-negative-active)}._purpur-pagination-page-trigger__trigger-item--expressive_1gni9_101{background-color:var(--purpur-color-background-interactive-expressive);color:var(--purpur-color-text-interactive-on-expressive)}._purpur-pagination-page-trigger__trigger-item--expressive_1gni9_101:hover:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-expressive-hover)}._purpur-pagination-page-trigger__trigger-item--expressive_1gni9_101:active:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-expressive-active)}._purpur-pagination-page-trigger__trigger-item--expressive-negative_1gni9_111{background-color:var(--purpur-color-background-interactive-expressive-negative);color:var(--purpur-color-text-interactive-on-expressive-negative)}._purpur-pagination-page-trigger__trigger-item--expressive-negative_1gni9_111:hover:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-expressive-negative-hover);color:var(--purpur-color-text-interactive-on-expressive-negative-hover)}._purpur-pagination-page-trigger__trigger-item--expressive-negative_1gni9_111:active:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-expressive-negative-active);color:var(--purpur-color-text-interactive-on-expressive-negative-active)}._purpur-pagination-page-trigger__trigger-item--page-trigger_1gni9_123{background-color:var(--purpur-color-background-interactive-transparent);box-sizing:border-box;color:var(--purpur-color-text-interactive-primary);height:calc(var(--purpur-spacing-150) + var(--purpur-spacing-400));padding:var(--purpur-spacing-150);transition:background-color var(--purpur-duration-100) var(--purpur-timing-standard),color var(--purpur-duration-100) var(--purpur-timing-standard);width:calc(var(--purpur-spacing-150) + var(--purpur-spacing-400))}._purpur-pagination-page-trigger__trigger-item_1gni9_4:hover:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61):not(._purpur-pagination-page-trigger__trigger-item--selected_1gni9_132){background-color:var(--purpur-color-background-interactive-transparent-hover)}._purpur-pagination-page-trigger__trigger-item_1gni9_4:active:not(._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61){background-color:var(--purpur-color-background-interactive-transparent-active)}._purpur-pagination-page-trigger__trigger-item--selected_1gni9_132{background-color:var(--purpur-color-background-interactive-primary);color:var(--purpur-color-text-interactive-on-primary)}._purpur-pagination-page-trigger__trigger-item--disabled_1gni9_61{background-color:var(--purpur-color-background-interactive-transparent);box-shadow:none;color:var(--purpur-color-text-weak);cursor:not-allowed}._purpur-pagination-pages_2g8is_1{display:flex;gap:var(--purpur-spacing-100);justify-content:center;list-style-type:none;margin:0;padding:0}._purpur-pagination-truncation-separator_b9cij_1{box-sizing:border-box;display:block;height:calc(2 * var(--purpur-spacing-250));padding:var(--purpur-spacing-150) 0;text-align:center;width:var(--purpur-spacing-250)}._purpur-pagination-step-trigger__trigger-item_104ss_1{align-items:center;border-radius:var(--purpur-border-radius-full);border-width:0;cursor:pointer;display:inline-flex;font-size:var(--purpur-typography-scale-100);font-family:var(--purpur-typography-family-default);font-weight:var(--purpur-typography-weight-medium);gap:var(--purpur-spacing-100);justify-content:center;line-height:var(--purpur-spacing-300);outline:0;position:relative;text-decoration:none;transition:all var(--purpur-motion-duration-100) ease;width:auto}._purpur-pagination-step-trigger__trigger-item_104ss_1:focus:before{border-radius:var(--purpur-border-radius-full);box-shadow:0 0 0 var(--purpur-border-width-sm) var(--purpur-color-border-interactive-focus);content:"";display:block;inset:calc(var(--purpur-spacing-25) * -1);position:absolute}._purpur-pagination-step-trigger__trigger-item_104ss_1:focus:not(:focus-visible):before{box-shadow:none}._purpur-pagination-step-trigger__trigger-item_104ss_1 svg{display:block}._purpur-pagination-step-trigger__trigger-item--sm_104ss_33{padding:var(--purpur-spacing-100) var(--purpur-spacing-250)}._purpur-pagination-step-trigger__trigger-item--sm_104ss_33._purpur-pagination-step-trigger__trigger-item--icon-only_104ss_36{padding:var(--purpur-spacing-100)}._purpur-pagination-step-trigger__trigger-item--md_104ss_39{padding:var(--purpur-spacing-150) var(--purpur-spacing-300)}._purpur-pagination-step-trigger__trigger-item--md_104ss_39._purpur-pagination-step-trigger__trigger-item--icon-only_104ss_36{padding:var(--purpur-spacing-150)}._purpur-pagination-step-trigger__trigger-item--lg_104ss_45{padding:var(--purpur-spacing-200) var(--purpur-spacing-400)}._purpur-pagination-step-trigger__trigger-item--lg_104ss_45._purpur-pagination-step-trigger__trigger-item--icon-only_104ss_36{padding:var(--purpur-spacing-200)}._purpur-pagination-step-trigger__trigger-item--full-width_104ss_51{width:100%}._purpur-pagination-step-trigger__trigger-item--primary_104ss_54{background-color:var(--purpur-color-background-interactive-primary);color:var(--purpur-color-text-interactive-on-primary)}._purpur-pagination-step-trigger__trigger-item--primary_104ss_54:hover:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-primary-hover)}._purpur-pagination-step-trigger__trigger-item--primary_104ss_54:active:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-primary-active)}._purpur-pagination-step-trigger__trigger-item--primary-negative_104ss_64{background-color:var(--purpur-color-background-interactive-primary-negative);color:var(--purpur-color-text-interactive-on-primary-negative)}._purpur-pagination-step-trigger__trigger-item--primary-negative_104ss_64:hover:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-primary-negative-hover);border-color:var(--purpur-color-background-interactive-primary-negative-hover)}._purpur-pagination-step-trigger__trigger-item--primary-negative_104ss_64:active:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-primary-negative-active);border-color:var(--purpur-color-background-interactive-primary-negative-active)}._purpur-pagination-step-trigger__trigger-item--secondary_104ss_76{background-color:var(--purpur-color-background-interactive-transparent);box-shadow:inset 0 0 0 var(--purpur-border-width-xs) var(--purpur-color-border-interactive-primary);color:var(--purpur-color-text-interactive-primary)}._purpur-pagination-step-trigger__trigger-item--secondary_104ss_76:hover:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-transparent-hover)}._purpur-pagination-step-trigger__trigger-item--secondary_104ss_76:active:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-transparent-active)}._purpur-pagination-step-trigger__trigger-item--secondary-negative_104ss_87{background-color:var(--purpur-color-background-interactive-transparent);box-shadow:inset 0 0 0 var(--purpur-border-width-xs) var(--purpur-color-border-interactive-primary-negative);color:var(--purpur-color-text-interactive-primary-negative)}._purpur-pagination-step-trigger__trigger-item--secondary-negative_104ss_87:hover:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-transparent-negative-hover)}._purpur-pagination-step-trigger__trigger-item--secondary-negative_104ss_87:active:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-transparent-negative-active)}._purpur-pagination-step-trigger__trigger-item--expressive_104ss_98{background-color:var(--purpur-color-background-interactive-expressive);color:var(--purpur-color-text-interactive-on-expressive)}._purpur-pagination-step-trigger__trigger-item--expressive_104ss_98:hover:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-expressive-hover)}._purpur-pagination-step-trigger__trigger-item--expressive_104ss_98:active:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-expressive-active)}._purpur-pagination-step-trigger__trigger-item--expressive-negative_104ss_108{background-color:var(--purpur-color-background-interactive-expressive-negative);color:var(--purpur-color-text-interactive-on-expressive-negative)}._purpur-pagination-step-trigger__trigger-item--expressive-negative_104ss_108:hover:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-expressive-negative-hover);color:var(--purpur-color-text-interactive-on-expressive-negative-hover)}._purpur-pagination-step-trigger__trigger-item--expressive-negative_104ss_108:active:not(._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58){background-color:var(--purpur-color-background-interactive-expressive-negative-active);color:var(--purpur-color-text-interactive-on-expressive-negative-active)}._purpur-pagination-step-trigger__trigger-item--step-trigger_104ss_120{box-sizing:border-box;height:calc(var(--purpur-spacing-150) + var(--purpur-spacing-400));padding:var(--purpur-spacing-100);width:calc(var(--purpur-spacing-150) + var(--purpur-spacing-400))}@media screen and (min-width: 1024px){._purpur-pagination-step-trigger__trigger-item--step-trigger_104ss_120{padding:var(--purpur-spacing-100) var(--purpur-spacing-250);width:auto}}._purpur-pagination-step-trigger__trigger-item--disabled_104ss_58{background-color:var(--purpur-color-background-interactive-disabled);box-shadow:none;color:var(--purpur-color-text-weak);cursor:not-allowed}
@@ -0,0 +1,35 @@
1
+ export type PaginationInformation = {
2
+ pageSize: number;
3
+ currentPage: number;
4
+ };
5
+ export type NavigationInformation = PaginationInformation & {
6
+ url: string;
7
+ };
8
+ export type HrefInformation = {
9
+ pageSize: number;
10
+ page: number;
11
+ };
12
+ export type LinkProps = {
13
+ anchorTagElement?: Link;
14
+ asLink: true;
15
+ hrefGetter: (hrefInformation: HrefInformation) => string;
16
+ navigationFunction?: (navigationInformation: NavigationInformation) => void;
17
+ onPageChange?: (paginationChangeInformation: PaginationInformation) => void;
18
+ };
19
+ export type NoLinkProps = {
20
+ anchorTagElement?: never;
21
+ asLink?: never;
22
+ hrefGetter?: never;
23
+ navigationFunction?: never;
24
+ onPageChange: (paginationChangeInformation: PaginationInformation) => void;
25
+ };
26
+ export type InternalLinkProps = {
27
+ asLink: true;
28
+ hrefGetter: (page: number) => string;
29
+ };
30
+ export type InternalNoLinkProps = {
31
+ asLink?: never;
32
+ hrefGetter?: never;
33
+ };
34
+ export type Link = React.ForwardRefExoticComponent<React.AnchorHTMLAttributes<HTMLAnchorElement>> | "a";
35
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,qBAAqB,GAAG;IAC1D,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,gBAAgB,CAAC,EAAE,IAAI,CAAC;IACxB,MAAM,EAAE,IAAI,CAAC;IACb,UAAU,EAAE,CAAC,eAAe,EAAE,eAAe,KAAK,MAAM,CAAC;IACzD,kBAAkB,CAAC,EAAE,CAAC,qBAAqB,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC5E,YAAY,CAAC,EAAE,CAAC,2BAA2B,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC7E,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,UAAU,CAAC,EAAE,KAAK,CAAC;IACnB,kBAAkB,CAAC,EAAE,KAAK,CAAC;IAC3B,YAAY,EAAE,CAAC,2BAA2B,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC5E,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,IAAI,CAAC;IACb,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,UAAU,CAAC,EAAE,KAAK,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,IAAI,GACZ,KAAK,CAAC,yBAAyB,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC,GAC9E,GAAG,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { Option } from '@purpurds/autocomplete';
2
+
3
+ type UsePageOptionsHook = {
4
+ options: Option[];
5
+ };
6
+ export declare const usePageOptions: (numberOfPages: number) => UsePageOptionsHook;
7
+ export {};
8
+ //# sourceMappingURL=use-page-options.hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-page-options.hook.d.ts","sourceRoot":"","sources":["../src/use-page-options.hook.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,cAAc,kBAAmB,MAAM,KAAG,kBAatD,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { SelectOption } from '@purpurds/select';
2
+
3
+ type UsePageSizeOptionsHook = {
4
+ options: SelectOption[];
5
+ };
6
+ export declare const usePageSizeOptions: (availablePageSizes: number[]) => UsePageSizeOptionsHook;
7
+ export {};
8
+ //# sourceMappingURL=use-page-size-options.hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-page-size-options.hook.d.ts","sourceRoot":"","sources":["../src/use-page-size-options.hook.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,KAAK,sBAAsB,GAAG;IAC5B,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,kBAAkB,uBAAwB,MAAM,EAAE,KAAG,sBAajE,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Option } from '@purpurds/autocomplete';
2
+
3
+ type UsePaginationPageSelectorEvents = {
4
+ onKeyUp: (event: React.KeyboardEvent<HTMLInputElement>) => void;
5
+ onSelectionChange: (option?: Option) => void;
6
+ resetSelection: () => void;
7
+ };
8
+ export declare const usePaginationPageSelectorEvents: (currentPage: number, navigationFunction: (page: number, url: string) => void, numberOfPages: number, onPageChange: (page: number) => void, options: Option[], setValue: React.Dispatch<React.SetStateAction<string | number>>, asLink?: boolean, hrefGetter?: (page: number) => string) => UsePaginationPageSelectorEvents;
9
+ export {};
10
+ //# sourceMappingURL=use-pagination-page-selector-events.hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-pagination-page-selector-events.hook.d.ts","sourceRoot":"","sources":["../src/use-pagination-page-selector-events.hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,KAAK,+BAA+B,GAAG;IACrC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAChE,iBAAiB,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,+BAA+B,gBAC7B,MAAM,sBACC,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,iBACxC,MAAM,gBACP,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,WAC3B,MAAM,EAAE,YACP,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,WACtD,OAAO,eACH,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,KACpC,+BAyCF,CAAC"}
@@ -0,0 +1,7 @@
1
+ type UsePaginationPagesHook = {
2
+ pages: number[];
3
+ numberOfPages: number;
4
+ };
5
+ export declare const usePaginationPages: (totalItems: number, pageSize: number, currentPage: number) => UsePaginationPagesHook;
6
+ export {};
7
+ //# sourceMappingURL=use-pagination-pages.hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-pagination-pages.hook.d.ts","sourceRoot":"","sources":["../src/use-pagination-pages.hook.ts"],"names":[],"mappings":"AAEA,KAAK,sBAAsB,GAAG;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,kBAAkB,eACjB,MAAM,YACR,MAAM,eACH,MAAM,KAClB,sBAcF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@purpurds/pagination",
3
+ "version": "5.19.1",
4
+ "license": "AGPL-3.0-only",
5
+ "main": "./dist/pagination.cjs.js",
6
+ "types": "./dist/pagination.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/pagination.cjs.js",
10
+ "types": "./dist/pagination.d.ts",
11
+ "default": "./dist/pagination.es.js"
12
+ },
13
+ "./styles": "./dist/styles.css"
14
+ },
15
+ "source": "src/pagination.tsx",
16
+ "dependencies": {
17
+ "classnames": "~2.5.0",
18
+ "@purpurds/action": "5.19.1",
19
+ "@purpurds/label": "5.19.1",
20
+ "@purpurds/autocomplete": "5.19.1",
21
+ "@purpurds/select": "5.19.1",
22
+ "@purpurds/tokens": "5.19.1",
23
+ "@purpurds/paragraph": "5.19.1",
24
+ "@purpurds/text-field": "5.19.1",
25
+ "@purpurds/icon": "5.19.1"
26
+ },
27
+ "devDependencies": {
28
+ "@rushstack/eslint-patch": "~1.10.0",
29
+ "@storybook/blocks": "^8.2.6",
30
+ "@storybook/react": "^8.2.6",
31
+ "@telia/base-rig": "~8.2.0",
32
+ "@telia/react-rig": "~3.2.0",
33
+ "@testing-library/dom": "~9.3.3",
34
+ "@testing-library/jest-dom": "~6.4.0",
35
+ "@testing-library/react": "~14.3.0",
36
+ "@testing-library/user-event": "~14.5.1",
37
+ "@types/node": "20.12.12",
38
+ "@types/react-dom": "^18.3.0",
39
+ "@types/react": "^18.3.3",
40
+ "eslint-plugin-testing-library": "~6.2.0",
41
+ "eslint": "^8.57.0",
42
+ "jsdom": "~22.1.0",
43
+ "lint-staged": "~10.5.3",
44
+ "prettier": "~2.8.8",
45
+ "react-dom": "^18.3.1",
46
+ "react": "^18.3.1",
47
+ "storybook": "^8.2.6",
48
+ "typescript": "^5.5.4",
49
+ "vite": "5.3.4",
50
+ "vitest": "~1.5.0",
51
+ "@purpurds/component-rig": "1.0.0",
52
+ "@purpurds/skeleton": "5.19.1",
53
+ "@purpurds/listbox": "5.19.1"
54
+ },
55
+ "scripts": {
56
+ "build:dev": "vite",
57
+ "build:watch": "vite build --watch",
58
+ "build": "vite build",
59
+ "ci:build": "rushx build",
60
+ "coverage": "vitest run --coverage",
61
+ "lint:fix": "eslint . --fix",
62
+ "lint": "lint-staged --no-stash 2>&1",
63
+ "sbdev": "rush sbdev",
64
+ "test:unit": "vitest run --passWithNoTests",
65
+ "test:watch": "vitest --watch",
66
+ "test": "rushx test:unit",
67
+ "typecheck": "tsc -p ./tsconfig.json"
68
+ }
69
+ }
@@ -0,0 +1,4 @@
1
+ declare module "*.scss" {
2
+ const styles: { [className: string]: string };
3
+ export default styles;
4
+ }
@@ -0,0 +1,3 @@
1
+ export const navigateToPage = (url: string): void => {
2
+ window.location.href = url;
3
+ };
@@ -0,0 +1,26 @@
1
+ .purpur-pagination-page-selector {
2
+ align-items: center;
3
+ display: flex;
4
+ gap: var(--purpur-spacing-150);
5
+
6
+ &__autocomplete {
7
+ max-width: var(--purpur-spacing-1000);
8
+ width: 100%;
9
+ }
10
+
11
+ &__input {
12
+ input[type="number"] {
13
+ -moz-appearance: textfield;
14
+ }
15
+
16
+ input[type="number"]::-webkit-outer-spin-button,
17
+ input[type="number"]::-webkit-inner-spin-button {
18
+ -webkit-appearance: none;
19
+ margin: 0;
20
+ }
21
+ }
22
+
23
+ &__of-total-pages-label {
24
+ flex: 0 0 auto;
25
+ }
26
+ }
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+ import * as matchers from "@testing-library/jest-dom/matchers";
3
+ import { cleanup, render, screen } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { PaginationPageSelector } from "./pagination-page-selector";
8
+
9
+ expect.extend(matchers);
10
+
11
+ afterEach(() => {
12
+ vi.clearAllMocks();
13
+ cleanup();
14
+ });
15
+
16
+ describe("PaginationPageSelector", () => {
17
+ const defaultProps = {
18
+ currentPage: 1,
19
+ navigationFunction: vi.fn(),
20
+ numberOfPages: 5,
21
+ onPageChange: vi.fn(),
22
+ outOfLabel: "of",
23
+ pageSelectorListBoxLabel: "Select a page",
24
+ pageSelectorNoOptionsText: "Page does not exist",
25
+ };
26
+
27
+ it("renders without crashing", () => {
28
+ render(<PaginationPageSelector {...defaultProps} />);
29
+ expect(screen.getByTestId("purpur-pagination-page-selector")).toBeInTheDocument();
30
+ });
31
+
32
+ it("displays the correct number of total pages", () => {
33
+ render(<PaginationPageSelector {...defaultProps} />);
34
+ expect(screen.getByTestId(Selectors.OF_TOTAL_PAGES).textContent).toContain("of 5");
35
+ });
36
+
37
+ it("displays the correct page number in the autocomplete input", () => {
38
+ render(<PaginationPageSelector {...defaultProps} />);
39
+ expect(screen.getByTestId(Selectors.AUTO_COMPLETE_INPUT)).toHaveValue(1);
40
+ });
41
+
42
+ it("calls onPageChange when a page is clicked", async () => {
43
+ const onPageChange = vi.fn();
44
+ render(<PaginationPageSelector {...defaultProps} onPageChange={onPageChange} />);
45
+
46
+ await userEvent.click(screen.getByTestId(Selectors.AUTO_COMPLETE_INPUT));
47
+ await userEvent.click(screen.getByText("2"));
48
+ expect(onPageChange).toHaveBeenCalledWith(2);
49
+ });
50
+
51
+ it("calls navigationFunction when a page is clicked and link mode is set", async () => {
52
+ const onPageChange = vi.fn();
53
+ const navigationFunction = vi.fn();
54
+ const hrefGetter = vi.fn().mockReturnValue("#page-2");
55
+ render(
56
+ <PaginationPageSelector
57
+ {...defaultProps}
58
+ asLink
59
+ hrefGetter={hrefGetter}
60
+ onPageChange={onPageChange}
61
+ navigationFunction={navigationFunction}
62
+ />
63
+ );
64
+
65
+ await userEvent.click(screen.getByTestId(Selectors.AUTO_COMPLETE_INPUT));
66
+ await userEvent.click(screen.getByText("2"));
67
+ expect(onPageChange).toHaveBeenCalledWith(2);
68
+ expect(hrefGetter).toHaveBeenCalledWith(2);
69
+ expect(navigationFunction).toHaveBeenCalledWith(2, "#page-2");
70
+ });
71
+ });
72
+
73
+ const Selectors = {
74
+ PAGE_SELECTOR: "purpur-pagination-page-selector",
75
+ OF_TOTAL_PAGES: "purpur-pagination-page-selector-of-total-pages",
76
+ AUTO_COMPLETE: "purpur-pagination-page-selector-autocomplete",
77
+ AUTO_COMPLETE_INPUT: "purpur-pagination-page-selector-autocomplete-input-input",
78
+ };
@@ -0,0 +1,105 @@
1
+ import React, { FormEvent, ForwardedRef, forwardRef, useId } from "react";
2
+ import { Autocomplete } from "@purpurds/autocomplete";
3
+ import { Paragraph } from "@purpurds/paragraph";
4
+ import { TextField } from "@purpurds/text-field";
5
+ import c from "classnames/bind";
6
+
7
+ import styles from "./pagination-page-selector.module.scss";
8
+ import { InternalLinkProps, InternalNoLinkProps } from "./types";
9
+ import { usePageOptions } from "./use-page-options.hook";
10
+ import { usePaginationPageSelectorEvents } from "./use-pagination-page-selector-events.hook";
11
+ const cx = c.bind(styles);
12
+
13
+ export type PaginationPageSelectorProps = {
14
+ ["data-testid"]?: string;
15
+ className?: string;
16
+ currentPage: number;
17
+ navigationFunction: (page: number, url: string) => void;
18
+ numberOfPages: number;
19
+ onPageChange: (page: number) => void;
20
+ outOfLabel: string;
21
+ pageSelectorAutocompleteId?: string;
22
+ pageSelectorListBoxLabel: string;
23
+ pageSelectorNoOptionsText: string;
24
+ } & (InternalLinkProps | InternalNoLinkProps);
25
+
26
+ const rootClassName = "purpur-pagination-page-selector";
27
+
28
+ export const PaginationPageSelector = forwardRef(
29
+ (
30
+ {
31
+ ["data-testid"]: dataTestId = "purpur-pagination-page-selector",
32
+ asLink,
33
+ className,
34
+ currentPage,
35
+ hrefGetter,
36
+ navigationFunction,
37
+ numberOfPages,
38
+ onPageChange,
39
+ outOfLabel,
40
+ pageSelectorAutocompleteId,
41
+ pageSelectorListBoxLabel,
42
+ pageSelectorNoOptionsText,
43
+ ...props
44
+ }: PaginationPageSelectorProps,
45
+ ref: ForwardedRef<HTMLDivElement>
46
+ ) => {
47
+ const id = useId();
48
+ const classes = cx([className, rootClassName]);
49
+ const [value, setValue] = React.useState<string | number>(currentPage);
50
+ const { options } = usePageOptions(numberOfPages);
51
+ const { onSelectionChange, onKeyUp, resetSelection } = usePaginationPageSelectorEvents(
52
+ currentPage,
53
+ navigationFunction,
54
+ numberOfPages,
55
+ onPageChange,
56
+ options,
57
+ setValue,
58
+ asLink,
59
+ hrefGetter
60
+ );
61
+
62
+ React.useEffect(() => {
63
+ setValue(currentPage);
64
+ }, [currentPage]);
65
+
66
+ return (
67
+ <div className={classes} data-testid={dataTestId} ref={ref} {...props}>
68
+ <Autocomplete
69
+ className={cx(`${rootClassName}__autocomplete`)}
70
+ combobox
71
+ data-testid={`${dataTestId}-autocomplete`}
72
+ id={pageSelectorAutocompleteId ?? id}
73
+ listboxLabel={pageSelectorListBoxLabel}
74
+ noOptionsText={pageSelectorNoOptionsText}
75
+ onSelect={onSelectionChange}
76
+ options={options}
77
+ renderInput={(props) => (
78
+ <TextField
79
+ {...props}
80
+ className={cx(`${rootClassName}__input`)}
81
+ min={1}
82
+ max={numberOfPages}
83
+ onBlur={resetSelection}
84
+ onInput={(event: FormEvent<HTMLInputElement>) => {
85
+ setValue((event.target as HTMLInputElement).value);
86
+ }}
87
+ onKeyUp={onKeyUp}
88
+ type="number"
89
+ value={value}
90
+ />
91
+ )}
92
+ selectedOption={options[currentPage - 1]}
93
+ />
94
+ <Paragraph
95
+ className={cx(`${rootClassName}__of-total-pages-label`)}
96
+ data-testid={`${dataTestId}-of-total-pages`}
97
+ >
98
+ {`${outOfLabel} ${numberOfPages}`}
99
+ </Paragraph>
100
+ </div>
101
+ );
102
+ }
103
+ );
104
+
105
+ PaginationPageSelector.displayName = "PaginationPageSelector";
@@ -0,0 +1,34 @@
1
+ @import "@purpurds/tokens/breakpoint/variables";
2
+
3
+ .purpur-pagination-page-size-selector {
4
+ $root: &;
5
+ align-items: center;
6
+ display: flex;
7
+ gap: var(--purpur-spacing-150);
8
+ justify-content: center;
9
+
10
+ @media screen and (min-width: $purpur-breakpoint-lg) {
11
+ justify-content: flex-start;
12
+ }
13
+
14
+ &__select {
15
+ flex: 1 0 var(--purpur-spacing-1000);
16
+ max-width: var(--purpur-spacing-1000);
17
+ width: 100%;
18
+ }
19
+
20
+ #{$root}__items-per-page-label {
21
+ flex: 0 1 auto;
22
+ display: block;
23
+ font-weight: var(--purpur-typography-weight-normal);
24
+ hyphens: none;
25
+ line-height: var(--purpur-typography-line-height-loose);
26
+ margin: 0;
27
+ overflow: hidden;
28
+ text-overflow: ellipsis;
29
+ }
30
+
31
+ #{$root}__items-per-page-label-text {
32
+ white-space: nowrap;
33
+ }
34
+ }
@@ -0,0 +1,73 @@
1
+ import React from "react";
2
+ import * as matchers from "@testing-library/jest-dom/matchers";
3
+ import { cleanup, render, screen } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { PaginationPageSizeSelector } from "./pagination-page-size-selector";
8
+
9
+ expect.extend(matchers);
10
+
11
+ vi.mock("./navigation");
12
+
13
+ afterEach(() => {
14
+ vi.clearAllMocks();
15
+ cleanup();
16
+ });
17
+
18
+ describe("PaginationPageSizeSelector", () => {
19
+ const defaultProps = {
20
+ availablePageSizes: [10, 20, 30],
21
+ pageSizeLabel: "Items per page",
22
+ onPageSizeChange: vi.fn(),
23
+ pageSize: 10,
24
+ };
25
+
26
+ it("renders correctly with default props", () => {
27
+ render(<PaginationPageSizeSelector {...defaultProps} />);
28
+
29
+ expect(screen.getByTestId(Selectors.PAGINATION_PAGE_SIZE_SELECTOR)).toBeInTheDocument();
30
+ expect(screen.getByTestId(Selectors.ITEMS_PER_PAGE_LABEL)).toHaveTextContent("Items per page");
31
+ expect(screen.getByTestId(Selectors.PAGE_SIZE_SELECT)).toHaveValue("10");
32
+ });
33
+
34
+ it("calls onPageSizeChange when a new page size is selected", async () => {
35
+ render(<PaginationPageSizeSelector {...defaultProps} />);
36
+
37
+ const select = screen.getByTestId(Selectors.PAGE_SIZE_SELECT);
38
+ await userEvent.selectOptions(select, "20");
39
+
40
+ expect(defaultProps.onPageSizeChange).toHaveBeenCalledWith(20);
41
+ });
42
+
43
+ it("navigates to the correct page when asLink is true", async () => {
44
+ const hrefGetter = vi.fn().mockReturnValue("/page/1?pageSize=20");
45
+ const navigationFunction = vi.fn().mockReturnValue("/page/1?pageSize=20");
46
+
47
+ render(
48
+ <PaginationPageSizeSelector
49
+ {...defaultProps}
50
+ asLink
51
+ hrefGetter={hrefGetter}
52
+ navigationFunction={navigationFunction}
53
+ />
54
+ );
55
+
56
+ const select = screen.getByTestId(Selectors.PAGE_SIZE_SELECT);
57
+ await userEvent.selectOptions(select, "20");
58
+
59
+ expect(hrefGetter).toHaveBeenCalledWith({ page: 1, pageSize: 20 });
60
+ expect(defaultProps.onPageSizeChange).toHaveBeenCalledWith(20);
61
+ expect(navigationFunction).toHaveBeenCalledWith({
62
+ currentPage: 1,
63
+ pageSize: 20,
64
+ url: "/page/1?pageSize=20",
65
+ });
66
+ });
67
+ });
68
+
69
+ const Selectors = {
70
+ PAGINATION_PAGE_SIZE_SELECTOR: "purpur-pagination-page-size-selector",
71
+ ITEMS_PER_PAGE_LABEL: "purpur-pagination-page-size-selector-item-per-page-label",
72
+ PAGE_SIZE_SELECT: "purpur-pagination-page-size-selector-select-select",
73
+ };
@@ -0,0 +1,78 @@
1
+ import React, { ForwardedRef, forwardRef, useId } from "react";
2
+ import { Label } from "@purpurds/label";
3
+ import { Select } from "@purpurds/select";
4
+ import c from "classnames/bind";
5
+
6
+ import { navigateToPage } from "./navigation";
7
+ import styles from "./pagination-page-size-selector.module.scss";
8
+ import { LinkProps, NoLinkProps } from "./types";
9
+ import { usePageSizeOptions } from "./use-page-size-options.hook";
10
+ const cx = c.bind(styles);
11
+
12
+ export type PaginationPageSizeSelectorProps = {
13
+ ["data-testid"]?: string;
14
+ availablePageSizes: number[];
15
+ className?: string;
16
+ onPageSizeChange: (itemsPerPage: number) => void;
17
+ pageSize: number;
18
+ pageSizeLabel: string;
19
+ } & (LinkProps | NoLinkProps);
20
+
21
+ const rootClassName = "purpur-pagination-page-size-selector";
22
+
23
+ export const PaginationPageSizeSelector = forwardRef(
24
+ (
25
+ {
26
+ ["data-testid"]: dataTestId = "purpur-pagination-page-size-selector",
27
+ asLink,
28
+ availablePageSizes,
29
+ className,
30
+ hrefGetter,
31
+ navigationFunction,
32
+ onPageSizeChange,
33
+ pageSize,
34
+ pageSizeLabel,
35
+ ...props
36
+ }: PaginationPageSizeSelectorProps,
37
+ ref: ForwardedRef<HTMLDivElement>
38
+ ) => {
39
+ const selectId = useId();
40
+ const classes = cx([className, rootClassName]);
41
+ const { options } = usePageSizeOptions(availablePageSizes);
42
+
43
+ const onSelectionChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
44
+ const selectedPageSize = Number(event.target.value);
45
+ onPageSizeChange(selectedPageSize);
46
+ if (asLink) {
47
+ const url = hrefGetter({ page: 1, pageSize: selectedPageSize });
48
+ if (navigationFunction) {
49
+ navigationFunction({ currentPage: 1, pageSize: selectedPageSize, url });
50
+ } else {
51
+ navigateToPage(url);
52
+ }
53
+ }
54
+ };
55
+
56
+ return (
57
+ <div className={classes} data-testid={dataTestId} ref={ref} {...props}>
58
+ <Label
59
+ className={cx(`${rootClassName}__items-per-page-label`)}
60
+ data-testid={`${dataTestId}-item-per-page-label`}
61
+ htmlFor={selectId}
62
+ >
63
+ <span className={cx(`${rootClassName}__items-per-page-label-text`)}>{pageSizeLabel}</span>
64
+ </Label>
65
+ <Select
66
+ className={cx(`${rootClassName}__select`)}
67
+ data-testid={`${dataTestId}-select`}
68
+ id={selectId}
69
+ onChange={onSelectionChange}
70
+ options={options}
71
+ value={pageSize}
72
+ />
73
+ </div>
74
+ );
75
+ }
76
+ );
77
+
78
+ PaginationPageSizeSelector.displayName = "PaginationPageSizeSelector";
@@ -0,0 +1,41 @@
1
+ @import "@purpurds/action/src/action.scss";
2
+
3
+ .purpur-pagination-page-trigger {
4
+ display: block;
5
+
6
+ &__trigger-item {
7
+ $trigger-item: &;
8
+ @include action();
9
+
10
+ &--page-trigger {
11
+ background-color: var(--purpur-color-background-interactive-transparent);
12
+ box-sizing: border-box;
13
+ color: var(--purpur-color-text-interactive-primary);
14
+ height: calc(var(--purpur-spacing-150) + var(--purpur-spacing-400));
15
+ padding: var(--purpur-spacing-150);
16
+ transition: background-color var(--purpur-duration-100) var(--purpur-timing-standard),
17
+ color var(--purpur-duration-100) var(--purpur-timing-standard);
18
+ width: calc(var(--purpur-spacing-150) + var(--purpur-spacing-400));
19
+ }
20
+
21
+ &:hover:not(#{$trigger-item}--disabled):not(#{$trigger-item}--selected) {
22
+ background-color: var(--purpur-color-background-interactive-transparent-hover);
23
+ }
24
+
25
+ &:active:not(#{$trigger-item}--disabled) {
26
+ background-color: var(--purpur-color-background-interactive-transparent-active);
27
+ }
28
+
29
+ &--selected {
30
+ background-color: var(--purpur-color-background-interactive-primary);
31
+ color: var(--purpur-color-text-interactive-on-primary);
32
+ }
33
+
34
+ &--disabled {
35
+ background-color: var(--purpur-color-background-interactive-transparent);
36
+ box-shadow: none;
37
+ color: var(--purpur-color-text-weak);
38
+ cursor: not-allowed;
39
+ }
40
+ }
41
+ }