|
| 1 | +import defaults from 'lodash/defaults'; |
| 2 | +import cx from 'classnames'; |
| 3 | +import { |
| 4 | + bemHelper, |
| 5 | + getContainerNode, |
| 6 | +} from '../../lib/utils.js'; |
| 7 | + |
| 8 | +const defaultLabels = { |
| 9 | + previous: '‹', |
| 10 | + next: '›', |
| 11 | + first: '«', |
| 12 | + last: '»', |
| 13 | +}; |
| 14 | +const bem = bemHelper('ais-pagination'); |
| 15 | + |
| 16 | +/** |
| 17 | + * Add a pagination menu to navigate through the results |
| 18 | + * @function pagination |
| 19 | + * @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget |
| 20 | + * @param {Object} [options.labels] Text to display in the various links (prev, next, first, last) |
| 21 | + * @param {string} [options.labels.previous] Label for the Previous link |
| 22 | + * @param {string} [options.labels.next] Label for the Next link |
| 23 | + * @param {string} [options.labels.first] Label for the First link |
| 24 | + * @param {string} [options.labels.last] Label for the Last link |
| 25 | + * @param {number} [options.maxPages] The max number of pages to browse |
| 26 | + * @param {number} [options.padding=3] The number of pages to display on each side of the current page |
| 27 | + * @param {string|DOMElement|boolean} [options.scrollTo='body'] Where to scroll after a click, set to `false` to disable |
| 28 | + * @param {boolean} [options.showFirstLast=true] Define if the First and Last links should be displayed |
| 29 | + * @param {boolean} [options.autoHideContainer=true] Hide the container when no results match |
| 30 | + * @param {Object} [options.cssClasses] CSS classes to be added |
| 31 | + * @param {string|string[]} [options.cssClasses.root] CSS classes added to the parent `<ul>` |
| 32 | + * @param {string|string[]} [options.cssClasses.item] CSS classes added to each `<li>` |
| 33 | + * @param {string|string[]} [options.cssClasses.link] CSS classes added to each link |
| 34 | + * @param {string|string[]} [options.cssClasses.page] CSS classes added to page `<li>` |
| 35 | + * @param {string|string[]} [options.cssClasses.previous] CSS classes added to the previous `<li>` |
| 36 | + * @param {string|string[]} [options.cssClasses.next] CSS classes added to the next `<li>` |
| 37 | + * @param {string|string[]} [options.cssClasses.first] CSS classes added to the first `<li>` |
| 38 | + * @param {string|string[]} [options.cssClasses.last] CSS classes added to the last `<li>` |
| 39 | + * @param {string|string[]} [options.cssClasses.active] CSS classes added to the active `<li>` |
| 40 | + * @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `<li>` |
| 41 | + * @return {Object} |
| 42 | + */ |
| 43 | +const usage = `Usage: |
| 44 | +pagination({ |
| 45 | + container, |
| 46 | + [ cssClasses.{root,item,page,previous,next,first,last,active,disabled}={} ], |
| 47 | + [ labels.{previous,next,first,last} ], |
| 48 | + [ maxPages ], |
| 49 | + [ padding=3 ], |
| 50 | + [ showFirstLast=true ], |
| 51 | + [ autoHideContainer=true ], |
| 52 | + [ scrollTo='body' ] |
| 53 | +})`; |
| 54 | +const connectPagination = paginationRendering => ({ |
| 55 | + container, |
| 56 | + cssClasses: userCssClasses = {}, |
| 57 | + labels: userLabels = {}, |
| 58 | + maxPages, |
| 59 | + padding = 3, |
| 60 | + showFirstLast = true, |
| 61 | + autoHideContainer = true, |
| 62 | + scrollTo: userScrollTo = 'body', |
| 63 | + } = {}) => { |
| 64 | + let scrollTo = userScrollTo; |
| 65 | + |
| 66 | + if (!container) { |
| 67 | + throw new Error(usage); |
| 68 | + } |
| 69 | + |
| 70 | + if (scrollTo === true) { |
| 71 | + scrollTo = 'body'; |
| 72 | + } |
| 73 | + |
| 74 | + const containerNode = getContainerNode(container); |
| 75 | + const scrollToNode = scrollTo !== false ? getContainerNode(scrollTo) : false; |
| 76 | + |
| 77 | + const cssClasses = { |
| 78 | + root: cx(bem(null), userCssClasses.root), |
| 79 | + item: cx(bem('item'), userCssClasses.item), |
| 80 | + link: cx(bem('link'), userCssClasses.link), |
| 81 | + page: cx(bem('item', 'page'), userCssClasses.page), |
| 82 | + previous: cx(bem('item', 'previous'), userCssClasses.previous), |
| 83 | + next: cx(bem('item', 'next'), userCssClasses.next), |
| 84 | + first: cx(bem('item', 'first'), userCssClasses.first), |
| 85 | + last: cx(bem('item', 'last'), userCssClasses.last), |
| 86 | + active: cx(bem('item', 'active'), userCssClasses.active), |
| 87 | + disabled: cx(bem('item', 'disabled'), userCssClasses.disabled), |
| 88 | + }; |
| 89 | + |
| 90 | + const labels = defaults(userLabels, defaultLabels); |
| 91 | + |
| 92 | + return { |
| 93 | + init({helper, createURL}) { |
| 94 | + this.setCurrentPage = page => { |
| 95 | + helper.setCurrentPage(page); |
| 96 | + if (scrollToNode !== false) { |
| 97 | + scrollToNode.scrollIntoView(); |
| 98 | + } |
| 99 | + helper.search(); |
| 100 | + }; |
| 101 | + |
| 102 | + this.createURL = state => page => createURL(state.setPage(page)); |
| 103 | + |
| 104 | + paginationRendering({ |
| 105 | + createURL: this.createURL(helper.state), |
| 106 | + cssClasses, |
| 107 | + currentPage: helper.getPage(), |
| 108 | + labels, |
| 109 | + nbHits: 0, |
| 110 | + nbPages: 0, |
| 111 | + padding, |
| 112 | + setCurrentPage: this.setCurrentPage, |
| 113 | + shouldAutoHideContainer: autoHideContainer, |
| 114 | + showFirstLast, |
| 115 | + containerNode, |
| 116 | + }, true); |
| 117 | + }, |
| 118 | + |
| 119 | + getMaxPage(results) { |
| 120 | + if (maxPages !== undefined) { |
| 121 | + return Math.min(maxPages, results.nbPages); |
| 122 | + } |
| 123 | + return results.nbPages; |
| 124 | + }, |
| 125 | + |
| 126 | + render({results, state}) { |
| 127 | + paginationRendering({ |
| 128 | + createURL: this.createURL(state), |
| 129 | + cssClasses, |
| 130 | + currentPage: results.page, |
| 131 | + labels, |
| 132 | + nbHits: results.nbHits, |
| 133 | + nbPages: this.getMaxPage(results), |
| 134 | + padding, |
| 135 | + setCurrentPage: this.setCurrentPage, |
| 136 | + shouldAutoHideContainer: autoHideContainer && results.nbHits === 0, |
| 137 | + showFirstLast, |
| 138 | + containerNode, |
| 139 | + }, false); |
| 140 | + }, |
| 141 | + }; |
| 142 | +}; |
| 143 | + |
| 144 | +export default connectPagination; |
0 commit comments