PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.1.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.1.1
7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 6.0.5 All 36 releases
mlsimport / includes / standalone / class-mlsimport-pagination.php

class-mlsimport-pagination.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.1.1, at includes/standalone/class-mlsimport-pagination.php

121 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Standalone (theme_id 990) pagination component.
4 *
5 * One source of pager markup for every listings surface: the AJAX listings grid
6 * (Mlsimport_Standalone_Render::render_grid + the AJAX repaint) and the GET-based
7 * Search Results page block. Given a total, a page size and the current page it
8 * returns a <nav> of page controls (or '' for a single page).
9 *
10 * Each control is an anchor carrying BOTH a real ?page= URL (shareable / no-JS /
11 * the GET block) and a data-page hook (the listings JS intercepts these to repaint
12 * the grid via AJAX). The page list is windowed so a large set collapses to
13 * first … current±2 … last instead of printing every page.
14 *
15 * @package Mlsimport
16 */
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 /**
23 * Renders the shared listings pager.
24 */
25 class Mlsimport_Pagination {
26
27 /**
28 * Page numbers to keep either side of the current page before collapsing the
29 * run to an ellipsis.
30 */
31 const WINDOW = 2;
32
33 /**
34 * Render the pager.
35 *
36 * @param int $total Full match count (not the page count).
37 * @param int $per_page Page size.
38 * @param int $current Current page (1-based).
39 * @param array $opts { @type string $label aria-label for the nav. @type string $param
40 * query-arg key for the page links (default 'page'). Singular pages
41 * must use a non-reserved key — WP's redirect_canonical strips ?page=
42 * on a single post that has no <!--nextpage--> content. }
43 * @return string Pager HTML, or '' when there is a single page.
44 */
45 public static function render( int $total, int $per_page, int $current, array $opts = array() ): string {
46 $per_page = max( 1, $per_page );
47 $pages = (int) ceil( $total / $per_page );
48 if ( $pages < 2 ) {
49 return '';
50 }
51 $current = min( max( 1, $current ), $pages );
52 $label = isset( $opts['label'] ) ? (string) $opts['label'] : __( 'Listings pagination', 'mlsimport' );
53 $param = isset( $opts['param'] ) ? (string) $opts['param'] : 'page';
54
55 $out = '<nav class="mlsimport-pager" role="navigation" aria-label="' . esc_attr( $label ) . '">';
56 if ( $current > 1 ) {
57 $out .= self::link( $current - 1, __( 'Previous', 'mlsimport' ), $param, 'mlsimport-pager__prev', 'prev' );
58 }
59 foreach ( self::page_list( $pages, $current ) as $page ) {
60 if ( 0 === $page ) {
61 $out .= '<span class="mlsimport-pager__gap" aria-hidden="true">&hellip;</span>';
62 } elseif ( $page === $current ) {
63 $out .= '<span class="mlsimport-pager__btn is-active" aria-current="page">' . esc_html( (string) $page ) . '</span>';
64 } else {
65 $out .= self::link( $page, (string) $page, $param );
66 }
67 }
68 if ( $current < $pages ) {
69 $out .= self::link( $current + 1, __( 'Next', 'mlsimport' ), $param, 'mlsimport-pager__next', 'next' );
70 }
71 $out .= '</nav>';
72 return $out;
73 }
74
75 /**
76 * One page control: a real ?<param>= link that the AJAX layer hooks via data-page.
77 *
78 * @param int $page Target page.
79 * @param string $label Visible label.
80 * @param string $param Query-arg key for the page number.
81 * @param string $extra_class Extra BEM modifier class.
82 * @param string $rel Optional rel attribute (prev/next).
83 * @return string
84 */
85 private static function link( int $page, string $label, string $param = 'page', string $extra_class = '', string $rel = '' ): string {
86 $class = 'mlsimport-pager__btn' . ( '' !== $extra_class ? ' ' . $extra_class : '' );
87 $rel = '' !== $rel ? ' rel="' . esc_attr( $rel ) . '"' : '';
88 return '<a class="' . esc_attr( $class ) . '" href="' . esc_url( add_query_arg( $param, $page ) ) . '"'
89 . ' data-page="' . esc_attr( (string) $page ) . '"' . $rel . '>' . esc_html( $label ) . '</a>';
90 }
91
92 /**
93 * The page-number sequence to render: page 1, the window around the current
94 * page, and the last page — with 0 marking an ellipsis gap where pages are
95 * skipped. E.g. pages=20 current=10 -> [1,0,8,9,10,11,12,0,20].
96 *
97 * @param int $pages Total pages.
98 * @param int $current Current page.
99 * @return int[]
100 */
101 private static function page_list( int $pages, int $current ): array {
102 $keep = array();
103 for ( $page = 1; $page <= $pages; $page++ ) {
104 if ( 1 === $page || $pages === $page || ( $page >= $current - self::WINDOW && $page <= $current + self::WINDOW ) ) {
105 $keep[] = $page;
106 }
107 }
108
109 $list = array();
110 $prev = 0;
111 foreach ( $keep as $page ) {
112 if ( $prev && $page - $prev > 1 ) {
113 $list[] = 0; // Gap marker.
114 }
115 $list[] = $page;
116 $prev = $page;
117 }
118 return $list;
119 }
120 }
121