UNPKG

7.92 kBJavaScriptView Raw
1/**
2 * Copyright IBM Corp. 2016, 2018
3 *
4 * This source code is licensed under the Apache-2.0 license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import settings from '../../globals/js/settings';
9import mixin from '../../globals/js/misc/mixin';
10import createComponent from '../../globals/js/mixins/create-component';
11import initComponentBySearch from '../../globals/js/mixins/init-component-by-search';
12import handles from '../../globals/js/mixins/handles';
13import on from '../../globals/js/misc/on';
14
15class PaginationNav extends mixin(
16 createComponent,
17 initComponentBySearch,
18 handles
19) {
20 /**
21 * Pagination Nav component
22 * @extends CreateComponent
23 * @extends InitComponentBySearch
24 * @extends Handles
25 * @param {HTMLElement} element The element working as a pagination nav.
26 */
27 constructor(element, options) {
28 super(element, options);
29 this.manage(on(this.element, 'click', (evt) => this.handleClick(evt)));
30 this.manage(
31 on(this.element, 'change', (evt) => {
32 if (evt.target.matches(this.options.selectorPageSelect)) {
33 this.handleSelectChange(evt);
34 }
35 })
36 );
37 }
38
39 /**
40 * Get active page number
41 */
42 getActivePageNumber = () => {
43 let pageNum;
44 const activePageElement = this.element.querySelector(
45 this.options.selectorPageActive
46 );
47 if (activePageElement) {
48 pageNum = Number(activePageElement.getAttribute(this.options.attribPage));
49 }
50 return pageNum;
51 };
52
53 /**
54 * Clear active page attributes
55 */
56 clearActivePage = (evt) => {
57 const pageButtonNodeList = this.element.querySelectorAll(
58 this.options.selectorPageButton
59 );
60 const pageSelectElement = this.element.querySelector(
61 this.options.selectorPageSelect
62 );
63 Array.prototype.forEach.call(pageButtonNodeList, (el) => {
64 el.classList.remove(this.options.classActive, this.options.classDisabled);
65 el.removeAttribute(this.options.attribActive);
66 el.removeAttribute('aria-disabled');
67 el.removeAttribute('aria-current');
68 });
69 if (pageSelectElement) {
70 pageSelectElement.removeAttribute('aria-current');
71 const pageSelectElementOptions = pageSelectElement.options;
72 Array.prototype.forEach.call(pageSelectElementOptions, (el) => {
73 el.removeAttribute(this.options.attribActive);
74 });
75 if (!evt.target.matches(this.options.selectorPageSelect)) {
76 pageSelectElement.classList.remove(this.options.classActive);
77 pageSelectElement.value = '';
78 }
79 }
80 };
81
82 /**
83 * Add active state on click
84 */
85 handleClick = (evt) => {
86 if (!evt.target.getAttribute('aria-disabled') === true) {
87 let nextActivePageNumber = this.getActivePageNumber();
88 const pageElementNodeList = this.element.querySelectorAll(
89 this.options.selectorPageElement
90 );
91 const pageSelectElement = this.element.querySelector(
92 this.options.selectorPageSelect
93 );
94 this.clearActivePage(evt);
95 if (evt.target.matches(this.options.selectorPageButton)) {
96 nextActivePageNumber = Number(
97 evt.target.getAttribute(this.options.attribPage)
98 );
99 }
100 if (evt.target.matches(this.options.selectorPagePrevious)) {
101 nextActivePageNumber -= 1;
102 }
103 if (evt.target.matches(this.options.selectorPageNext)) {
104 nextActivePageNumber += 1;
105 }
106 const pageTargetElement = pageElementNodeList[nextActivePageNumber - 1];
107 pageTargetElement.setAttribute(this.options.attribActive, true);
108 if (pageTargetElement.tagName === 'OPTION') {
109 pageSelectElement.value = this.getActivePageNumber();
110 pageSelectElement.classList.add(this.options.classActive);
111 pageSelectElement.setAttribute('aria-current', 'page');
112 } else {
113 pageTargetElement.classList.add(
114 this.options.classActive,
115 this.options.classDisabled
116 );
117 pageTargetElement.setAttribute('aria-disabled', true);
118 pageTargetElement.setAttribute('aria-current', 'page');
119 }
120 this.setPrevNextStates();
121 }
122 };
123
124 /**
125 * Handle select menu on change
126 */
127 handleSelectChange = (evt) => {
128 this.clearActivePage(evt);
129 const pageSelectElement = this.element.querySelector(
130 this.options.selectorPageSelect
131 );
132 const pageSelectElementOptions = pageSelectElement.options;
133 pageSelectElementOptions[
134 pageSelectElementOptions.selectedIndex
135 ].setAttribute(this.options.attribActive, true);
136 evt.target.setAttribute('aria-current', 'page');
137 evt.target.classList.add(this.options.classActive);
138 this.setPrevNextStates();
139 };
140
141 /**
142 * Set Previous and Next button states
143 */
144 setPrevNextStates = () => {
145 const pageElementNodeList = this.element.querySelectorAll(
146 this.options.selectorPageElement
147 );
148 const totalPages = pageElementNodeList.length;
149 const pageDirectionElementPrevious = this.element.querySelector(
150 this.options.selectorPagePrevious
151 );
152 const pageDirectionElementNext = this.element.querySelector(
153 this.options.selectorPageNext
154 );
155 if (pageDirectionElementPrevious) {
156 if (this.getActivePageNumber() <= 1) {
157 pageDirectionElementPrevious.setAttribute('aria-disabled', true);
158 pageDirectionElementPrevious.classList.add(this.options.classDisabled);
159 } else {
160 pageDirectionElementPrevious.removeAttribute('aria-disabled');
161 pageDirectionElementPrevious.classList.remove(
162 this.options.classDisabled
163 );
164 }
165 }
166 if (pageDirectionElementNext) {
167 if (this.getActivePageNumber() >= totalPages) {
168 pageDirectionElementNext.setAttribute('aria-disabled', true);
169 pageDirectionElementNext.classList.add(this.options.classDisabled);
170 } else {
171 pageDirectionElementNext.removeAttribute('aria-disabled');
172 pageDirectionElementNext.classList.remove(this.options.classDisabled);
173 }
174 }
175 };
176
177 /**
178 * The map associating DOM element and pagination nav instance.
179 * @member PaginationNav.components
180 * @type {WeakMap}
181 */
182 static components /* #__PURE_CLASS_PROPERTY__ */ = new WeakMap();
183
184 /**
185 * The component options.
186 * If `options` is specified in the constructor, {@linkcode PaginationNav.create .create()},
187 * or {@linkcode PaginationNav.init .init()},
188 * properties in this object are overriden for the instance being create and how {@linkcode PaginationNav.init .init()} works.
189 * @member PaginationNav.options
190 * @type {object}
191 * @property {string} selectorInit The data attribute to find pagination nav.
192 * @property {string} selectorPageElement The data attribute to find page element.
193 * @property {string} selectorPageButton The data attribute to find page interactive element.
194 * @property {string} selectorPageDirection The data attribute to find page change element.
195 * @property {string} selectorPageSelect The data attribute to find page select element.
196 * @property {string} selectorPageActive The data attribute to find active page element.
197 * @property {string} [classActive] The CSS class for page's selected state.
198 * @property {string} [classDisabled] The CSS class for page's disabled state.
199 */
200 static get options() {
201 const { prefix } = settings;
202 return {
203 selectorInit: '[data-pagination-nav]',
204 selectorPageElement: '[data-page]',
205 selectorPageButton: '[data-page-button]',
206 selectorPagePrevious: '[data-page-previous]',
207 selectorPageNext: '[data-page-next]',
208 selectorPageSelect: '[data-page-select]',
209 selectorPageActive: '[data-page-active="true"]',
210 attribPage: 'data-page',
211 attribActive: 'data-page-active',
212 classActive: `${prefix}--pagination-nav__page--active`,
213 classDisabled: `${prefix}--pagination-nav__page--disabled`,
214 };
215 }
216}
217
218export default PaginationNav;