Skip to Main Content

Pagination

La pagination est une série de commandes qui permet aux utilisateurs de se déplacer dans un contenu se trouvant dans plusieurs pages faisant partie d’une seule page Web.

Utilisez-les lorsque :

  • Vous souhaitez afficher beaucoup de renseignements sur une page et vous ne voulez pas que l’utilisateur ait besoin de faire défiler la page.
  • Vous devez afficher beaucoup de renseignements sur une page et vous n’avez pas le luxe de disposer d’un espace vertical.

Composant interactif

1 à 12 de 300 résultats

Intégration des bibliothèques

Utilisez le code ci-dessous pour ajouter cet élément à votre projet. Téléchargez la dernière version de la bibliothèque pour implémenter facilement notre ensemble complet d’éléments.

Angular

Étape 1. Installer

Effectuer la commande suivante pour installer ce composant.

npm i [NPM-PLACE-HOLDER]/ds-pagination

Étape 2. Importer

Importer les éléments suivants dans votre dossier app.module.ts.

import { DsPaginationModule } from 'ds-pagination';
Pagination
<cpc-ds-pagination 
[totalRecords]="1000" 
[selectedIndex]="0" 
[pageSize]="12" 
[pageSizeOptions]="[12, 24, 48, 96, 112, 212, 412, 512, 612, 712]"></cpc-ds-pagination>
Propriétés
Nom Type Défaut Description
totalRecords number 100

Nombre total de résultats dans le jeu d’enregistrement.

selectedIndex number 0

Établit la sélection de pagination de départ.

pageSize number 12

Établit le nombre de résultats par page.

pageSizeOptions any  

Établit le nombre de résultats sur chaque page.

HTML

Étape 1: Importer CSS

Importer les éléments suivants dans votre thème ou votre page.

<link type="text/css" href="[CSS-PLACE-HOLDER]" rel="stylesheet">

Étape 2: Importer JS

Importer les éléments suivants dans votre thème ou votre page.

<script src="[JS-PLACE-HOLDER]"></script> 
<script>
    document.addEventListener('DOMContentLoaded', initPaging('custom-dropdown-pagination', 'custom-dropdown-label-pagination'), false);
</script>

Extraits de code

pagination
<div data-title="Pagination">
                <h2 class="section-heading">Pagination</h2>
                <div id="pageinationText" class="pageinationText" aria-live="polite"></div>
                <div class="pagination-block">
                    <div id="pagination" class="paginatin-item" role="navigation" aria-label="Pagination"></div>
                    <div class="resultsPerPage paginatin-item">
                        <label id="custom-dropdown-label-pagination">Results per page </label>
                        <select id="custom-dropdown-pagination" aria-labelledby="custom-dropdown-label-pagination" class="ds-cpc-control-select__tpl small-width">
                            <option selected="true">12</option>
                            <option>24</option>
                            <option>48</option>
                            <option>96</option>
                        </select>
                    </div>
                </div>
                <div class="spacer"></div>
    </div>

Interaction

La pagination doit être appliquée de façon uniforme dans l’ensemble du site Web et permettre aux utilisateurs de passer à la page suivante et à la page précédente de façon séquentielle. Les utilisateurs doivent également pouvoir se déplacer vers n’importe laquelle des 5 premières ou 5 dernières pages, selon celles qui sont affichées.

Emplacement

Le contrôleur de pagination doit être aligné au centre, sous le contenu qui est affiché. Les flèches servant à avancer et à reculer doivent être alignées avec le contrôleur, mais positionnées à l’extrême droite et à l’extrême gauche. Les flèches ne doivent s’afficher que lorsque cette interaction est possible.

Lorsqu’il y a 5 pages ou moins

Lorsqu’il y a 5 pages ou moins, les numéros de page doivent s’afficher de façon séquentielle, de 1 à 5.

Lorsqu’il y a plus de 5 pages

Lorsqu’il y a plus de 5 pages, les lignes directrices suivantes s’appliquent.

Légende
a = numéro de la page affichée moins 1
b = numéro de la page affichée
c = numéro de la page affichée plus 1
d = numéro de la dernière page moins 2
e = numéro de la dernière page moins 1
f = numéro de la dernière page

  • Si l’utilisateur consulte la page 1 ou 2, les numéros de page doivent s’afficher comme suit : 1, 2, 3, ..., f
  • Si l’utilisateur navigue au-delà des 3 premières pages et en deçà des 3 dernières pages, les numéros de page devraient s’afficher comme suit : 1, ..., a, b, c, ..., f
  • Si l’utilisateur consulte les dernières pages, les numéros de page devraient s’afficher comme suit : 1, ... , d, e, f
  • Lorsque l’utilisateur consulte une page, le numéro de cette dernière devrait s’afficher comme ayant un état sélectionné.
  • Les éléments interactifs sont indiqués grâce à la possibilité de cliquer lorsque l’utilisateur place son curseur sur l’élément.

Lignes directrices générales

Les utilisateurs qui n’ont pas de problèmes de vision savent clairement comment naviguer vers les pages précédentes ou suivantes dans les résultats de recherche ou dans les archives. Toutefois, pour ceux qui sont atteints d’un handicap visuel, nous devons indiquer aux logiciels de lecture d’écran où se trouve l’utilisateur et vers où il se dirige. Le titre d’onglet devrait tenir compte du type de contenu que l’utilisateur consulte.

Exemples de codes

Section de base

Veuillez noter que ce composant pourrait nécessiter un élément de style supplémentaire.

<!DOCTYPE html>
<html lang="en">
<head>
    <META http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link rel="stylesheet" type="text/css" href="css/pagination.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
    <title>Design System - Pagination</title>
</head>

<body>
        
    <div class="ds-container">
        <div class="example-container">
            <div id="pageinationText" class="pageinationText" aria-live="polite"></div>
            <div class="pagination-block">
                <div id="pagination" class="paginatin-item" role="navigation" aria-label="Pagination"></div>
                <div class="resultsPerPage paginatin-item">
                    <label id="custom-dropdown-label">Results per page </label>
                    <select id="custom-dropdown" aria-labelledby="custom-dropdown-label" class="ds-cpc-control-select__tpl small-width">
                        <option selected="true">12</option>
                        <option>24</option>
                        <option>48</option>
                        <option>96</option>
                    </select>
                </div>
            </div>
        </div>
    </div>
</body>

<script src="js/pagination.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="css/dropdown.css">
<script src="js/dropdown.js"></script>

</html>
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");
body{
  margin: 0;
  padding: 0;
}
.ds-container {
  margin: 0 auto;
  /* border: 1px solid #ccc; */
  /* padding: 20px; */
  text-align: center;
}
.example-container {
  /* width: 100%; */
  margin-top: 300px;
  text-align: right;
}
h1 {
  font-family: Roboto;
  font-size: 24px;
}

*,
*:before,
*:after {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

/*
Do not delete the following comment. It is essential for tracking purposes.
#Merc2021DoNotDelete
*/
.pagination-block{
  display: inline-block;
  height: 44px;
}

#pagination {
  display: inline-block;
  height: 44px;
  /* vertical-align: middle;
  height: auto; */
}
#pagination a, #pagination i {
  display: inline-block;
  vertical-align: middle;
}

.show-for-sr {
    position: absolute !important;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
}

#pagination button {
  width: 44px;
  height: 44px;
  padding: 10px 0;

  border: none;
  border-radius: 2px;
  text-align: center;
  margin-right: 8px;
  background-color: transparent;

  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  text-align: center;
  color: #333;
}
#pagination button:hover{
  box-shadow: inset 0 0 0 2px #004990;
  border: none;
  cursor: pointer;
}
#pagination button:focus {
  box-shadow: inset 0 0 0 2px #004990, 0 0 0 2px #fff, 0 0 0 4px #0467c6; 
  outline: none;
}

#pagination button.by-keyboard:focus {
  box-shadow: inset 0 0 0 2px #004990 !important;
  border: none;
}

#pagination button.current.by-keyboard:focus {
  box-shadow: inset 0 0 0 0px #004990 !important;
  border: none;
}

/* Disabled */
#pagination button:disabled {
  background-color: #eee;
  pointer-events: none;
}
#pagination button:disabled:hover {
  outline: none;
  box-shadow: none;
}
#pagination button.current {
    background-color: #0467c6;
    color: #fff;
    pointer-events: none;
    border: none;
    outline: none;
    box-shadow: none;
    font-weight: 400;
}
#pagination button:last-of-type{
  margin-right: 0px;
}


#pagination i {
  width: 44px;
  height: 44px;
  margin-right: 8px;
  text-align: center;
  pointer-events: none;
  background: url(../img/icons/ellipsis.svg) 50% center no-repeat;
}


.pageinationText{
  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #333;
  
  margin-bottom: 32px;
  text-align: left;
}

 
/* Mobile */
.mobileText{
  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #333;
  margin-right: 8px;

  display: none;
}

.paginationTextField{
  width: 61px;
  height: 44px;
  margin: 0 12px 0 0;
  padding: 10px 16px;
  border-radius: 2px;
  border: solid 1px #666;
  background-color: #fff;

  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #333;

  display: none;
}

/* Hide the number field arrow controls */
/* Chrome, Safari, Edge, Opera */
input.paginationTextField::-webkit-outer-spin-button,
input.paginationTextField::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
/* Firefox */
input.paginationTextField[type=number] {
  -moz-appearance: textfield;
}

.resultsPerPage{
  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 24px;
  letter-spacing: 0.5px;
  color: #333;
  margin-right: 0px;
  margin-left: 32px;
  display: inline-block;
}

@media only screen and (max-width: 730px) {
  .pagination-block{
    position: relative;
  }
  .resultsPerPage{
    display: block;
    text-align: right;
    margin-top: 24px;
    margin-left: 0;
  }
  .resultsPerPage.marginRight{
    position: absolute;
    top: 44px;
    margin-right: 0;
    right: 0;
  }
}

.desktop{
  display: inline-block;
  height: 44px;
}

.resultsPerPage label{
  display: inline-block;

  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 24px;
  letter-spacing: 0.5px;
  color: #333;
  margin-right: 0px;
}

@media only screen and (max-width: 40em) {
  .example-container {
    text-align: center;
  }
  .desktop{
    display: none;
  }
  .mobileText{
    display: inline-block;
  }
  .paginationTextField{
    display: inline-block;
  }
  .resultsPerPage{
    display: block;
    text-align: center;
    margin-top: 24px;
    margin-left: 0;
    left: 0;
  }
  .pagination-block{
    width: 100%;
  }
}


/* Input focus */
input[type="number"]:focus {
  outline: 0;
  border: solid 1px #0467c6;
  border-radius: 2px;
  
  -webkit-box-shadow: inset 1px 1px 0px 0px#0467c6, inset -1px -1px 0px 0px#0467c6;
  -moz-box-shadow: inset 1px 1px 0px 0px#0467c6, inset -1px -1px 0px 0px#0467c6;
  box-shadow: inset 1px 1px 0px 0px#0467c6, inset -1px -1px 0px 0px#0467c6;
}

.marginRight{
  margin-right: 28px;
}


/* 
Do not delete the following comment. It is essential for tracking purposes.
#Merc2021DoNotDelete 
*/
  
.cpc-control-select {
	position: relative;
	margin-bottom: 0;
	width: 100%;
   	max-width: 368px;
	/* Pagination changes */
	display: inline;
	margin-left: 12px;
}

@media only screen and (max-width: 40em) {
	.cpc-control-select {
		width: 100%;
		max-width: 100%;
		min-width: 100%;
	}
}

label{
  font-family: Roboto;
  font-size: 16px;
  line-height: 24px;
  font-weight: 500;
  font-stretch: normal;
  font-style: normal;
  letter-spacing: 0.5px;
  color: #333333;
}
/* label + .cpc-control-select {
  margin-top: 1px !important;
} */

 .cpc-control-select.disabled .cpc-control-select__toggle {
	 border: solid 1px #666;
	 background-color: #f0f0f0;
	 color: #666;

}
.cpc-control-select button.cpc-control-select__toggle:hover,  
.cpc-control-select.disabled .cpc-control-select__toggle:hover, 
 .cpc-control-select.disabled .cpc-control-select__toggle:focus {
	 outline: none !important;
	 -webkit-box-shadow: none;
	 -moz-box-shadow: none;
	 box-shadow: none !important;
	 border: solid 1px #666 !important;
}
 .cpc-control-select.disabled .cpc-control-select__toggle .cpc-control-select__toggle-indicator {
	 border-top-color: #888 !important;
}
 .cpc-control-select button.cpc-control-select__toggle {
	 font-family: Roboto;
	 font-size: 16px;
	 font-weight: 300;
	 font-stretch: normal;
	 font-style: normal;
	 line-height: 1.5;
	 letter-spacing: 0.5px;
	 color: #333;
	 position: relative;
	 height: 44px;
	 max-height: 44px;
	 width: 100% !important;
	 border-radius: 2px;
	 border: solid 1px #666 !important;
	 background-color: #fff;
	 padding: 10px 16px !important;
	 max-width: 368px;
	 /* min-width: 79px; */
	 text-align: left;
}

.cpc-control-select button.small-width{
	width: 79px !important;
	height: 44px !important;
	vertical-align: middle;
	margin-top: -2px;
}
.cpc-control-select__combo{
	width: 79px !important;
}

 .cpc-control-select button.cpc-control-select__toggle:focus {
	 outline: none !important;
	 border: 1px solid #0467c6 !important;
	 -webkit-box-shadow: inset 1px 1px 0px 0px #0467c6, inset -1px -1px 0px 0px #0467c6 !important;
	 -moz-box-shadow: inset 1px 1px 0px 0px #0467c6, inset -1px -1px 0px 0px #0467c6 !important;
	 box-shadow: inset 1px 1px 0px 0px #0467c6, inset -1px -1px 0px 0px #0467c6 !important;
}
 .cpc-control-select button.cpc-control-select__toggle.error {
	 border: 1px solid #ca261a !important;
}
 .cpc-control-select button.cpc-control-select__toggle.error:focus {
	 outline: none !important;
	 -webkit-box-shadow: inset 1px 1px 0px 0px #ca261a, inset -1px -1px 0px 0px #ca261a !important;
	 -moz-box-shadow: inset 1px 1px 0px 0px #ca261a, inset -1px -1px 0px 0px #ca261a !important;
	 box-shadow: inset 1px 1px 0px 0px #ca261a, inset -1px -1px 0px 0px #ca261a !important;
}
 .cpc-control-select button.cpc-control-select__toggle > span:first-child {
	 white-space: nowrap;
	 width: 100%;
	 display: block;
	 overflow: hidden;
	 text-align: left;
}
 .cpc-control-select button.cpc-control-select__toggle .cpc-control-select__toggle-indicator {
	 position: absolute;
	 right: 15px;
	 top: 17px;
	 background: url("../img/icons/drop-down-arrow.svg") center 50% no-repeat;
	 width: 12px;
	 height: 8px;
	 transition: transform 0.4s;
	 border: none;
}

.cpc-control-select .cpc-control-select__combo::-webkit-scrollbar {
	 width: 14px;
}
 .cpc-control-select .cpc-control-select__combo {
	 --scrollbarBG: #fff;
	 --thumbBG: #888;
	 scrollbar-width: thin;
	 scrollbar-color: var(--thumbBG) var(--scrollbarBG);
}
 .cpc-control-select .cpc-control-select__combo::-webkit-scrollbar-track {
	 background: var(--scrollbarBG);
	 margin-top: 5px;
}
 .cpc-control-select .cpc-control-select__combo::-webkit-scrollbar-thumb {
	 background-color: #888;
	 border: 4px solid #fff;
	 border-radius: 100px;
}
 .cpc-control-select .cpc-control-select__combo {
	 display: none;
	 z-index: 999999;
	 opacity: 1;
	 position: absolute;
	 left: 0;
	 right: 0;
	 max-width: 368px;
	 width: 100%;
	 
	 background-color: white;
	 border: 1px solid #666;
	 overflow: auto;
	 height: auto;
	 text-align: left;
	 border-radius: 2px;
	 -webkit-box-shadow: 0 3px 8px 0 rgba(17, 17, 17, 0.13);
	 -moz-box-shadow: 0 3px 8px 0 rgba(17, 17, 17, 0.13);
	 box-shadow: 0 3px 8px 0 rgba(17, 17, 17, 0.13);	 
}

@media only screen and (max-width: 40em) {
	.cpc-control-select .cpc-control-select__combo {
	width: 100%;
	max-width: 100%;
	min-width: 100%;
	}
}
 .cpc-control-select.cpc-control-select--open .cpc-control-select__toggle:focus {
	 outline: none !important;
	 border: 1px solid #666 !important;
	 -webkit-box-shadow: none;
	 -moz-box-shadow: none;
	 box-shadow: none !important;
}
 .cpc-control-select.cpc-control-select--open .cpc-control-select__toggle.error:focus {
	 outline: none;
	 border: 1px solid #ca261a;
	 -webkit-box-shadow: none;
	 -moz-box-shadow: none;
	 box-shadow: none;
}
 .cpc-control-select.cpc-control-select--open .cpc-control-select__combo {
	 display: inherit;
}
 .cpc-control-select.cpc-control-select--open .cpc-control-select__combo.openBelow {
	 border-top-left-radius: 0px;
	 border-top-right-radius: 0px;
	 border-top: 0px;
}
 .cpc-control-select.cpc-control-select--open .cpc-control-select__combo.openUp {
	 border-bottom-left-radius: 0px;
	 border-bottom-right-radius: 0px;
	 border-bottom: 0px;
}
 .cpc-control-select.cpc-control-select--open button.cpc-control-select__toggle.openBelow {
	 border-bottom-left-radius: 0px;
	 border-bottom-right-radius: 0px;
}
 .cpc-control-select.cpc-control-select--open button.cpc-control-select__toggle.openUp {
	 border-top-left-radius: 0px;
	 border-top-right-radius: 0px;
}
 .cpc-control-select.cpc-control-select--open button.cpc-control-select__toggle.error.openBelow {
	 border-bottom-left-radius: 0px;
	 border-bottom-right-radius: 0px;
	 border-bottom: 1px solid #666666 !important;
}
 .cpc-control-select.cpc-control-select--open button.cpc-control-select__toggle.error.openUp {
	 border-top-left-radius: 0px;
	 border-top-right-radius: 0px;
	 border-top: 1px solid #666666 !important;
}
 .cpc-control-select.cpc-control-select--open .cpc-control-select__toggle-indicator {
	 transform: rotate(-180deg);
}
 .cpc-control-select ul {
	 list-style: none;
   margin: 0;
   padding-left: 0;
}
 .cpc-control-select ul li {
	 height: 40px;
	 padding: 8px 15px;
	 margin-bottom: 0;
	 font-family: Roboto;
	 font-size: 16px;
	 font-weight: 300;
	 font-stretch: normal;
	 font-style: normal;
	 line-height: 1.5;
	 letter-spacing: 0.5px;
	 color: #333;
}
 .cpc-control-select ul li span {
	 overflow: hidden;
	 width: 100%;
	 display: inline-block;
}
 .cpc-control-select ul li.selected, .cpc-control-select ul li:focus, .cpc-control-select ul li:hover, .cpc-control-select ul li.selected span, .cpc-control-select ul li:focus span, .cpc-control-select ul li:hover span {
	 background-color: #0467c6 !important;
	 color: #fff;
	 outline: none !important;
	 font-weight: 400;
}
 select.cpc-control-select__tpl {
	 display: none;
}
.ds-error {
  font-family: Roboto;
  font-size: 16px;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #ca261a;
  text-align: left;
  display: block;
  margin-top: 4px;

  background-image: url('../img/icons/red-alert.svg');
  background-repeat: no-repeat;
  padding-left: 32px;
}
 
/*
Do not delete the following comment. It is essential for tracking purposes.
#Merc2021DoNotDelete
*/

var Pagination = {

    code: '',

    /* */
    pageLanguage: document.documentElement.lang,

    // --------------------
    // Utility
    // --------------------

    // converting initialize data
    Extend: function (data) {
        data = data || {};
        Pagination.totalResults = data.totalResults;
        Pagination.perPage = data.perPage || 12;
        Pagination.size = data.size || Math.round(data.totalResults / Pagination.perPage) + 1;
        Pagination.page = data.page || 1;
        Pagination.step = data.step || 3;
        
    },

    // add pages by number (from [s] to [f])
    Add: function (s, f) {
        for (var i = s; i < f; i++) {
            Pagination.code += '<span class="show-for-sr">page </span>' + i + '';
        }
    },

    // add last page with separator
    Last: function () {
        Pagination.code += '<span class="show-for-sr">page </span>' + Pagination.size + '';
    },

    // add first page with separator
    First: function () {
        Pagination.code += '<span class="show-for-sr">page </span>1';
    },



    // --------------------
    // Handlers
    // --------------------

    // change page
    Click: function () {
        Pagination.page = +this.dataset.text
        Pagination.Start();
    },

    // previous page
    Prev: function () {
        Pagination.page--;
        if (Pagination.page < 1) {
            Pagination.page = 1;
        }
        Pagination.Start();
    },

    // next page
    Next: function () {
        Pagination.page++;
        if (Pagination.page > Pagination.size) {
            Pagination.page = Pagination.size;
        }
        Pagination.Start();
    },

    

    // --------------------
    // Script
    // --------------------

    // binding pages
    Bind: function () {
        var a = Pagination.e.getElementsByTagName('button');
        for (var i = 0; i < a.length; i++) {
            if (+a[i].dataset.text === Pagination.page) {
                a[i].className = 'current';
                a[i].setAttribute('aria-current', 'page');
                a[i].focus();
            }
            else {
                a[i].removeAttribute('aria-current');
            }

            a[i].addEventListener('click', Pagination.Click, false);
            
        }
    },

    // write pagination
    Finish: function () {
        Pagination.e.innerHTML = Pagination.code;
        Pagination.code = '';
        Pagination.Bind();
        buttonsEventListener();
    },

    // find pagination type
    Start: function () {
        if (Pagination.size < Pagination.step * 2 + 6) {
            Pagination.Add(1, Pagination.size + 1);
        }
        else if (Pagination.page < Pagination.step * 2 + 1) {
            Pagination.Add(1, Pagination.step * 2 + 4);
            Pagination.Last();
        }
        else if (Pagination.page > Pagination.size - Pagination.step * 2) {
            Pagination.First();
            Pagination.Add(Pagination.size - Pagination.step * 2 - 2, Pagination.size + 1);
        }
        else {
            Pagination.First();
            Pagination.Add(Pagination.page - Pagination.step, Pagination.page + Pagination.step + 1);
            Pagination.Last();
        }



        /* Live region text */
        const fromRecord = Pagination.page == 1 ? Pagination.page : parseInt(Pagination.perPage * Pagination.page) - Pagination.perPage + 1;
        const toRecord = Pagination.page == Pagination.size ? Pagination.totalResults : parseInt(Pagination.page * Pagination.perPage);

        if (Pagination.pageLanguage === 'fr') {
            resulText = `${fromRecord} &agrave; ${toRecord} de ${Pagination.totalResults} r&eacute;sultats`;
            resultsPerPage = `R&eacute;sultats par page`;
        } else {
            resulText = `${fromRecord} to ${toRecord} of ${Pagination.totalResults} results`;
            resultsPerPage = `Results per page`;
        }
       

        document.getElementById('custom-dropdown-label').innerHTML = resultsPerPage;
        document.getElementById('pageinationText').innerHTML = resulText;
        document.getElementById('prevButton').style.display = Pagination.page == 1 ? 'none' : 'inline';
        document.getElementById('nextButton').style.display = Pagination.page == Pagination.size ? 'none' : 'inline';
        document.getElementById('mobilePageShow').value = Pagination.page;

        
        if (document.getElementById('mobilePageShow')) {
            document.getElementById('mobilePageShow').addEventListener('change', () => {

                if (document.getElementById('mobilePageShow').value < 1) {
                    Pagination.page = 1;
                } else if (document.getElementById('mobilePageShow').value > Pagination.size) {
                    Pagination.page = Pagination.size;
                } else {
                    Pagination.page = document.getElementById('mobilePageShow').value;
                }

                Pagination.Start();

            });
        }

        Pagination.Finish();
    },



    // --------------------
    // Initialization
    // --------------------

    // binding buttons
    Buttons: function (e) {
        e.querySelector('.prev-button').addEventListener('click', Pagination.Prev, false);
        e.querySelector('.next-button').addEventListener('click', Pagination.Next, false);
    },

    // create skeleton
    Create: function (e) {
        
        if (Pagination.pageLanguage === 'fr') {
            textForMobile = `de ${Pagination.size} pages`;
            prevPage = `Page pr&eacute;c&eacute;dente`;
            nextPage = `Page suivante`;
        } else {
            textForMobile = `of ${Pagination.size} pages`;
            prevPage = `previous page`;
            nextPage = `next page`;
        }
        
        var html = [
            ``,
            `<em class="show-for-sr">${prevPage} </em><img src="img/icons/chevron-left.svg" alt="" />`, // previous button
            '',  // pagination container
            ``,
            `${textForMobile}`, // Mobile pagination
            `<em class="show-for-sr">${nextPage} </em><img src="img/icons/chevron-right.svg" alt="" />`,  // next button
            ``
        ];

        e.innerHTML = html.join('');
        Pagination.e = e.getElementsByTagName('span')[0];
        Pagination.Buttons(e);
    },

    // init
    Init: function (e, data) {
        Pagination.Extend(data);
        Pagination.Create(e);
        Pagination.Start();
    }
};

/* * * * * * * * * * * * * * * * *
* Initialization
* * * * * * * * * * * * * * * * */

var initPaging = function () {
    if (document.getElementById('custom-dropdown')) {
        changed = false;
        document.getElementById('custom-dropdown').addEventListener('change', () => {
            let selectedValue = document.getElementById('custom-dropdown').value;
            Pagination.Init(document.getElementById('pagination'), {
                totalResults: 100,
                page: 1,  // selected page
                step: 1,  // pages before and after current
                perPage: selectedValue == '' ? 12 : selectedValue, // results per page
            });
            changed = true;
            document.querySelector('.cpc-control-select__toggle').focus();
        });
        if (changed == false) {
            Pagination.Init(document.getElementById('pagination'), {
                totalResults: 100,
                page: 1,  // selected page
                step: 1,  // pages before and after current
                perPage: 12, // results per page
            });
        }
    }
    else {
        Pagination.Init(document.getElementById('pagination'), {
            totalResults: 100,
            page: 1,  // selected page
            step: 1,  // pages before and after current
            perPage: 12, // results per page
        });
    }
};

document.addEventListener('DOMContentLoaded', initPaging, false);

function buttonsEventListener() {
    document.querySelectorAll('button').forEach(function (el) {
        // Add event listeners to the various buttons
        el.addEventListener('click', ButtonEventHandler);
        el.addEventListener('keyup', ButtonEventHandler);
        el.addEventListener('blur', ButtonEventHandler);
    });
}

function ButtonEventHandler(event) {
    var type = event.type;
    if (type === 'keyup') {
        if (event.keyCode === 13 || event.keyCode === 32) {
            event.target.classList.remove('by-keyboard');
        }
        else if (event.keyCode === 9) {
            event.target.classList.remove('by-keyboard');
        }
    } else if (type === 'click') {
        event.target.classList.add('by-keyboard');
    }
    else if (type === 'blur') {
        event.target.classList.add('by-keyboard');
    }
}


/*DROP DOWN BLOCK*/
const SELECTOR_NATIVE_SELECT = 'select.ds-cpc-control-select__tpl';
/*
Do not delete the following comment. It is essential for tracking purposes.
#Merc2021DoNotDelete
*/

/**
 * initialize all select elements who has class name of 'ds-cpc-control-select__tpl'
 */
function DSCpcSelects() {
    const cpcSelects = {};
    const nativeSelectElms = document.querySelectorAll(SELECTOR_NATIVE_SELECT);
    for (let i = 0; i < nativeSelectElms.length; i += 1) {
        const cpcSelect = new DSCpcSelect(nativeSelectElms[i]);
        const extraClassNames = nativeSelectElms[i].className.replace('ds-cpc-control-select__tpl', '').trim();
        let extraAttributes = [];
        if (nativeSelectElms[i].getAttribute('aria-invalid')) {
            extraAttributes['aria-invalid'] = nativeSelectElms[i].getAttribute('aria-invalid');
        }
        if (nativeSelectElms[i].getAttribute('aria-described-by')) {
            extraAttributes['aria-described-by'] = nativeSelectElms[i].getAttribute('aria-described-by');
        }
        /* if (nativeSelectElms[i].getAttribute('id')) {
            extraAttributes['id'] = nativeSelectElms[i].getAttribute('id');
        } */
        if (nativeSelectElms[i].getAttribute('aria-labelledby')) {
            extraAttributes['aria-labelledby'] = nativeSelectElms[i].getAttribute('aria-labelledby');
        }


        cpcSelect.init(extraClassNames, extraAttributes);

        const id = nativeSelectElms[i].id || `cpc-select-id_${i}`;
        cpcSelects[id] = cpcSelect;
        cpcSelect.createObserver();
    }

    return cpcSelects;
}

const CLASS_OPEN = 'cpc-control-select--open';

/**
 * CPC customized select component
 */
function DSCpcSelect(selectElm) {
    // The original native select element
    const nativeSelectElm = selectElm;

    // The in-house made drop down element
    let cpcSelectElm;

    // The toggle object which trigger open of close drop down
    let toggle;

    // The in-house made drop down combobox element
    let comboElm;

    // The ul list
    let comboListElm;

    // All the options
    const options = [];

    // statues to track whether drop down is opened or closed
    let opened = false;

    // Track the current selected option
    let selected;

    // how many option rows to be displayed in the drop down
    let rowsToDisplay;

    let oldSelectedValue;


    // iPhone fix
    let onFirstOpen = true;

    return {
        init,
        open,
        close,
        toggle: doToggle,
        disable,
        enable,
        createObserver,
        reset
    };

    function init(extraClassNames, extraAttributes) {
        cpcSelectElm = document.createElement('div');

        cpcSelectElm.className = 'cpc-control-select';

        // creating toggle button
        toggle = new CpcSelectToggle(extraClassNames, extraAttributes);
        cpcSelectElm.appendChild(toggle.element);

        // creating the comboBox
        comboElm = document.createElement('div');
        comboElm.className = 'cpc-control-select__combo';
        // comboElm.setAttribute('role', 'combobox');
        cpcSelectElm.appendChild(comboElm);

        // creating the ul
        comboListElm = document.createElement('ul');
        comboListElm.className = 'cpc-control-select__combo--list';
        comboListElm.setAttribute('aria-expanded', false);
        comboListElm.setAttribute('role', 'listbox');
        if (extraAttributes["id"]) {
            comboListElm.setAttribute('id', 'ac_' + extraAttributes["id"]);
        }

        comboListElm.setAttribute('aria-labelledby', 'custom-dropdown-label');

        comboElm.appendChild(comboListElm);

        // create options and link together
        const optionElms = nativeSelectElm.querySelectorAll('option');
        for (let i = 0; i < optionElms.length; i += 1) {
            const option = new CpcSelectOption(optionElms[i], i, nativeSelectElm.id);
            options[i] = option;
            if (i > 0) {
                // chain together
                option.prev(options[i - 1]);
                options[i - 1].next(option);
            }

            if (option.isSelected()) {
                selected = option;
                toggle.setText(option.textContent);
            }
            comboListElm.appendChild(option.element);
        }
        nativeSelectElm.parentNode.insertBefore(cpcSelectElm, nativeSelectElm);
        nativeSelectElm.style.display = 'none';

        if (nativeSelectElm.disabled) disable();
        else enable();

        // document.getElementById(nativeSelectElm.id).dispatchEvent(new Event('change'));

        oldSelectedValue = selected.value;
    }

    function disable() {
        unRegisterEventListener();
        cpcSelectElm.classList.add('disabled');
        toggle.element.setAttribute('aria-disabled', true);
    }

    function enable() {
        registerEventListener();
        cpcSelectElm.classList.remove('disabled');
        toggle.element.removeAttribute('aria-disabled');
    }

    // observer  for disabled attributes
    function mutate(mutations) {
        mutations.forEach((mutation) => {
            if (mutation.target.className === 'cpc-control-select disabled' || mutation.target.disabled) {
                unRegisterEventListener();
            } else if (mutation.target.className === 'cpc-control-select') {
                registerEventListener();
            }
        });
    }

    function createObserver() {
        const observer = new MutationObserver(mutate);
        const config = { attributes: true, childList: true, subtree: true };
        observer.observe(cpcSelectElm, config);
    }

    // resets value(s) of options to idx 0
    function reset(e) {
        e.preventDefault();
        chooseOption(options[0]);
        close();
    }

    function registerEventListener() {
        cpcSelectElm.addEventListener('keydown', onKeydown);
        
        toggle.element.addEventListener('click', onClickToggle);
        toggle.element.addEventListener('keyup', onKeyUpToggle);

        comboElm.addEventListener('click', onClickCombo);

        document.addEventListener('click', onClickDocumentCpcSelect);
        document.addEventListener('keyup', onKeyupDocumentCpcSelect);

        // disable focus ring if mouse click
        // new FocusRingHandler('.cpc-control-select__toggle').initialize();
    }

    function unRegisterEventListener() {
        cpcSelectElm.removeEventListener('keydown', onKeydown);

        toggle.element.removeEventListener('click', onClickToggle);
        toggle.element.removeEventListener('keyup', onKeyUpToggle);

        comboElm.removeEventListener('click', onClickCombo);

        document.removeEventListener('click', onClickDocumentCpcSelect);
        document.removeEventListener('keyup', onKeyupDocumentCpcSelect);
    }

    /**
     * For mouse click outside the select list
     */
    function onClickDocumentCpcSelect(e) {
        if (!cpcSelectElm.contains(e.target)) close();
    }

    /**
     * For escape key, close the drop down anyway
     */
    function onKeyupDocumentCpcSelect(e) {
        const key = e.which || e.keyCode;
        if (key !== 27) return;
        close();
    }

    /**
     * Select the option which gets clicked
     */
    function onClickCombo(e) {
        e.preventDefault();
        const target = e.target;
        const idx = target.getAttribute('data-index') || target.parentElement.getAttribute('data-index');
        chooseOption(options[idx]);
        close();
    }

    /**
     * When the toggle clicked, open or close the drop down depends whether it's opened already
     */
    function onClickToggle(e) {
        e.preventDefault();
        doToggle(e);
    }

    function onKeyUpToggle(e) {
        const key = e.which || e.keyCode;
        e.preventDefault();
        if (key === 32 && !opened) { // Spacebar if opened dont close
            doToggle(e);
        }
    }

    /**
     * When ArrowUp or ArrowDown pressed on the toggle, change the selected option
     */
    function onKeydown(e) {

        const key = e.which || e.keyCode;
        if (key !== 38
            && key !== 40
            && key !== 18
            && key !== 9
            && key !== 35
            && key !== 36
            && key !== 27
            && key !== 32
            && key !== 13) return;
        
        if (key === 9 || key === 27) {
            // TAB and Escape. If drop down opened, then close it.
            // Otherwise, do it's default
            if (opened) close();
            return;
        }

        e.preventDefault();
        if (key === 13 || (e.altKey && key === 40)) {
            // Enter key or ALT + Down Arrow key
            doToggle(e);
            return;
        }

        if (key === 35) { // END KEY{
            chooseOption(options[options.length - 1]);
        }
        else if (key === 36) { // HOME KEY
            chooseOption(options[0]);
        }
        else if (key === 38) {
            // ArrowUp
            chooseOption(selected ? selected.prev() : options[0]);

            // eslint-disable-next-line max-len
            if (comboElm.getBoundingClientRect().top > selected.element.getBoundingClientRect().top - 1) {
                // There is 1px border for the comboElm
                comboElm.scrollTop -= CpcSelectOption.HEIGHT;
            }
        } else if (key === 40) {

            // ArrowDown
            chooseOption(selected ? selected.next() : options[0]);

            // eslint-disable-next-line max-len
            if (selected.element.getBoundingClientRect().bottom - 1 > comboElm.getBoundingClientRect().bottom) {
                // There is 1px border for the comboElm
                comboElm.scrollTop += CpcSelectOption.HEIGHT;
            }
        }
        selected.element.focus();
    }


    /**
     * Choose the selected option and unselected the previous selected option
     *
     * @param opt - The one to be selected
     */
    function chooseOption(opt) {
        if (!opt) return;

        if (selected) selected.unSelect();
        selected = opt;
        selected.select();
        toggle.setText(opt.textContent);
    }

    function open() {
        cpcSelectElm.classList.add(CLASS_OPEN);
        toggle.element.setAttribute('aria-expanded', true);
        comboListElm.setAttribute('aria-expanded', true);

        // iPhone fix for filter not scrolling
        if (onFirstOpen) {
            onFirstOpen = false;
            // disable and re-enable scrolling
            comboElm.style.overflow = 'hidden';
            setTimeout(() => {
                comboElm.style.overflow = 'auto';
            }, 50);
        }

        rePosition();
        if (selected) scrollToSelected();
        opened = true;
    }

    function scrollToSelected() {
        comboElm.scrollTop = 0;
        const diff = (selected.index + 1) - rowsToDisplay;
        if (diff > 0) {
            // the selected option is outside the drop down, so scroll it in view
            comboElm.scrollTop += CpcSelectOption.HEIGHT * diff;
        }
    }

    /**
     * Determine open direction
     */
    function rePosition() {
        const toggleRect = toggle.element.getBoundingClientRect();
        const spaceBelow = window.innerHeight - toggleRect.bottom;
        // const spaceAbove = toggleRect.top;

        if (window === window.top) {
            // give it a max-height
            const optionHeight = CpcSelectOption.HEIGHT;
            // rowsToDisplay = Math.floor(availableSpace / optionHeight);
            rowsToDisplay = 6;

            // const openBelow = (spaceBelow - spaceAbove) >= 0;
            // const availableSpace = openBelow ? spaceBelow : spaceAbove;          
            const openBelow = (spaceBelow > ((optionHeight * rowsToDisplay)));

            toggle.element.classList.remove('openBelow');
            comboElm.classList.remove('openBelow');
            toggle.element.classList.remove('openUp');
            comboElm.classList.remove('openUp');

            if (openBelow) {
                toggle.element.classList.add('openBelow');
                comboElm.classList.add('openBelow');
            } else {
                toggle.element.classList.add('openUp');
                comboElm.classList.add('openUp');
            }

            const maxHeight = rowsToDisplay * optionHeight;
            comboElm.style.maxHeight = `${maxHeight}px`;

            /* comboElm.style.top = openBelow ? 'auto' : `-${comboElm.getBoundingClientRect().height}px`; */

            /* Pagination Change */
            comboElm.style.top = openBelow ? '32px' : `-${comboElm.getBoundingClientRect().height + 12}px`;
        }
    }

    function close() {
        cpcSelectElm.classList.remove(CLASS_OPEN);
        toggle.element.setAttribute('aria-expanded', false);
        // Focus only if the current active element is the current select or any of its options
        // Fix for this element being focused when other elements were interacted with
        if (isDescendant(cpcSelectElm, document.activeElement)) {
            toggle.element.focus();
        }
        comboListElm.setAttribute('aria-expanded', false);
        opened = false;

        var x = document.getElementById(nativeSelectElm.id);
        for (i = 0; i < x.options.length; i++) { // To prevent the document click close div
            if (x.options[i].value == selected.value && selected.value != oldSelectedValue) {
                x.options[i].setAttribute('selected', 'true');
                document.getElementById(nativeSelectElm.id).dispatchEvent(new Event('change'));
                oldSelectedValue = selected.value;
            }
            else {
                x.options[i].removeAttribute('selected');
            }
        }
    }

    function isDescendant(parent, child) {
        let node = child.parentNode;
        while (node != null) {
            if (node === parent) {
                return true;
            }
            node = node.parentNode;
        }
        return false;
    }

    function doToggle(e) {
        if (opened) {
            close();
        } else {
            open();
        }
    }
}

/**
 * The toggle to open/close the select drop down list
 */
function CpcSelectToggle(extraClassNames, extraAttributes) {
    const toggleElm = document.createElement('button');
    toggleElm.className = 'cpc-control-select__toggle';
    if (extraClassNames) {
        toggleElm.classList.add(extraClassNames);
    }
    if (extraAttributes) {
        for (var key in extraAttributes) {
            toggleElm.setAttribute(key, extraAttributes[key])
        }
    }
    toggleElm.setAttribute('tabindex', 0);
    toggleElm.setAttribute('aria-expanded', false);
    if (extraAttributes && extraAttributes["id"]) {
        toggleElm.setAttribute('aria-controls', 'ac_' + extraAttributes["id"]);
    }
    toggleElm.setAttribute('aria-haspopup', 'listbox');
    toggleElm.setAttribute('role', 'combobox');





    const toggleTextElm = document.createElement('span');
    toggleElm.appendChild(toggleTextElm);
    const indicator = document.createElement('span');
    indicator.className = 'cpc-control-select__toggle-indicator';
    toggleElm.appendChild(indicator);

    return {
        element: toggleElm,
        setText
    };

    function setText(text) {
        toggleTextElm.innerText = text;
        toggleElm.title = text;
    }
}

/**
 * Select option
 */
function CpcSelectOption(optSrcElm, i, id) {
    // The original native select option element
    const optionSourceElm = optSrcElm;

    // our option element
    let optionElm;

    // The previous option in the drop down list
    let prevOpt;

    // The next option in the drop down list
    let nextOpt;

    // The position where the option in the drop down list
    const index = i;

    // The option text
    let spanElm;

    let nativeElementID = id;

    const textContent = optionSourceElm.textContent;
    const value = optionSourceElm.value;

    init();

    return {
        element: optionElm,
        index,
        value,
        textContent,
        next,
        prev,
        unSelect,
        select,
        isSelected
    };

    function init() {
        optionElm = document.createElement('li');
        optionElm.className = 'cpc-control-select__combo--list-option';

        // spanElm = document.createElement('span');
        // spanElm.setAttribute('aria-selected', 'false');
        // optionElm.appendChild(spanElm);

        optionElm.innerText = textContent;
        optionElm.setAttribute('role', 'option');
        optionElm.setAttribute('data-value', value);
        optionElm.setAttribute('data-index', i);
        optionElm.setAttribute('tabindex', 0);

        if (optionSourceElm.getAttribute('selected') !== null) select();
    }

    function next(val) {
        if (val) nextOpt = val;
        return nextOpt;
    }

    function prev(val) {
        if (val) prevOpt = val;
        return prevOpt;
    }

    function select() {
        optionElm.classList.add('selected');
        optionElm.setAttribute('aria-selected', 'true');
    }

    function unSelect() {
        optionElm.classList.remove('selected');
        optionElm.removeAttribute('aria-selected', 'false');
    }

    function isSelected() {
        return optionSourceElm.getAttribute('selected') !== null;
    }
}

// 40px here, see '_select.scss' --> ul li{height:40px}
//
CpcSelectOption.HEIGHT = 40;

/**
 * Initialize all CpcSelect elements on the page. Return a map
 * of the initialized CpcSelect object, keyed with the native
 * select element's id, or an auto generated id if the native
 * select element missing the id attribute.
 */

DSCpcSelects.init = function init() {
    return DSCpcSelects();
};


setTimeout(() => {
    DSCpcSelects.init();
}, 100);


// export default DSCpcSelects;

Angulaire

Veuillez noter que ce composant pourrait nécessiter un élément de style supplémentaire.

<div style="padding-top: 30vh; text-align: center">
  <button (click)="useLanguage('en')">EN</button> &nbsp; <button (click)="useLanguage('fr')">FR</button>
</div>

<div style="padding-top: 10vh">
  <div class="pg-container">
    <mat-paginator
      appPagination
      [length]="dsPaginator.length"
      [pageSize]="dsPaginator.pageSize"
      [pageIndex]="dsPaginator.pageIndex"
      [hidePageSize]="false"
      positionOfPageSize="right"
      [pageSizeOptions]="[12, 24, 48, 96]"
      (page)="showPageEmit($event)"
    >
    </mat-paginator>
  </div>
</div>

*,
*:before,
*:after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

.pg-container {
  display: flex;
  justify-content: center;
  flex-direction: column;
  align-items: center;
}

.ds-pagination button {
  width: 44px;
  height: 44px;
  padding: 10px 0;

  border: none;
  border-radius: 2px;
  text-align: center;
  margin-right: 8px;
  background-color: transparent;

  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  text-align: center;
  color: #333;
}

.ds-pagination button:focus {
  box-shadow: inset 0 0 0 2px #004990, 0 0 0 2px #fff, 0 0 0 4px #0467c6;
  outline: none;
}

.ds-pagination button.by-keyboard:focus {
  box-shadow: none !important;
  border: none;
}

.ds-pagination button.current.by-keyboard:focus {
  box-shadow: inset 0 0 0 0px #004990 !important;
  border: none;
}

.ds-pagination button.by-keyboard:hover {
  box-shadow: inset 0 0 0 2px #004990 !important;
  border: none;
}

/* Current Page */
.ds-pagination button.custom-paginator-page-disabled {
  background-color: #0467c6;
  color: #fff;
  pointer-events: none;
  border: none;
  outline: none;
  box-shadow: none;
  font-weight: 400 !important;
}

.ds-pagination button.custom-paginator-arrow-disabled {
  display: none;
}

.ds-pagination button:last-child {
  margin-right: 0 !important;
}

/* Ellipses */
.ds-pagination i,
.ds-pagination .ds-ellipsis {
  width: 44px;
  height: 44px;
  margin-right: 8px;
  text-align: center;
  pointer-events: none;
  background: url(../assets/img/icons/ellipsis.svg) 50% center no-repeat;
}

/* Total records text */
.ds-paginator-counter {
  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #333;
  margin-bottom: 40px;
  text-align: left;
}

/* Arrows */
.mat-paginator-navigation-next svg,
.mat-paginator-navigation-previous svg {
  display: none;
}
.mat-paginator-navigation-next {
  background: url(../assets/img/icons/chevron-right.svg) no-repeat center center;
}
.mat-paginator-navigation-previous {
  background: url(../assets/img/icons/chevron-left.svg) no-repeat center center;
}

/* Results per page */
.mat-paginator-page-size {
  margin-right: 28px !important;
  margin-left: 0px !important;
  align-items: center !important;
}
.marginRight {
  margin-right: 32px !important;
}

.ds-pagination,
.mat-paginator-page-size-label {
  display: inline-block;
  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 24px;
  letter-spacing: 0.5px;
  color: #333;
  margin-right: 0px;
}

.mat-paginator-page-size-label {
  margin-right: 8px !important;
  margin-left: 0px !important;
  height: 44px;
  display: flex;
  align-items: center;
}

.mat-paginator-container {
  position: relative;
  min-height: 44px !important;
  padding: 0 !important;
  min-width: 255px !important;
}
.mat-paginator-range-label {
  margin: 0 !important;
}

.ds-paginator-counter {
  position: absolute;
  top: -37px;
  left: 0px;
}

/* Mobile pagination number field */
.mobileview {
  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #333;
  margin-right: 12px;

  display: none;
}
.paginationTextField {
  width: 61px;
  height: 44px;
  margin: 0 12px 0 0;
  padding: 10px 16px;
  border-radius: 2px;
  border: solid 1px #666;
  background-color: #fff;

  font-family: Roboto;
  font-size: 1rem;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #333;
}

/* Hide the number field arrow controls */
/* Chrome, Safari, Edge, Opera */
input.paginationTextField::-webkit-outer-spin-button,
input.paginationTextField::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
/* Firefox */
input.paginationTextField[type='number'] {
  -moz-appearance: textfield;
}

/* Input focus */
input[type='number']:focus {
  outline: 0;
  border: solid 1px #0467c6;
  border-radius: 2px;

  -webkit-box-shadow: inset 1px 1px 0px 0px#0467c6, inset -1px -1px 0px 0px#0467c6;
  -moz-box-shadow: inset 1px 1px 0px 0px#0467c6, inset -1px -1px 0px 0px#0467c6;
  box-shadow: inset 1px 1px 0px 0px#0467c6, inset -1px -1px 0px 0px#0467c6;
}

@media only screen and (max-width: 750px) {
  .ds-paginator-counter {
    top: -56px;
  }
  .custom-paginator-page {
    display: none;
  }
  .mat-paginator-page-size {
    position: absolute;
    top: 77px;
    width: 100%;
    height: 44px;
    margin-right: 0 !important;
    justify-content: center;
  }
  .mobileview {
    display: inline-block;
  }
  .marginRight {
    margin-right: 0 !important;
  }

  .mat-paginator-container {
    justify-content: center !important;
  }
}

/* Hide the background on focus of button */
.mat-button-focus-overlay {
  background-color: transparent !important;
}

/* Drop down */
/* Width of Drop Down */

.mat-paginator-page-size-select {
  width: 79px !important;
  margin: 6px 0 0 0 !important;
}

.mat-paginator,
.mat-paginator-page-size .mat-select-trigger {
  font-size: 16px !important;
  font-weight: 300 !important;
  color: #333 !important;
}

.mat-select {
  position: relative;
  font-family: Roboto;
  font-size: 16px;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #333333;
  text-align: left;
  border: solid 1px #666 !important;
  background-color: #fff;
  height: 44px;
  max-height: 44px;
  width: 100%;
  max-width: 368px;
  min-width: 79px;
  border-radius: 2px;
  padding: 10px 0px;
  @media only screen and (max-width: 40em) {
    width: 100%;
    max-width: 100%;
    min-width: 79px;
  }
}

.mat-select[aria-expanded='false']:focus {
  outline: none;
  border: 1px solid #0467c6 !important;
  -webkit-box-shadow: inset 1px 1px 0px 0px #0467c6, inset -1px -1px 0px 0px #0467c6;
  -moz-box-shadow: inset 1px 1px 0px 0px #0467c6, inset -1px -1px 0px 0px #0467c6;
  box-shadow: inset 1px 1px 0px 0px #0467c6, inset -1px -1px 0px 0px #0467c6;
}

.mat-select[aria-disabled='true'] {
  border: solid 1px #666;
  background-color: #f0f0f0;
  color: #666;
  &:focus {
    border: solid 1px #666 !important;
    outline: none;
    -webkit-box-shadow: none;
    -moz-box-shadow: none;
    box-shadow: none;
  }
}

.mat-select[aria-expanded="true"] {
  border: solid 1px #666 !important;
  border-bottom-left-radius: 0px;
  border-bottom-right-radius: 0px;
  /* border-bottom: none !important; */
  -webkit-box-shadow: none;
  -moz-box-shadow: none;
  box-shadow: none;

  &.openUp {
    border: solid 1px #666 !important;
    border-top: 0px !important;
    border-top-left-radius: 0px !important;
    border-top-right-radius: 0px !important;
  }
  &.openBelow {
    border: solid 1px #666 !important;
    border-bottom: 0px !important;
    border-bottom-left-radius: 0px !important;
    border-bottom-right-radius: 0px !important;
  }
}

.mat-select.mat-select-required.mat-select-empty.mat-select-invalid {
  border: 1px solid #ca261a !important;
  &:focus {
    outline: none;
    -webkit-box-shadow: inset 1px 1px 0px 0px #ca261a, inset -1px -1px 0px 0px #ca261a;
    -moz-box-shadow: inset 1px 1px 0px 0px #ca261a, inset -1px -1px 0px 0px #ca261a;
    box-shadow: inset 1px 1px 0px 0px #ca261a, inset -1px -1px 0px 0px #ca261a;
  }
}
.mat-select[aria-expanded="true"].mat-select-required.mat-select-empty.mat-select-invalid {
  border: solid 1px #ca261a !important;
  border-bottom-left-radius: 0px;
  border-bottom-right-radius: 0px;
  /* border-bottom: none !important; */
  -webkit-box-shadow: none !important;
  -moz-box-shadow: none !important;
  box-shadow: none !important;
  &.openUp {
    border: solid 1px #ca261a !important;
    border-top: 0px !important;
    border-top-left-radius: 0px !important;
    border-top-right-radius: 0px !important;
  }
  &.openBelow {
    border: solid 1px #ca261a !important;
    border-bottom: 0px !important;
    border-bottom-left-radius: 0px !important;
    border-bottom-right-radius: 0px !important;
  }
}

.mat-select-panel::-webkit-scrollbar {
  width: 14px;
}
.mat-select-panel {
  --scrollbarBG: #fff;
  --thumbBG: #888;
  scrollbar-width: thin;
  scrollbar-color: var(--thumbBG) var(--scrollbarBG);
}
.mat-select-panel::-webkit-scrollbar-track {
  background: var(--scrollbarBG);
  margin-top: 5px;
}
.mat-select-panel::-webkit-scrollbar-thumb {
  background-color: #888;
  border: 4px solid #fff;
  border-radius: 100px;
}

.cdk-overlay-pane {
  transform: none !important;
}
.mat-select-panel {
  border-radius: 2px !important;
  border: solid 1px #666 !important;
  border-top: 0px !important;
  min-width: 79px !important;
  max-height: 240px !important;
  position: absolute;
  top: 33px;
  left: -1px !important;
  transform: none !important;

  border-top-left-radius: 0px !important;
  border-top-right-radius: 0px !important;
  @media only screen and (max-width: 40em) {
    width: 100.44% !important;
    max-width: 101% !important;
    min-width: 100% !important;
  }
  @media only screen and (max-width: 20em) {
    width: 100.6% !important;
    max-width: 101% !important;
    min-width: 100% !important;
  }
  &.openUp {
    border: solid 1px #666 !important;
    border-bottom-left-radius: 0px !important;
    border-bottom-right-radius: 0px !important;
    -webkit-box-shadow: none !important;
    -moz-box-shadow: none !important;
    box-shadow: none !important;
  }
  &.openBelow {
    border: solid 1px #666 !important;
    border-top-left-radius: 0px !important;
    border-top-right-radius: 0px !important;
  }
}

.show-for-sr {
    position: absolute !important;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
}

.mat-select-panel:not([class*='mat-elevation-z']) {
  -webkit-box-shadow: 0 3px 8px 0 rgba(17, 17, 17, 0.13) !important;
  -moz-box-shadow: 0 3px 8px 0 rgba(17, 17, 17, 0.13) !important;
  box-shadow: 0 3px 8px 0 rgba(17, 17, 17, 0.13) !important;
}

.mat-select-arrow {
  background: url('../assets/img/icons/drop-down-arrow.svg') center 50% no-repeat !important;
  width: 12px !important;
  height: 12px !important;
  transition: transform 0.4s !important;
  border: none !important;
  margin: -1px 15px 0 0 !important;
}
.mat-select[aria-expanded="true"] .mat-select-arrow {
  transform: rotate(-180deg);
}

.mat-form-field-appearance-fill .mat-form-field-flex {
  background-color: transparent;
}
.mat-form-field-underline {
  display: none;
}
.mat-form-field-appearance-fill .mat-select-arrow-wrapper {
  transform: translateY(0%) !important;
}
.mat-select-value {
  color: #333333;
  padding-left: 16px;
}

.mat-form-field.mat-focused.mat-primary .mat-select-arrow {
  color: #666666;
}

.mat-option {
  font-family: Roboto !important;
  font-size: 16px !important;
  font-weight: 300 !important;
  font-stretch: normal !important;
  font-style: normal !important;
  line-height: 1.5 !important;
  letter-spacing: 0.5px !important;
  color: #333333 !important;
  height: 40px !important;
  &.selected,
  &:focus,
  &:hover,
  &.selected span,
  &:focus span,
  &:hover span {
    background-color: #0467c6 !important;
    color: #ffffff !important;
    outline: none !important;
    font-weight: 400 !important;
  }
}

.mat-option.mat-active,
.mat-select-panel .mat-option.mat-selected:not(.mat-option-multiple) {
  background-color: #0467c6 !important;
  color: #ffffff !important;
  outline: none !important;
  font-weight: 400 !important;
}

.mat-primary .mat-option.mat-selected {
  color: #333333;
}
.mat-primary .mat-option.mat-selected:not(.mat-option-disabled) {
  color: #333333;
}

select[aria-disabled='true'] {
  pointer-events: none;
}

.mat-error {
  font-family: Roboto;
  font-size: 16px;
  font-weight: 300;
  font-stretch: normal;
  font-style: normal;
  line-height: 1.5;
  letter-spacing: 0.5px;
  color: #ca261a;
  text-align: left;

  background-image: url('../assets/img/icons/red-alert.svg');
  background-repeat: no-repeat;
  padding-left: 32px;
  margin-top: 4px;
}

.mat-form-field {
  width: 100%;
}
.mat-form-field-infix {
  width: 100% !important;
}
.mat-form-field-appearance-legacy .mat-form-field-subscript-wrapper {
  margin-top: 0px !important;
}
.mat-form-field-appearance-legacy .mat-form-field-infix {
  padding: 0 !important;
}

.mat-form-field-subscript-wrapper div.ng-trigger-transitionMessages {
  transition: none !important;
  transform: none !important;
  opacity: 1 !important;
}

/* label {
  font-family: Roboto;
  font-size: 16px;
  line-height: 24px;
  font-weight: 500;
  font-stretch: normal;
  font-style: normal;
  letter-spacing: 0.5px;
  color: #333333;
  display: block;
  margin-bottom: 4px;
} */

.mat-select-disabled .mat-select-value {
  color: #666 !important;
}
/*app.component.ts*/
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation: ViewEncapsulation.None,
})
export class AppComponent implements OnInit {
  /* Page result size */
  pageSizeOptions: number[] = [12, 24, 48, 96];

  // States
  EmitResult = {
    pageNumber: '',
    pageSize: '',
  };

  /* Starting Point */
  dsPaginator = {
    length: 100,
    pageSize: this.pageSizeOptions[0],
    pageIndex: 0,
  };

  constructor(private translate: TranslateService) {
    translate.setDefaultLang('en');

    let browserLang = translate.getBrowserLang();
    translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');
  }

  ngOnInit(): void {}

  showPageEmit = ($event) => {
    this.EmitResult = {
      pageNumber: $event.pageIndex,
      pageSize: $event.pageSize,
    };
  };

  useLanguage(language: string): void {
    this.translate.use(language);
  }
}

/* app.module.ts*/
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './modules/app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatPaginatorModule, MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
import { MatFormField, MatFormFieldModule } from '@angular/material/form-field';
import { PaginatorDirective } from './pagination.directive';
import { MatTableModule } from '@angular/material/table';
import { FormsModule } from '@angular/forms';

import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient, HttpClientModule } from '@angular/common/http';

import { CustomMatPaginatorIntl } from './custom-mat-paginator-intl';

@NgModule({
  declarations: [AppComponent, PaginatorDirective],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    MatFormFieldModule,
    MatPaginatorModule,
    BrowserAnimationsModule,
    HttpClientModule,
    MatTableModule,
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient],
      },
    }),
  ],
  providers: [
    {
      provide: MatPaginatorIntl,
      useClass: CustomMatPaginatorIntl,
    },
  ],
  bootstrap: [AppComponent],
  exports: [PaginatorDirective],
})
export class AppModule {}

// required for AOT compilation
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
  return new TranslateHttpLoader(http);
}


/*custom-mat-paginator-intls.ts*/
import { Injectable, OnDestroy } from '@angular/core';
import { MatPaginatorIntl } from '@angular/material/paginator';
import { TranslateService } from '@ngx-translate/core';
import { Subject } from 'rxjs';
import { map, tap, takeUntil } from 'rxjs/operators';

@Injectable()
export class CustomMatPaginatorIntl extends MatPaginatorIntl implements OnDestroy {
  unsubscribe: Subject = new Subject();
  OF_LABEL: string = '';
  TO_LABEL: string = '';
  RESULTS_LABEL: string = '';
  ZERO_RESULTS_LABEL: string = '';
  PAGES_LABEL: string = '';

  constructor(private translate: TranslateService) {
    super();

    this.translate.onLangChange.pipe(takeUntil(this.unsubscribe)).subscribe(() => {
      this.getAndInitTranslations();
    });

    this.getAndInitTranslations();
  }

  ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
  }

  getAndInitTranslations() {
    this.translate
      .get([
        'paginationText.NextPage',
        'paginationText.PreviousPage',
        'paginationText.ResultsPerPage',
        'paginationText.of',
        'paginationText.to',
        'paginationText.results',
        'paginationText.zeroResults',
        'paginationText.pages',
      ])
      .pipe(takeUntil(this.unsubscribe))
      .subscribe((translation) => {
        this.nextPageLabel = translation['paginationText.NextPage'];
        this.previousPageLabel = translation['paginationText.PreviousPage'];
        this.itemsPerPageLabel = translation['paginationText.ResultsPerPage'];
        this.OF_LABEL = translation['paginationText.of'];
        this.TO_LABEL = translation['paginationText.to'];
        this.RESULTS_LABEL = translation['paginationText.results'];
        this.ZERO_RESULTS_LABEL = translation['paginationText.zeroResults'];
        this.PAGES_LABEL = translation['paginationText.pages'];
        this.changes.next();
      });
  }

  getRangeLabel = (page: number, pageSize: number, length: number): string => {
    if (length === 0 || pageSize === 0) {
      return `${this.ZERO_RESULTS_LABEL}`;
    }
    length = Math.max(length, 0);
    const startIndex = page * pageSize;
    const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize;
    // return `${startIndex + 1} - ${endIndex} ${this.OF_LABEL} ${length}`;
    return length > 0
      ? startIndex + 1 + ` ${this.TO_LABEL} ` + endIndex + ` ${this.OF_LABEL} ` + length + ` ${this.RESULTS_LABEL} `
      : `${this.ZERO_RESULTS_LABEL}`;
  };
}


/*pagination.directive.ts*/
import {
  AfterViewInit,
  Directive,
  DoCheck,
  Host,
  Optional,
  Renderer2,
  Self,
  ViewContainerRef,
  Inject,
  HostListener,
  Input,
} from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatButton } from '@angular/material/button';
import { DOCUMENT } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';

@Directive({
  selector: '[appPagination]',
})
export class PaginatorDirective implements DoCheck, AfterViewInit {
  private currentPage: number;
  private pageGapTxt: string[];
  private rangeStart: number;
  private rangeEnd: number;
  private buttons: MatButton[] = [];
  private showTotalPages: number;
  private checkPage: number[];
  private OF_LABEL: string = 'of';
  unsubscribe: Subject = new Subject();

  @HostListener('document:keyup', ['$event'])
  @HostListener('document:click', ['$event'])
  @HostListener('document:focusout', ['$event'])
  onKeyUp(ev: KeyboardEvent) {
    let elements = this._document.querySelectorAll(
      '.custom-paginator-page, .mat-button-base, .primary-button, .secondary-button, .micro-buttons',
    );
    elements.forEach((element) => {
      if (ev.type === 'keyup') {
        if (ev.key === 'Enter' || ev.key === 'Space') {
          element.classList.remove('by-keyboard');
          ev.preventDefault();
        } else if (ev.key === 'Tab') {
          element.classList.remove('by-keyboard');
        }
      } else if (ev.type === 'click' || ev.type === 'focusout') {
        element.classList.add('by-keyboard');
      }
    });
  }

  @Input() positionOfPageSize = 'left';
  constructor(
    @Host() @Self() @Optional() private readonly matPag: MatPaginator,
    private readonly ViewContainer: ViewContainerRef,
    private readonly renderer: Renderer2,
    @Inject(DOCUMENT) private _document: HTMLDocument,
    private translate: TranslateService,
  ) {
    this.currentPage = 1;
    this.pageGapTxt = ['&bull;&bull;&bull;', '---'];
    this.showTotalPages = 3;
    this.checkPage = [0, 0, 0];

    // Subscribe to rerender buttons when next page and last page button is used
    this.matPag.page.subscribe((paginator: PageEvent) => {
      this.currentPage = paginator.pageIndex;
      this.matPag.pageIndex = paginator.pageIndex;
      this.initPageRange();
    });

    this.translate.onLangChange.pipe(takeUntil(this.unsubscribe)).subscribe(() => {
      this.translate
        .get(['paginationText.of'])
        .pipe(takeUntil(this.unsubscribe))
        .subscribe((translation) => {
          this.OF_LABEL = translation['paginationText.of'];

          /* It has a separate Mobile view thats why need to update like this */
          const pageCount = this.matPag.getNumberOfPages();
          var mobilePageNum = this.ViewContainer.element.nativeElement.querySelector('.mobile-num-pages');
          if (mobilePageNum) mobilePageNum.innerHTML = `${this.OF_LABEL} ${pageCount} pages`;
        });
    });
  }

  ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
  }

  ngDoCheck(): void {
    // Reset paginator if the pageSize, pageIndex, length changes
    if (
      this.matPag?.length !== this.checkPage[0] ||
      this.matPag?.pageSize !== this.checkPage[1] ||
      this.matPag?.pageIndex !== this.checkPage[2]
    ) {
      const pageCount = this.matPag.getNumberOfPages();
      if (this.currentPage > pageCount && pageCount !== 0) {
        this.currentPage = 1;
        this.matPag.pageIndex = 0;
      }
      this.currentPage = this.matPag.pageIndex;
      this.initPageRange();
      this.checkPage = [this.matPag.length, this.matPag.pageSize, this.matPag.pageIndex];

      const inputField = this.ViewContainer.element.nativeElement.querySelector('input.paginationTextField');
      if (inputField) {
        inputField.value = this.currentPage + 1;
      }

      var mobilePageNum = this.ViewContainer.element.nativeElement.querySelector('.mobile-num-pages');
      if (mobilePageNum) mobilePageNum.innerHTML = `${this.OF_LABEL} ${pageCount} pages`;
    }
    
  }

  private buildPageNumbers = () => {
    let dots: boolean[];
    let page: number;
    let pageDifference: number;
    let startIndex: number;
    let totalPages: number;
    totalPages = this.matPag.getNumberOfPages();

    // Container div with paginator elements
    const actionContainer = this.ViewContainer.element.nativeElement.querySelector('div.mat-paginator-range-actions');
    // Button that triggers the next page action
    const nextPageNode = this.ViewContainer.element.nativeElement.querySelector('button.mat-paginator-navigation-next');
    // Label showing the page range
    const pageRange = this.ViewContainer.element.nativeElement.querySelector('div.mat-paginator-range-label');

    let prevButtonCount = this.buttons.length;

    // Remove buttons before creating new ones
    if (prevButtonCount > 0) {
      this.buttons.forEach((button) => {
        this.renderer.removeChild(actionContainer, button);
      });
      // Empty state array
      prevButtonCount = 0;
    }

    this.renderer.addClass(pageRange, 'ds-paginator-counter');
    this.renderer.setAttribute(pageRange, 'aria-live', 'polite');
    this.renderer.addClass(actionContainer, 'ds-pagination');

    // Initialize next page and last page buttons
    if (prevButtonCount === 0) {
      const nodeArray = actionContainer.childNodes;
      setTimeout(() => {
        for (const node of nodeArray) {
          // console.log(node.className);

          //mat-paginator-navigation-next
          if (node.nodeName === 'BUTTON') {
            // Next Button styles
            this.renderer.addClass(node, 'by-keyboard');

            if (!node.disabled) {
              this.renderer.setAttribute(node, 'role', 'link');    
            }
            

            if (node.innerHTML.length > 100 && node.disabled) {
              this.renderer.addClass(node, 'custom-paginator-arrow-disabled');
              this.renderer.removeClass(node, 'custom-paginator-arrow-enabled');
              this.renderer.removeClass(node, 'ds-ellipsis');
            } else if (node.innerHTML.length > 100 && !node.disabled) {
              this.renderer.addClass(node, 'custom-paginator-arrow-enabled');
              this.renderer.removeClass(node, 'custom-paginator-arrow-disabled');
              this.renderer.removeClass(node, 'ds-ellipsis');
            }
          }
        }
      });
    }

    dots = [false, false];

    if (totalPages > 0) {
      this.renderer.insertBefore(actionContainer, this.createButton('0', this.matPag.pageIndex), nextPageNode);
    }

    page = this.showTotalPages + 2;
    pageDifference = totalPages - page;
    startIndex = Math.max(this.currentPage - this.showTotalPages - 2, 1);

    for (let index = startIndex; index < totalPages - 1; index = index + 1) {
      if (
        (index < page && this.currentPage <= this.showTotalPages) ||
        (index >= this.rangeStart && index <= this.rangeEnd) ||
        (this.currentPage > pageDifference && index >= pageDifference) ||
        totalPages < this.showTotalPages + page
      ) {
        this.renderer.insertBefore(actionContainer, this.createButton(`${index}`, this.matPag.pageIndex), nextPageNode);
      } else {
        if (index > this.rangeEnd && !dots[0]) {
          this.renderer.insertBefore(
            actionContainer,
            this.createButton(this.pageGapTxt[0], this.matPag.pageIndex),
            nextPageNode,
          );
          dots[0] = true;
          break;
        }
        if (index < this.rangeEnd && !dots[1]) {
          this.renderer.insertBefore(
            actionContainer,
            this.createButton(this.pageGapTxt[1], this.matPag.pageIndex),
            nextPageNode,
          );
          dots[1] = true;
        }
      }
    }

    if (totalPages > 1) {
      this.renderer.insertBefore(
        actionContainer,
        this.createButton(`${totalPages - 1}`, this.matPag.pageIndex),
        nextPageNode,
      );
    }
  };

  private createButton(index: string, pageIndex: number) {
    let pagingTxt = (isNaN(+index) ? this.pageGapTxt[0] : +index + 1);
    if (index === this.pageGapTxt[0] || index === this.pageGapTxt[1]) {
      pagingTxt = '';
    }

    let linkBtn;
    if (index === this.pageGapTxt[0] || index === this.pageGapTxt[1]) {
      linkBtn = this.renderer.createElement('i');
      this.renderer.setAttribute(linkBtn, 'class', 'custom-paginator-page');
      this.renderer.addClass(linkBtn, 'custom-paginator-page-enabled');
      this.renderer.addClass(linkBtn, 'custom-paginator-arrow-enabled');
      this.renderer.addClass(linkBtn, 'ds-ellipsis');
      this.renderer.setAttribute(linkBtn, 'aria-label', '...');
    }
    else {
      linkBtn = this.renderer.createElement('button');
      this.renderer.setAttribute(linkBtn, 'class', 'custom-paginator-page');
      this.renderer.addClass(linkBtn, 'by-keyboard');
      this.renderer.addClass(linkBtn, 'custom-paginator-page-enabled');
      this.renderer.setAttribute(linkBtn, 'role', 'link');
      this.renderer.setAttribute(linkBtn, 'aria-label', 'Page ' + pagingTxt);
    }

    

    const text = this.renderer.createText(pagingTxt + '');
    this.renderer.addClass(linkBtn, 'mat-custom-page');
    switch (index) {
      case `${pageIndex}`:
        this.renderer.setAttribute(linkBtn, 'disabled', 'disabled');
        this.renderer.setAttribute(linkBtn, 'aria-current', 'page');
        this.renderer.removeClass(linkBtn, 'custom-paginator-page-enabled');
        this.renderer.addClass(linkBtn, 'custom-paginator-page-disabled');
        break;
      case this.pageGapTxt[0]:
        break;
        this.renderer.listen(linkBtn, 'click', () => {
          this.switchPage(
            this.currentPage < this.showTotalPages + 1
              ? this.showTotalPages + 2
              : this.currentPage + this.showTotalPages - 1,
          );
        });
        break;
      case this.pageGapTxt[1]:
        break;
        this.renderer.listen(linkBtn, 'click', () => {
          this.switchPage(
            this.currentPage > this.matPag.getNumberOfPages() - this.showTotalPages - 2
              ? this.matPag.getNumberOfPages() - this.showTotalPages - 3
              : this.currentPage - this.showTotalPages + 1,
          );
        });
        break;
      default:
        this.renderer.listen(linkBtn, 'click', () => {
          this.switchPage(+index);
        });
        break;
    }
    this.renderer.appendChild(linkBtn, text);
    // Add button to private array for state
    this.buttons.push(linkBtn);
    return linkBtn;
  }

  /**
   * @description calculates the button range based on class input parameters and based on current page index value.
   */
  private initPageRange(): void {
    this.rangeStart = this.currentPage - this.showTotalPages / 2;
    this.rangeEnd = this.currentPage + this.showTotalPages / 2;
    this.buildPageNumbers();
  }

  private switchPage(index: number): void {
    this.matPag.pageIndex = index;
    this.matPag.page.emit({
      previousPageIndex: this.currentPage,
      pageIndex: index,
      pageSize: this.matPag.pageSize,
      length: this.matPag.length,
    });
    this.currentPage = index;
    this.initPageRange();
  }

  public ngAfterViewInit(): void {
    this.rangeStart = 0;
    this.rangeEnd = this.showTotalPages - 1;
    this.initPageRange();

    /* Adding click event on Drop down showing on Pagination to show Option panel Up or down */
    let dropDownElement = this.ViewContainer.element.nativeElement.querySelector('.mat-select');
    dropDownElement.addEventListener('click', (e) => {
      this.forcePosition(e);
    });

    /* Mobile View */
    this.prepareMobileView();

    /* Repositioning DIV's to take care of Keyboard Tab issue as Angular material doesnot allow positiong of Results per page*/
    if (this.positionOfPageSize === 'right') {
      const paginationContainer = this.ViewContainer.element.nativeElement.querySelector('.mat-paginator-container');
      const paginationActions = this.ViewContainer.element.nativeElement.querySelector('.mat-paginator-range-actions');
      paginationContainer.insertBefore(paginationActions, paginationContainer.firstElementChild);
      paginationActions.classList.add('marginRight');
    }
  }

  public prepareMobileView(): void {
    const parentDiv = this.renderer.createElement('div');
    parentDiv.className = 'mobileview';

    /* Input field for mobile */
    var mobileViewInput = this.renderer.createElement('input');
    mobileViewInput.className = 'paginationTextField';
    mobileViewInput.setAttribute('type', 'number');
    mobileViewInput.setAttribute('value', 1);
    mobileViewInput.setAttribute('aria-describedby', 'mobile-aria-pages');
    mobileViewInput.addEventListener('change', this.updateRecord.bind(this)); // Cant set [(ngmodel)] as attribute so added the listener
    mobileViewInput.addEventListener('click', (e) => { e.target.select();});
    mobileViewInput.addEventListener('keypress', this.numbersOnly.bind(this));

    /* Text span for mobile */
    var mobileViewSpan = this.renderer.createElement('span');
    mobileViewSpan.setAttribute('id', 'mobile-aria-pages');
    mobileViewSpan.setAttribute('class', 'mobile-num-pages');
    var mobileViewText = this.renderer.createText(`${this.OF_LABEL} ${this.matPag.getNumberOfPages()} pages`);
    parentDiv.appendChild(mobileViewInput);
    mobileViewSpan.appendChild(mobileViewText);
    parentDiv.appendChild(mobileViewSpan);

    const actionContainer = this.ViewContainer.element.nativeElement.querySelector('div.mat-paginator-range-actions');
    const nextPageNode = this.ViewContainer.element.nativeElement.querySelector('button.mat-paginator-navigation-next');
    this.renderer.insertBefore(actionContainer, parentDiv, nextPageNode);
  }
public numbersOnly(e) {
    let onlyNum = /^([0-9])$/.test(e.key);
    if (!onlyNum) {
      e.preventDefault();
      e.target.value = null;
    }
  }
  public updateRecord(event) {
    const totalPages = this.matPag.getNumberOfPages();
    const setInputValue = event.target.value;
    const inputField = this.ViewContainer.element.nativeElement.querySelector('input.paginationTextField');
    if (setInputValue < 1) {
      this.matPag.pageIndex = 0;
      inputField.value = 1;
    } else if (setInputValue > totalPages) {
      this.matPag.pageIndex = totalPages - 1;
      inputField.value = totalPages;
    } else {
      this.matPag.pageIndex = event.target.value > 1 ? event.target.value - 1 : 0;
    }
  }

  /* Dropdown */
  public forcePosition(event) {
    setTimeout(() => {
      let openBelow: boolean;
      let elements = this._document.querySelectorAll('.mat-select-panel');
      elements.forEach((element) => {
        const spaceBelow = window.innerHeight - event.clientY;
        openBelow = spaceBelow > element.clientHeight + 25;
        const clientHeight = element.clientHeight + 11;
        element.classList.remove('openUp');
        element.classList.remove('openBelow');
        if (openBelow) {
          element.classList.add('openBelow');
        } else {
          element.classList.add('openUp');
          element.setAttribute('style', 'top:-' + clientHeight + 'px;');
        }
      });

      let elementsMain = this._document.querySelectorAll('.mat-select');
      elementsMain.forEach((element) => {
        element.classList.remove('openUp');
        element.classList.remove('openBelow');
        if (openBelow) {
          element.classList.add('openBelow');
        } else {
          element.classList.add('openUp');
        }
      });
    }, 100);
  }
}

Lignes directrices

Caractéristiques accessibles

Voici les principales caractéristiques d’un élément de pagination accessible :

  • Texte de l’annonceur de pagination
  • Bouton de pagination actuellement sélectionné qui est distinct visuellement et programmatiquement des autres boutons de pagination.
  • Dans l’affichage mobile, les boutons de pagination sont remplacés par un champ de saisie. Le champ de saisie comporte une chaîne de texte indiquant le nombre de pages disponibles.

Halo de saisie

Les liens ou boutons de pagination doivent avoir un contour de mise en évidence bien visible lorsqu’ils sont en état de survol ou de mise en évidence.

Interaction avec le clavier

  • Onglet : La mise en évidence passe de la cible du clavier à l’élément interactif suivant.
  • Touches « Maj + Tab » : La mise en évidence passe de la cible du clavier à l’élément interactif précédent.

Tous les éléments interactifs de la pagination se trouvent dans la hiérarchisation des onglets de page.

Les premier et dernier éléments interactifs d’un élément de pagination dépendent de la position du menu déroulant et de la page actuellement sélectionnée.

Le premier élément interactif sera toujours l’un des suivants :

  • Le bouton pour la page précédente
  • Le bouton pour la première page
  • Le menu déroulant

Le dernier élément interactif sera l’un des suivants :

  • Le menu déroulant
  • Le bouton pour la dernière page
  • Le bouton pour la page suivante

Lorsqu’un bouton de pagination est mis en évidence, Barre d’espacement ou touche « Entrée » active le bouton.

La mise en évidence demeure sur les boutons de pagination une fois qu’ils sont activés.

Commandes de pagination

Les commandes de pagination peuvent avoir le rôle d’un bouton ou d’un lien selon l’action qu’elles exécutent. L’apparence visuelle et la fonctionnalité des commandes demeurent les mêmes, peu importe le rôle assumé.

Lien ou bouton

Le rôle est réglé à « lien » lorsque les commandes de pagination chargent une nouvelle URL (p. ex., un nouvel ensemble de résultats de recherche).

Le rôle est réglé à « bouton » lorsque les commandes de pagination ne dirigent pas les utilisateurs vers une nouvelle adresse URL et chargent plutôt les valeurs précédentes ou suivantes d’un élément sur la même page (p. ex., les commandes de navigation dans un tableau).

Annonceur de pagination

L’annonceur de pagination est fourni pour veiller à ce que les utilisateurs de la conception non visuelle soient informés lorsque l’affichage de la page change. Lorsque l’utilisateur sélectionne un bouton de pagination, le texte de l’annonceur sera mis à jour afin avec les renseignements pertinents pour la nouvelle page.

Exemples

  1. Lorsqu’un utilisateur navigue dans les résultats de recherche, un changement de page pourrait annoncer « 21 de 40 résultats trouvés pour Timbres ».
  2. Lorsqu’un utilisateur navigue dans un élément de pagination à l’intérieur d’un document, l’élément peut annoncer quelque chose comme « Page 3 de 10 de la Table des matières » lorsque la page 3 est sélectionnée et affichée. Ce texte mis à jour est annoncé immédiatement au lecteur d’écran.

Propriétés ARIA

Les éléments de pagination peuvent avoir les propriétés ARIA suivantes :

  • Les boutons de pagination peuvent jouer le rôle de liens ou de boutons, selon l’application.
  • L’attribut aria-live du texte de l’annonceur de pagination est réglé à « polite » dans l’élément de conteneur.
  • Le bouton de pagination actuellement sélectionné est configuré à aria-current=”page”.
  • Le bloc de pagination (boutons de pagination et menu déroulant du nombre de résultats) est enveloppé dans une région de navigation.
  • La région de navigation comporte un ensemble d’étiquettes « Pagination » hors écran utilisant l’attribut aria-label.
  • Dans l’affichage mobile, les boutons de pagination sont remplacés par un champ de saisie. À côté du champ, une chaîne de texte indique le nombre de pages disponibles; cette chaîne est programmatiquement associée au champ à l’aide de l’attribut aria-describedby.

Test

Au moment de tester un élément de pagination, le testeur doit s’assurer de ce qui suit : 

  • Les éléments de pagination sont entièrement accessibles au moyen du clavier.
  • Les indications concernant les états de survol et de mise en évidence sont claires et bien en vue. Les boutons de pagination ont un halo en mode survol ou mise en évidence, et le menu déroulant et ses options sont mis en évidence en mode survol ou mise en évidence.
  • Le texte de l’annonceur de pagination change et il est annoncé au lecteur d’écran lorsque l’utilisateur se rend sur une nouvelle page.
  • La modification des résultats par page dans le menu déroulant modifie le texte de l’annonceur de pagination et ce texte mis à jour est annoncé au lecteur d’écran.
  • Le rôle du bouton de pagination est pertinent pour la fonction qu’il exécute.
  • Le bloc de pagination (boutons de pagination et menu déroulant) est enveloppé dans une région de navigation. Le lecteur d’écran annonce correctement tous les rôles (c.-à-d. la région, le lien ou le bouton, et l’ensemble de cases multiples).
  • La région de navigation porte l’étiquette « Pagination » et le lecteur d’écran annonce l’étiquette de la région avec la région de navigation.
  • Caractéristiques du bouton de page actuel :
    • La conception indique qu’il s’agit de la page actuelle.
    • Le lecteur d’écran l’annonce comme étant la page actuelle.
  • Sur les appareils mobiles, le lecteur d’écran annonce la valeur actuelle du champ d’entrée de pagination ainsi que le texte indiquant le nombre total de pages disponibles.
  • Le champ de saisie, la commande de la page précédente et de la page suivante ainsi que le menu déroulant subissent tous des changements visuels clairs et évidents lorsqu’on les sélectionne à l’écran d’un appareil mobile.

Contenu connexe