mCaption = $caption; $this->getOutput()->addModules( 'mediawiki.pager.codex' ); parent::__construct( $context, $linkRenderer ); $this->getOutput()->addJsConfigVars( [ 'wgCodexTablePagerLimit' => $this->mLimit, ] ); } /** * Get the entire Codex table markup, including the wrapper element, pagers, table wrapper * (which enables horizontal scroll), and the table element. * * @since 1.44 */ public function getFullOutput(): ParserOutput { // Pagers. $navigation = $this->getNavigationBar(); // `` element and its contents. $body = parent::getBody(); $pout = new ParserOutput(); $pout->setRawText( Html::openElement( 'div', [ 'class' => 'cdx-table' ] ) . "\n" . // In the future, a visible caption + header content could go here. $navigation . "\n" . Html::openElement( 'div', [ 'class' => 'cdx-table__table-wrapper' ] ) . "\n" . $body . "\n" . Html::closeElement( 'div' ) . "\n" . // In the future, footer content could go here. $navigation . "\n" . Html::closeElement( 'div' ) ); $pout->addModuleStyles( $this->getModuleStyles() ); return $pout; } /** * Generate the `` element. * * This creates a thead with a single tr and includes sort buttons if applicable. To customize * the thead layout, override this method. * * @stable to override */ protected function getThead(): string { $theadContent = ''; $fields = $this->getFieldNames(); // Build each th element. foreach ( $fields as $field => $name ) { if ( $name === '' ) { // th with no label (not advised). $theadContent .= Html::rawElement( 'th', $this->getCellAttrs( $field, $name ), "\u{00A0}" ) . "\n"; } elseif ( $this->isFieldSortable( $field ) ) { // Sortable column. $query = [ 'sort' => $field, 'limit' => $this->mLimit ]; $sortIconClasses = [ 'cdx-table__table__sort-icon' ]; if ( $this->mSort == $field ) { // Set data for the currently sorted column. if ( $this->mDefaultDirection == IndexPager::DIR_DESCENDING ) { $sortIconClasses[] = 'cdx-table__table__sort-icon--desc'; $query['asc'] = '1'; $query['desc'] = ''; } else { $sortIconClasses[] = 'cdx-table__table__sort-icon--asc'; $query['asc'] = ''; $query['desc'] = '1'; } } else { $sortIconClasses[] = 'cdx-table__table__sort-icon--unsorted'; } // Build the label and icon span that go inside the link. $linkContents = Html::rawElement( 'span', [ 'class' => 'cdx-table__table__sort-label' ], htmlspecialchars( $name ) ) . "\n" . Html::rawElement( 'span', [ 'class' => $sortIconClasses, 'aria-hidden' => true ] ) . "\n"; // Build the link that goes inside the th. $link = Html::rawElement( 'a', [ 'class' => [ 'cdx-table__table__sort-button' ], 'role' => 'button', 'href' => $this->getTitle()->getLinkURL( $query + $this->getDefaultQuery() ), ], $linkContents ); // Build the th. $thAttrs = $this->getCellAttrs( $field, $name ); $thAttrs[ 'class' ][] = $this->getSortHeaderClass(); $theadContent .= Html::rawElement( 'th', $thAttrs, $link ) . "\n"; } else { // Unsortable column. $theadContent .= Html::element( 'th', $this->getCellAttrs( $field, $name ), $name ) . "\n"; } } return Html::rawElement( 'thead', [], Html::rawElement( 'tr', [], "\n" . $theadContent . "\n" ) ); } /** * Append text to the caption if any fields are sortable. * * @param string $captionText Caption provided for the table */ private function getFullCaption( string $captionText ): string { $fields = $this->getFieldNames(); // Make table header foreach ( $fields as $field => $name ) { if ( $this->isFieldSortable( $field ) === true ) { return $this->msg( 'cdx-table-sort-caption', $captionText )->text(); } } return $captionText; } /** * Get the opening table tag through the opening tbody tag. * * This method should generally not be overridden: use getThead() to create a custom `` * and getTableClass to set additional classes on the `
` element. * * @stable to override */ protected function getStartBody(): string { $ret = Html::openElement( 'table', [ 'class' => $this->getTableClass() ] ); $ret .= Html::rawElement( 'caption', [], $this->getFullCaption( $this->mCaption ) ); $ret .= $this->getThead(); $ret .= Html::openElement( 'tbody' ) . "\n"; return $ret; } /** * Override to add a `` element. * * @stable to override */ protected function getTfoot(): string { return ''; } /** * Get the closing tbody tag through the closing table tag. * * @stable to override */ protected function getEndBody(): string { return "" . $this->getTfoot() . "
\n"; } /** * Get markup for the "no results" UI. This is placed inside the tbody tag. */ protected function getEmptyBody(): string { $colspan = count( $this->getFieldNames() ); $msgEmpty = $this->msg( 'table_pager_empty' )->text(); return Html::rawElement( 'tr', [ 'class' => 'cdx-table__table__empty-state' ], Html::element( 'td', [ 'class' => 'cdx-table__table__empty-state-content', 'colspan' => $colspan ], $msgEmpty ) ); } /** * Add alignment per column. * * @param string $field The column * @return string start (default), center, end, or number (always to the right) */ protected function getCellAlignment( string $field ): string { return 'start'; } /** * Add extra attributes to be applied to the given cell. * * @stable to override * * @param string $field The column * @param string $value The cell contents * @return array Array of attr => value */ protected function getCellAttrs( $field, $value ): array { return [ 'class' => [ 'cdx-table-pager__col--' . $field, 'cdx-table__table__cell--align-' . $this->getCellAlignment( $field ) ] ]; } /** * Class for the `` element. * * @stable to override */ protected function getTableClass(): string { return 'cdx-table__table'; } /** * Class for the outermost element of the pager UI. * * @stable to override */ protected function getNavClass(): string { return 'cdx-table-pager'; } /** * Class for th elements of sortable columns. * * @stable to override */ protected function getSortHeaderClass(): string { return 'cdx-table__table__cell--has-sort'; } /** * Pager bar with per-page limit and pager buttons. * * @stable to override * * @return string HTML for the pager UI */ public function getNavigationBar(): string { if ( !$this->isNavigationBarShown() ) { return ''; } $types = [ 'first', 'prev', 'next', 'last' ]; $queries = $this->getPagingQueries(); $title = $this->getTitle(); $buttons = []; foreach ( $types as $type ) { // TODO: Update Codex class suffix for previous to 'prev' so we don't have to do this. $classSuffix = $type === 'prev' ? 'previous' : $type; $isDisabled = $queries[ $type ] === false; $buttons[] = Html::rawElement( 'a', [ 'class' => [ 'cdx-button', 'cdx-button--fake-button', 'cdx-button--fake-button--' . ( $isDisabled ? 'disabled' : 'enabled' ), 'cdx-button--weight-quiet', 'cdx-button--icon-only' ], 'role' => 'button', 'disabled' => $queries[ $type ] === false, 'aria-label' => $this->msg( 'table_pager_' . $type )->text(), 'href' => $queries[ $type ] ? $title->getLinkURL( $queries[ $type ] + $this->getDefaultQuery() ) : null, ], Html::rawElement( 'span', [ 'class' => [ 'cdx-button__icon', 'cdx-table-pager__icon--' . $classSuffix ] ] ) ); } return Html::openElement( 'div', [ 'class' => $this->getNavClass() ] ) . "\n" . Html::rawElement( 'div', [ 'class' => 'cdx-table-pager__start' ], $this->getLimitForm() ) . "\n" . Html::rawElement( 'div', [ 'class' => 'cdx-table-pager__end' ], implode( '', $buttons ) ) . "\n" . Html::closeElement( 'div' ); } /** * Get a `