-1:false;}).map(function(c){return c.index;}).join(" "));var b=f.querySelectorAll("[data-rl-column]"),w=f.querySelector("[data-rl-columns-words]"),x=w?parseInt(w.getAttribute("data-rl-columns-fixed"),10)||0:0,n=x;b.forEach(function(e){e.checked=-1===p.hide.indexOf(e.getAttribute("data-rl-column"));if(e.checked){n++;}});if(w?(w.lastChild?3===w.lastChild.nodeType:false):false){w.lastChild.nodeValue=w.getAttribute("data-rl-columns-words").replace("%%1$s",n).replace("%%2$s",x+b.length);}}if(p.compact){t.classList.add("rooster-lms-table--compact");var d=f.querySelector("[data-rl-density]");if(d){d.setAttribute("aria-pressed","true");}}}catch(e){}})(document.getElementById(%s));';
private static array $early = array();
public static function early_prefs( string $html ): string {
if ( ! str_contains( $html, 'rooster-lms-table-frame' ) || ! preg_match_all( '/\bid="(rooster-lms-table-\d+)"[^>]*\bclass="rooster-lms rooster-lms-table-frame\b/', $html, $m ) ) {
return $html;
}
foreach ( array_unique( $m[1] ) as $uid ) {
if ( ! isset( self::$early[ $uid ] ) ) {
self::$early[ $uid ] = true;
$html = EarlyScripts::after( $html, sprintf( self::PREFS_JS, wp_json_encode( $uid ) ) );
}
}
return $html;
}
public static function display( TableDefinition $d, ?TableQuery $q = null ): void {
list( $html, $uid ) = self::frame( $d, $q );
if ( '' === $html ) {
return;
}
echo wp_kses( $html, Kses::current() );
if ( '' !== $uid ) {
wp_print_inline_script_tag( sprintf( self::PREFS_JS, wp_json_encode( $uid ) ) );
}
}
public static function render( TableDefinition $d, ?TableQuery $q = null ): string {
list( $html, $uid ) = self::frame( $d, $q );
if ( '' !== $uid ) {
wp_add_inline_script( Assets::TABLE, sprintf( self::PREFS_JS, wp_json_encode( $uid ) ), 'before' );
}
return $html;
}
private static function frame( TableDefinition $d, ?TableQuery $q ): array {
if ( ! $d->can_view( get_current_user_id(), $d->context() ) ) {
return array( '', '' );
}
$q = $q ?? TableQuery::from_request( $d );
if ( LibraryTable::eligible( $d ) ) {
return array( LibraryTable::frame( $d, $q ), '' );
}
$whole = self::whole( $d, $q, null );
list( $q, $page ) = self::read( $d, $q );
$whole = $whole ?? self::whole( $d, $q, $page );
Assets::enqueue_table( $d->mode(), $d->search_live() );
if ( self::searchable( $d, $page ) || '' !== $whole ) {
Assets::enqueue_table_search();
}
if ( is_admin() ) {
Assets::enqueue_row_actions();
}
++self::$count;
$uid = 'rooster-lms-table-' . self::$count;
$caption = '' !== $d->caption() ? $d->caption() : __( 'Table', 'rooster-lms' );
$columns = self::columns( $d );
$bulk = $d->bulk();
$attrs = array(
'id' => $uid,
'class' => 'rooster-lms rooster-lms-table-frame',
'data-rl-table' => $d->id(),
'data-rl-mode' => $d->mode(),
'data-rl-state' => (string) wp_json_encode( self::state( $d, $q, $page, '' !== $whole ) ),
'data-rl-i18n' => (string) wp_json_encode( self::strings( $d ) ),
'data-rl-paging' => $d->paging(),
);
if ( $d->search_live() ) {
$attrs['data-rl-search-live'] = '1';
}
$view_key = self::view_filter( $d );
if ( '' !== $view_key ) {
$attrs['data-rl-views'] = $view_key;
}
if ( 'router' === $d->mode() && $d->live_frame() ) {
$attrs['data-rl-rest'] = self::live_config( $d );
if ( null !== $page->total ) {
$attrs['data-rl-token'] = CountToken::issue( $d->id(), $d->context(), $q->hash(), array( 'n' => $page->total ) );
}
}
$html = '
';
$html .= self::toolbar( $d, $q, $uid, $columns );
$html .= '
' . self::chips( $d, $q, $page ) . '
';
$html .= '
';
if ( array() !== $bulk ) {
$html .= '
';
}
$html .= '
';
$html .= self::footer( $d, $q, $page, $uid );
$html .= '
';
return array( $html, $uid );
}
public static function fragment( TableDefinition $d, TableQuery $q ): array {
$all = null;
$whole = self::whole( $d, $q, null, $all );
if ( null !== $all && '' !== (string) $whole ) {
list( $q, $page ) = self::local_page( $d, $q, $all );
} else {
list( $q, $page ) = self::read( $d, $q );
}
$columns = self::columns( $d );
$whole = $whole ?? self::whole( $d, $q, $page );
return array(
'whole' => $whole,
'thead' => self::head( $d, $q, $columns ),
'tbody' => self::body( $d, $q, $page, $columns ),
'pager' => self::pager( $d, $q, $page ),
'summary' => self::summary( $q, $page ),
'chips' => self::chips( $d, $q, $page ),
'total' => $page->total,
'page' => $q->page,
'pages' => self::pages( $q, $page ),
'more' => $page->more,
'after' => $page->after,
'state' => self::state( $d, $q, $page, '' !== $whole ),
);
}
private static function whole( TableDefinition $d, TableQuery $q, ?TablePage $page, ?TablePage &$all = null ): ?string {
$local = $d->local_filters();
if ( array() === $local || 'offset' !== $d->paging() || '' !== $q->search ) {
return '';
}
$clear = array();
foreach ( $local as $key => $server_only ) {
$value = $q->filters[ $key ] ?? '';
if ( '' === $value || array() === $value ) {
continue;
}
if ( array_intersect( array_map( 'strval', (array) $value ), array_map( 'strval', (array) $server_only ) ) ) {
return '';
}
$clear[ $key ] = null;
}
if ( null === $page && array() === $clear ) {
return null;
}
$cap = max( $q->per_page, $d->local_cap() );
if ( null !== $page && ( null === $page->total || $page->total > $cap ) ) {
return '';
}
$whole = array(
'page' => 1,
'per_page' => $cap,
'known_total' => null,
);
if ( null === $page ) {
$read = $d->source()->page( $q->with( $whole + array( 'filters' => $clear ) ) );
} elseif ( 1 === $q->page && count( $page->rows ) >= (int) $page->total ) {
$read = $page;
} else {
$read = $d->source()->page( $q->with( $whole ) );
}
if ( null === $read->total || $read->total > $cap || count( $read->rows ) < $read->total ) {
return '';
}
if ( null === $page ) {
$all = $read;
}
$sorts = array() !== $d->local_sorts();
$rows = '';
foreach ( $read->rows as $row ) {
$tr = $d->row( $row );
if ( '' === $tr ) {
continue;
}
$values = array();
foreach ( $d->filter_values( $row ) as $key => $list ) {
$values[ (string) $key ] = array_values( array_map( 'strval', (array) $list ) );
}
$attrs = ' data-ra-f="' . esc_attr( (string) wp_json_encode( (object) $values, JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG ) ) . '"';
if ( $sorts ) {
$attrs .= ' data-ra-s="' . esc_attr( (string) wp_json_encode( (object) $d->sort_values( $row ), JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG ) ) . '"';
}
$rows .= (string) preg_replace( '/^' . $rows . '';
}
private static function local_page( TableDefinition $d, TableQuery $q, TablePage $all ): array {
$first = array();
foreach ( $d->filters() as $filter ) {
$keys = array_keys( $filter->options );
$first[ $filter->key ] = isset( $keys[0] ) ? (string) $keys[0] : '';
}
$asked = array();
foreach ( array_keys( $d->local_filters() ) as $key ) {
$value = $q->filters[ $key ] ?? '';
if ( '' === $value || array() === $value || ( ! is_array( $value ) && '0' === (string) $value && '0' === ( $first[ $key ] ?? '' ) ) ) {
continue;
}
$asked[ $key ] = array_map( 'strval', (array) $value );
}
$rows = array_values(
array_filter(
$all->rows,
static function ( $row ) use ( $d, $asked ): bool {
$values = $d->filter_values( $row );
foreach ( $asked as $key => $want ) {
if ( array() === array_intersect( $want, array_map( 'strval', (array) ( $values[ $key ] ?? array() ) ) ) ) {
return false;
}
}
return true;
}
)
);
$per = max( 1, $q->per_page );
$last = max( 1, (int) ceil( count( $rows ) / $per ) );
$q = $q->with(
array(
'page' => min( max( 1, $q->page ), $last ),
'known_total' => count( $rows ),
)
);
return array( $q, new TablePage( array_slice( $rows, ( $q->page - 1 ) * $per, $per ), count( $rows ), false, '', $all->counts ) );
}
public static function page_of( TableDefinition $d, TableQuery $q ): array {
return self::read( $d, $q );
}
private static function read( TableDefinition $d, TableQuery $q ): array {
$page = $d->source()->page( $q );
if ( 'offset' === $d->paging() && array() === $page->rows && null !== $page->total && $page->total > 0 && $q->page > 1 ) {
$last = max( 1, (int) ceil( $page->total / max( 1, $q->per_page ) ) );
if ( $q->page > $last ) {
$q = $q->with(
array(
'page' => $last,
'known_total' => $page->total,
)
);
$page = $d->source()->page( $q );
}
}
return array( null === $page->total ? $q : $q->with( array( 'known_total' => $page->total ) ), $page );
}
public static function live_config( TableDefinition $d, array $attrs = array() ): string {
return (string) wp_json_encode(
array(
'url' => TreeTable::user_locale( esc_url_raw( rest_url( 'rooster-lms/v1/tables/' . $d->id() . '/live' ) ) ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'lang' => Switcher::current(),
'context' => (object) $d->context(),
'attrs' => (object) array_map( 'intval', $attrs ),
)
);
}
public static function number_frames_from( int $n ): void {
self::$count = max( 0, $n - 1 );
}
public static function bare( array $head, string $tbody_html, string $table_class = '', string $caption = '', array $opts = array() ): string {
if ( LibraryTable::bare_eligible() ) {
return LibraryTable::bare( $head, $tbody_html, $table_class, $caption, $opts );
}
Assets::enqueue_styles();
$num = array();
$center = array();
$cells = '';
foreach ( array_values( $head ) as $i => $column ) {
if ( $column instanceof Column ) {
if ( 'end' === $column->align ) {
$num[] = $i + 1;
} elseif ( 'center' === $column->align ) {
$center[] = $i + 1;
}
$cells .= self::plain_head( $column );
} else {
$cells .= '| ' . esc_html( (string) $column ) . ' | ';
}
}
$attrs = array( 'class' => trim( 'rooster-lms-table ' . $table_class ) );
if ( array() !== $num ) {
$attrs['data-rl-num'] = implode( ' ', $num );
}
if ( array() !== $center ) {
$attrs['data-rl-center'] = implode( ' ', $center );
}
$html = '';
return $html;
}
public static function display_bare( array $head, string $tbody_html, string $table_class = '', string $caption = '', array $opts = array() ): void {
echo wp_kses( self::bare( $head, $tbody_html, $table_class, $caption, $opts ), Kses::current() );
}
public static function check_cell( int $id, string $label ): string {
return ' | ';
}
public static function live_part( string $key, string $classes, string $inner, array $attrs = array() ): string {
$attrs = array(
'class' => $classes,
'data-rl-live' => $key,
) + $attrs + array( 'data-rl-stamp' => self::stamp() );
return '' . $inner . '
';
}
public static function search_button( string $label ): string {
return '';
}
public static function clear_button( string $url, bool $active, string $scope = 'all' ): string {
return '' . esc_html__( 'Clear filters', 'rooster-lms' ) . '';
}
public static function nojs_submit( string $label, string $form = '' ): string {
return '';
}
private static function live_attrs( TableDefinition $d, string $part, string $classes ): string {
$attrs = array(
'class' => $classes,
'data-rl-live' => $d->id() . ':' . $part,
);
if ( 'router' === $d->mode() ) {
$attrs['data-wp-interactive'] = $d->interactive();
$attrs['data-wp-router-region'] = 'rooster-lms-table-' . sanitize_html_class( str_replace( '.', '-', $d->id() ) ) . ( 'rows' === $part ? '' : '-' . $part );
}
$attrs['data-rl-stamp'] = self::stamp();
return self::attrs( $attrs );
}
private static function stamp(): string {
static $stamp = '';
if ( '' === $stamp ) {
$stamp = bin2hex( random_bytes( 5 ) );
}
return $stamp;
}
private static function columns( TableDefinition $d ): array {
return array_slice( array_values( array_filter( $d->columns(), static fn( $c ): bool => $c instanceof Column ) ), 0, self::MAX_COLUMNS );
}
private static function state( TableDefinition $d, TableQuery $q, TablePage $page, bool $whole = false ): array {
$offset = array() !== $d->bulk() ? 1 : 0;
$columns = array();
foreach ( self::columns( $d ) as $i => $column ) {
$columns[] = array(
'key' => $column->key,
'label' => $column->label,
'index' => $i + 1 + $offset,
'hideable' => $column->hideable,
'visible' => $column->visible,
);
}
list( $default_by, $default_order ) = $d->default_order();
return array(
'id' => $d->id(),
'args' => TableQuery::arg_names( $d ),
'base' => TableQuery::strip( $d, $d->base_url() ),
'page' => $q->page,
'per_page' => $q->per_page,
'orderby' => $q->orderby,
'order' => $q->order,
'filters' => (object) $q->filters,
'after' => $q->after,
'next' => $page->after,
'total' => $page->total,
'hash' => $q->hash(),
'context' => (object) $q->context,
'defaults' => array(
'orderby' => (string) $default_by,
'order' => 'desc' === $default_order ? 'desc' : 'asc',
'per_page' => TableQuery::from_state( $d, array(), $q->context )->per_page,
),
'columns' => $columns,
'search' => '' !== $d->search_label() ? $d->search_transport() : '',
'lang' => self::language( $d ),
'client' => self::client( $d, $q, $page ),
'local' => $whole ? (object) array_map( static fn( $v ): array => array_values( array_map( 'strval', (array) $v ) ), $d->local_filters() ) : false,
'sorts' => $whole ? (object) $d->local_sorts() : (object) array(),
'listed' => array(
'orderby' => $q->orderby,
'order' => $q->order,
),
);
}
private static function searchable( TableDefinition $d, TablePage $page ): bool {
$rows = $page->rows;
$first = reset( $rows );
return false !== $first && '' !== $d->search_text( $first );
}
private static function client( TableDefinition $d, TableQuery $q, TablePage $page ): bool {
return 'offset' === $d->paging() && 1 === $q->page && '' === $q->search && null !== $page->total && $page->total <= $q->per_page && self::searchable( $d, $page );
}
private static function language( TableDefinition $d ): string {
$provider = Providers::current();
if ( 'none' === $provider->id() ) {
return '';
}
$lang = '' !== $d->language() ? Language::normalize( $d->language() ) : Names::display_language();
foreach ( $provider->languages() as $code ) {
if ( Language::normalize( (string) $code ) === $lang ) {
return $lang;
}
}
return '';
}
private static function strings( TableDefinition $d ): array {
return array(
'error' => __( 'The table could not be loaded. Try again.', 'rooster-lms' ),
'loading' => __( 'Loading…', 'rooster-lms' ),
/* translators: %s: number of selected rows. */
'selected' => __( '%s selected', 'rooster-lms' ),
'confirm' => __( 'Confirm', 'rooster-lms' ),
'cancel' => __( 'Cancel', 'rooster-lms' ),
'exporting' => __( 'Preparing the file…', 'rooster-lms' ),
'cleared' => __( 'Selection cleared', 'rooster-lms' ),
/* translators: 1: first row number, 2: last row number, 3: number of rows. */
'showing' => __( 'Showing %1$s–%2$s of %3$s', 'rooster-lms' ),
'none' => __( 'No results', 'rooster-lms' ),
'empty' => $d->empty_text( true ),
'pages' => __( 'Pages', 'rooster-lms' ),
'prev' => __( 'Previous page', 'rooster-lms' ),
'next' => __( 'Next page', 'rooster-lms' ),
/* translators: %s: page number. */
'page' => __( 'Page %s', 'rooster-lms' ),
'asc' => __( ', sorted ascending', 'rooster-lms' ),
'desc' => __( ', sorted descending', 'rooster-lms' ),
'chevron' => Pager::chevron(),
);
}
private static function toolbar( TableDefinition $d, TableQuery $q, string $uid, array $columns ): string {
$end = self::forms( $d, $q, $uid ) . self::tools( $d, $columns, $uid );
if ( Ui::kit_markup() ) {
$end = '' !== $end ? '' . $end . '
' : '';
$all = self::views_nav( $d ) . $end;
return '' !== $all ? '' . $all . '
' : '';
}
$end = '' !== $end ? '' . $end . '
' : '';
$all = self::views_nav( $d ) . $end;
return '' !== $all ? '' . $all . '
' : '';
}
private static function views_nav( TableDefinition $d ): string {
$views = $d->table_views();
return null === $views ? '' : $views->html();
}
private static function view_filter( TableDefinition $d ): string {
$views = $d->table_views();
if ( null === $views ) {
return '';
}
$key = array_search( $views->arg, TableQuery::arg_names( $d )['filters'], true );
return is_string( $key ) ? $key : '';
}
private static function forms( TableDefinition $d, TableQuery $q, string $uid ): string {
$names = TableQuery::arg_names( $d );
$view_key = self::view_filter( $d );
$filters = self::toolbar_filters( $d );
$search = '' !== $d->search_label();
$post = $search && 'post' === $d->search_transport();
$visible = self::toolbar_visible( $d );
$has_form = $visible || count( self::page_sizes( $d ) ) > 1;
$html = '';
if ( $post ) {
$html .= '';
}
if ( ! $has_form ) {
return $html;
}
$base = TableQuery::strip( $d, $d->base_url() );
$action = explode( '?', $base, 2 )[0];
$view = '' !== $view_key ? $q->filter( $view_key ) : '';
$view = is_string( $view ) ? $view : '';
$kit = Ui::kit_markup();
$html .= '';
}
private static function toolbar_filters( TableDefinition $d ): array {
$view_key = self::view_filter( $d );
return array_values( array_filter( $d->filters(), static fn( Filter $f ): bool => $f->key !== $view_key ) );
}
private static function toolbar_visible( TableDefinition $d ): bool {
return array() !== self::toolbar_filters( $d ) || ( '' !== $d->search_label() && 'post' !== $d->search_transport() );
}
private static function page_sizes( TableDefinition $d ): array {
return 'offset' === $d->paging() || 'keyset' === $d->paging() ? array_values( array_unique( array_map( 'intval', $d->per_page_options() ) ) ) : array();
}
private static function clear_link( string $url, bool $active ): string {
return ''
. self::icon( 'x' ) . esc_html__( 'Clear filters', 'rooster-lms' ) . '';
}
private static function search_field( TableDefinition $d, TableQuery $q, string $name, string $uid ): string {
$id = $uid . '-search';
if ( Ui::kit_markup() ) {
return ''
. \Rooster\Admin\Ui::input(
$name,
$q->search,
array(
'id' => $id,
'type' => 'search',
'icon' => 'search',
'placeholder' => $d->search_label(),
'maxlength' => (string) TableQuery::SEARCH_MAX,
'autocomplete' => 'off',
'attrs' => array( 'data-rl-search' => $d->search_transport() ),
)
) . '
';
}
return '' . self::icon( 'search' )
. '
';
}
private static function filter_control( Filter $filter, TableQuery $q, string $name, string $uid ): string {
$id = $uid . '-' . sanitize_html_class( $filter->key );
$current = $q->filter( $filter->key );
if ( Filter::TOGGLE === $filter->type ) {
$value = (string) array_key_first( array_filter( $filter->options, static fn( $label, $value ): bool => '' !== (string) $value, ARRAY_FILTER_USE_BOTH ) );
if ( Ui::kit_markup() ) {
return \Rooster\Admin\Ui::toggle(
$name,
'' !== checked( $current, $value, false ),
array(
'id' => $id,
'value' => $value,
'label' => $filter->label,
'hidden' => false,
'attrs' => array( 'data-rl-filter' => $filter->key ),
)
);
}
$hint = '' !== $filter->hint ? '' . esc_html( $filter->hint ) . '' : '';
if ( '' !== $hint ) {
Assets::enqueue_tip();
}
return '' . $hint;
}
$multi = Filter::MULTI === $filter->type;
$needs = '' !== $filter->depends ? $q->filter( $filter->depends ) : 'x';
$off = '' === $needs || array() === $needs;
if ( Ui::kit_markup() ) {
$options = '';
foreach ( $filter->options as $value => $label ) {
$value = (string) $value;
$on = $multi ? '' !== $value && in_array( $value, (array) $current, true ) : $value === $current;
$options .= '';
}
$select = '';
return \Rooster\Admin\Ui::filter( $filter->label, $select, $id, $off ? array( 'tip' => $filter->hint ) : array() );
}
$html = '';
}
private static function hidden_args( string $base ): string {
$query = (string) wp_parse_url( $base, PHP_URL_QUERY );
if ( '' === $query ) {
return '';
}
$args = array();
wp_parse_str( $query, $args );
$html = '';
foreach ( $args as $name => $value ) {
if ( is_scalar( $value ) ) {
$html .= '';
}
}
return $html;
}
private static function tools( TableDefinition $d, array $columns, string $uid ): string {
$hideable = array();
$off = array() !== $d->bulk() ? 1 : 0;
foreach ( $columns as $i => $column ) {
if ( $column->hideable && '' !== $column->label && ! $column->sr_only ) {
$hideable[] = array(
'key' => $column->key,
'label' => $column->label,
'index' => $i + 1 + $off,
'visible' => $column->visible,
);
}
}
$group = self::columns_tool( $uid, $hideable, count( array_filter( $columns, static fn( $c ): bool => '' !== $c->label && ! $c->sr_only ) ) - count( $hideable ) );
$label = __( 'Compact rows', 'rooster-lms' );
$kit = Ui::kit_markup();
if ( $kit ) {
$group .= \Rooster\Admin\Ui::button(
$label,
array(
'icon' => 'rows-3',
'icon_only' => true,
'attrs' => array(
'aria-pressed' => 'false',
'data-rl-density' => '1',
'data-rl-js' => '1',
),
)
);
$items = '' . $group . '
';
} else {
$group .= '';
$items = '' . $group . '
';
}
foreach ( $d->exports() as $export ) {
if ( ! $export instanceof ExportLink ) {
continue;
}
if ( $kit ) {
$items .= ExportLink::URL === $export->kind
? \Rooster\Admin\Ui::button(
$export->label,
array(
'icon' => 'download',
'href' => $export->target,
'class' => 'rooster-lms-table__export',
'attrs' => array( 'data-rl-export-url' => '1' ) + ( $export->shown ? array( 'data-rl-export-columns' => 'columns' ) : array() ),
)
)
: \Rooster\Admin\Ui::button(
$export->label,
array(
'icon' => 'download',
'class' => 'rooster-lms-table__export',
'attrs' => array(
'data-rl-js' => '1',
'data-rl-export' => $export->target,
'data-rl-format' => $export->format,
'data-rl-map' => (string) wp_json_encode( (object) $export->map ),
),
)
);
continue;
}
if ( ExportLink::URL === $export->kind ) {
$items .= 'shown ? ' data-rl-export-columns="columns"' : '' ) . '>' . self::icon( 'download' ) . esc_html( $export->label ) . '';
} else {
$items .= '';
}
}
return '' . $items . '
';
}
public static function columns_tool( string $uid, array $columns, int $fixed = 0 ): string {
$menu = '';
foreach ( $columns as $column ) {
$menu .= '';
}
if ( '' === $menu ) {
return '';
}
$label = __( 'Columns', 'rooster-lms' );
if ( Ui::kit_markup() ) {
$shown = $fixed + count( array_filter( $columns, static fn( array $c ): bool => ! empty( $c['visible'] ) ) );
/* translators: 1: the columns shown, 2: all the columns. */
$words = __( 'Columns %1$s of %2$s', 'rooster-lms' );
return '' . \Rooster\Admin\Ui::button(
sprintf( $words, number_format_i18n( $shown ), number_format_i18n( $fixed + count( $columns ) ) ),
array(
'icon' => 'columns-3',
'size' => 'sm',
'attrs' => array(
'aria-expanded' => 'false',
'aria-controls' => $uid . '-columns',
'data-rl-columns' => '1',
'data-rl-columns-fixed' => (string) $fixed,
'data-rl-columns-words' => $words,
),
)
) . '
';
}
return ''
. '
';
}
private static function icon( string $name ): string {
$paths = array(
'search' => '',
'columns' => '',
'rows' => '',
'download' => '',
'x' => '',
);
return '';
}
private static function footer( TableDefinition $d, TableQuery $q, TablePage $page, string $uid ): string {
if ( Ui::kit_markup() ) {
$html = '' . esc_html( self::summary( $q, $page ) ) . '
';
if ( 'offset' === $d->paging() || 'keyset' === $d->paging() ) {
$html .= '' . self::pager( $d, $q, $page ) . '
';
}
$sizes = self::page_sizes( $d );
if ( count( $sizes ) > 1 ) {
$id = $uid . '-per-page';
$options = '';
foreach ( $sizes as $size ) {
$options .= '';
}
$html .= ''
. '
';
if ( ! self::toolbar_visible( $d ) ) {
$html .= self::nojs_submit( __( 'Apply', 'rooster-lms' ), $uid . '-filters' );
}
}
return '';
}
$html = '' . esc_html( self::summary( $q, $page ) ) . '
';
if ( 'offset' === $d->paging() || 'keyset' === $d->paging() ) {
$html .= '' . self::pager( $d, $q, $page ) . '
';
}
$sizes = self::page_sizes( $d );
if ( count( $sizes ) > 1 ) {
$id = $uid . '-per-page';
$html .= '';
if ( ! self::toolbar_visible( $d ) ) {
$html .= self::nojs_submit( __( 'Apply', 'rooster-lms' ), $uid . '-filters' );
}
}
return '';
}
private static function chips( TableDefinition $d, TableQuery $q, TablePage $page ): string {
$chips = $d->chips();
if ( array() === $chips ) {
return '';
}
$html = '';
foreach ( $chips as $chip ) {
$key = (string) ( $chip['key'] ?? '' );
$value = (string) ( $chip['value'] ?? '' );
$on = $q->filter( $key ) === $value;
$url = $q->url(
$d,
array(
'filters' => array( $key => $on ? null : $value ),
'page' => 1,
'after' => '',
)
);
$count = $page->counts[ $key ][ $value ] ?? null;
$html .= '' . esc_html( (string) ( $chip['label'] ?? $value ) );
if ( null !== $count ) {
$html .= ' ' . esc_html( Format::number( (int) $count ) ) . '';
}
$html .= '';
}
return '' . $html . '
';
}
public static function summary( TableQuery $q, TablePage $page ): string {
$n = count( $page->rows );
if ( 0 === $n ) {
return 0 === (int) $page->total || $q->page <= 1 ? __( 'No results', 'rooster-lms' ) : '';
}
$from = $q->offset() + 1;
$to = $q->offset() + $n;
if ( null === $page->total ) {
/* translators: 1: first row number, 2: last row number. */
return sprintf( __( 'Showing %1$s–%2$s', 'rooster-lms' ), Format::number( $from ), Format::number( $to ) );
}
/* translators: 1: first row number, 2: last row number, 3: number of rows. */
return sprintf( __( 'Showing %1$s–%2$s of %3$s', 'rooster-lms' ), Format::number( $from ), Format::number( $to ), Format::number( $page->total ) );
}
private static function pages( TableQuery $q, TablePage $page ): int {
return null === $page->total ? max( 1, $q->page + ( $page->more ? 1 : 0 ) ) : max( 1, (int) ceil( $page->total / max( 1, $q->per_page ) ) );
}
private static function table_attrs( TableDefinition $d, array $columns ): string {
$off = array() !== $d->bulk() ? 1 : 0;
$num = array();
$center = array();
$hide = array();
foreach ( $columns as $i => $column ) {
$n = $i + 1 + $off;
if ( 'end' === $column->align ) {
$num[] = $n;
} elseif ( 'center' === $column->align ) {
$center[] = $n;
}
if ( ! $column->visible ) {
$hide[] = $n;
}
}
$attrs = array( 'class' => trim( 'rooster-lms-table ' . $d->table_class() ) );
if ( array() !== $num ) {
$attrs['data-rl-num'] = implode( ' ', $num );
}
if ( array() !== $center ) {
$attrs['data-rl-center'] = implode( ' ', $center );
}
$attrs['data-rl-hide'] = implode( ' ', $hide );
return self::attrs( $attrs );
}
private static function head( TableDefinition $d, TableQuery $q, array $columns ): string {
$html = '
';
if ( array() !== $d->bulk() ) {
$html .= '| ' . self::bulk_bar( $d->bulk() ) . ' | ';
}
foreach ( $columns as $column ) {
if ( ! $column->sortable ) {
$html .= self::plain_head( $column );
continue;
}
$current = $q->orderby === $column->key;
$sort = $current ? ( 'asc' === $q->order ? 'ascending' : 'descending' ) : 'none';
$next = $current && 'asc' === $q->order ? 'desc' : 'asc';
$url = $q->url(
$d,
array(
'orderby' => $column->key,
'order' => $next,
'page' => 1,
'after' => '',
)
);
$sr = '';
if ( $current ) {
$sr = '' . esc_html( 'asc' === $q->order ? __( ', sorted ascending', 'rooster-lms' ) : __( ', sorted descending', 'rooster-lms' ) ) . '';
}
$label = $column->sr_only ? '' . esc_html( $column->label ) . '' : esc_html( $column->label );
if ( Ui::kit_markup() ) {
$html .= '' . $label . $sr . self::sort_chevron() . ' | ';
continue;
}
$html .= '' . $label . $sr . ' | ';
}
return $html . '
';
}
private static function plain_head( Column $column ): string {
$label = $column->sr_only ? '' . esc_html( $column->label ) . '' : esc_html( $column->label );
if ( '' !== $column->html ) {
$label = '' . $column->html . '' . esc_html( $column->label ) . '';
}
return 'data_column ? ' data-column="' . esc_attr( $column->key ) . '"' : '' ) . '>' . $label . ' | ';
}
private static function th_class(): string {
return Ui::kit_markup() ? ' class="rooster-admin-table__th"' : '';
}
public static function sort_chevron(): string {
return '';
}
private static function body( TableDefinition $d, TableQuery $q, TablePage $page, array $columns ): string {
if ( array() === $page->rows ) {
$span = count( $columns ) + ( array() !== $d->bulk() ? 1 : 0 );
return '| ' . esc_html( $d->empty_text( $q->filtered() ) ) . ' |
';
}
$html = '';
foreach ( $page->rows as $row ) {
$html .= $d->row( $row );
}
return $html;
}
private static function pager( TableDefinition $d, TableQuery $q, TablePage $page ): string {
$labels = $d->labels();
if ( 'keyset' === $d->paging() ) {
$first = '' !== $q->after ? $q->url(
$d,
array(
'after' => '',
'page' => 1,
)
) : '';
$more = $page->more && '' !== $page->after ? $q->url(
$d,
array(
'after' => $page->after,
'page' => $q->page + 1,
)
) : '';
return Pager::keyset( $first, $more, $labels['first'] ?? __( 'First page', 'rooster-lms' ), $labels['more'] ?? __( 'Next page', 'rooster-lms' ) );
}
if ( 'offset' !== $d->paging() ) {
return '';
}
if ( null === $page->total ) {
return Pager::steps(
$q->page > 1 ? $q->url( $d, array( 'page' => $q->page - 1 ) ) : '',
$page->more ? $q->url( $d, array( 'page' => $q->page + 1 ) ) : ''
);
}
return Pager::offset( $q->page, self::pages( $q, $page ), static fn( int $n ): string => $q->url( $d, array( 'page' => $n ) ) );
}
private static function bulk_bar( array $actions ): string {
/* translators: %s: number of selected rows. */
$count = sprintf( __( '%s selected', 'rooster-lms' ), Format::number( 0 ) );
$html = '' . esc_html( $count ) . '';
$kit = Ui::kit_markup();
foreach ( $actions as $action ) {
if ( ! $action instanceof BulkAction ) {
continue;
}
if ( $kit ) {
$html .= self::kit_button(
$action->label,
$action->variant,
array(
'name' => 'rl_bulk',
'value' => $action->key,
'data-rl-confirm' => '' !== $action->confirm ? $action->confirm : false,
),
'submit'
);
continue;
}
$html .= '';
}
if ( $kit ) {
return $html . self::kit_button(
__( 'Clear selection', 'rooster-lms' ),
'ghost',
array(
'data-rl-bulk-clear' => '1',
'data-rl-js' => '1',
)
) . '
';
}
return $html . '';
}
public static function kit_button( string $label, string $variant, array $attrs = array(), string $type = 'button' ): string {
$variant = array(
'secondary' => 'outline',
'dim' => 'ghost',
)[ $variant ] ?? $variant;
$class = 'rooster-admin-button' . ( in_array( $variant, array( 'primary', 'ghost', 'destructive', 'mono' ), true ) ? ' rooster-admin-button--' . $variant : '' ) . ' rooster-admin-button--sm';
return '';
}
private static function attrs( array $attrs ): string {
$out = '';
foreach ( $attrs as $name => $value ) {
if ( false === $value ) {
continue;
}
$out .= true === $value ? ' ' . esc_attr( $name ) : ' ' . esc_attr( $name ) . '="' . esc_attr( (string) $value ) . '"';
}
return $out;
}
}