mode(), array( 'rest', 'local', 'none' ), true ) && in_array( $d->paging(), array( 'offset', 'none' ), true ) && 'post' !== $d->search_transport(); return $fits && (bool) apply_filters( 'rooster_lms_library_table', true, $d ); } public static function bare_eligible(): bool { if ( ! Ui::chrome() && ! ( is_admin() && ! wp_doing_ajax() && function_exists( 'rooster_admin_builders' ) && rooster_admin_builders() ) ) { return false; } return method_exists( '\Rooster\Admin\Ui', 'table' ) && (bool) apply_filters( 'rooster_lms_library_table', true, null ); } public static function bare( array $head, string $tbody_html, string $table_class = '', string $caption = '', array $opts = array() ): string { $columns = self::bare_columns( $head ); $rows = array(); foreach ( self::split_rows( $tbody_html ) as $parsed ) { $rows[] = self::row( $parsed, $columns, false ); } $html = self::head_html( \Rooster\Admin\Ui::table( $columns, $rows, self::bare_opts( $opts ) + array( 'caption' => '' !== $caption ? $caption : __( 'Table', 'rooster-lms' ), 'empty' => esc_html__( 'Nothing to show yet.', 'rooster-lms' ), ) ), $columns ); $class = trim( $table_class ); return '
' . $html . '
'; } private static function bare_opts( array $opts ): array { $out = array(); foreach ( array( 'cards', 'sticky_first', 'dense', 'striped' ) as $flag ) { if ( ! empty( $opts[ $flag ] ) ) { $out[ $flag ] = true; } } if ( '' !== (string) ( $opts['id'] ?? '' ) ) { $out['id'] = (string) $opts['id']; } return $out; } public static function editable( array $head, string $rows_html, string $template, string $caption, string $table_class = '', array $opts = array() ): string { if ( ! self::bare_eligible() ) { $heads = ''; foreach ( $head as $column ) { $label = esc_html( $column instanceof Column ? $column->label : (string) $column ); $heads .= '' . ( $column instanceof Column && $column->sr_only ? '' . $label . '' : $label ) . ''; } return '' . $heads . '' . $rows_html . '
' . esc_html( $caption ) . '
' . ''; } $html = self::bare( $head, $rows_html, $table_class, $caption, $opts ); $html = (string) preg_replace( '#]*>.*?#s', '', $html, 1 ); $html = (string) preg_replace( '#]*)>#', '', $html, 1 ); foreach ( $head as $column ) { if ( $column instanceof Column && $column->sr_only ) { $label = esc_html( $column->label ); $html = str_replace( 'scope="col">' . $label . '', 'scope="col">' . $label . '', $html ); } } $columns = self::bare_columns( $head ); $rows = array(); foreach ( self::split_rows( $template ) as $parsed ) { $rows[] = self::row( $parsed, $columns, false ); } return $html . ''; } private static function bare_columns( array $head ): array { $columns = array(); foreach ( array_values( $head ) as $i => $column ) { $col = $column instanceof Column ? array( 'key' => '' !== $column->key ? $column->key : 'c' . $i, 'label' => $column->label, 'type' => 'html', 'align' => in_array( $column->align, array( 'start', 'center', 'end' ), true ) ? $column->align : 'start', ) : array( 'key' => 'c' . $i, 'label' => (string) $column, 'type' => 'html', ); if ( $column instanceof Column && '' !== $column->html ) { $col['head_html'] = $column->html; } if ( $column instanceof Column && '' !== $column->width ) { $col['width'] = $column->width; } if ( $column instanceof Column ) { $col += self::phone( $column ); } $columns[] = $col; } return $columns; } private static function phone( Column $column ): array { $out = array(); if ( 'low' === $column->priority ) { $out['priority'] = 'low'; } if ( '' !== $column->group ) { $out['group'] = $column->group; } return $out; } public static function frame( TableDefinition $d, TableQuery $q ): string { if ( ! $d->can_view( get_current_user_id(), $d->context() ) ) { return ''; } list( $q, $page ) = DataTable::page_of( $d, $q ); if ( is_admin() ) { Assets::enqueue_row_actions(); } if ( method_exists( '\Rooster\Admin\States', 'enqueue' ) && wp_script_is( 'rooster-admin-chrome', 'registered' ) ) { \Rooster\Admin\States::enqueue(); } $id = self::dom_id( $d ); $columns = self::columns( $d, $q ); $bulk = array_values( array_filter( $d->bulk(), static fn( $b ): bool => $b instanceof BulkAction ) ); $names = TableQuery::arg_names( $d ); $view = self::view_key( $d ); $filters = self::filters( $d, $view ); $views = $d->table_views(); $opts = array( 'id' => $id, 'caption' => '' !== $d->caption() ? $d->caption() : __( 'Table', 'rooster-lms' ), 'empty' => esc_html( $d->empty_text( $q->filtered() ) ), 'selectable' => array() !== $bulk, ); if ( array() !== $filters ) { $opts['filters'] = $filters; $opts['filter_values'] = self::filter_values( $d, $q, $filters ); } $opts['typed'] = $q->search; $opts += self::kind_opts( $d ); if ( '' !== $d->search_label() ) { $opts['search'] = array( 'label' => $d->search_label(), 'placeholder' => $d->search_label(), ); if ( array() === $filters ) { $opts['filters'] = array(); } } $toolbar = self::toolbar( $d, $q, $page ); if ( '' !== $toolbar ) { $opts['toolbar'] = $toolbar; } $export = self::export( $d, $id ); if ( array() !== $export ) { $opts['export'] = $export; } $chooser = self::chooser( $columns ); if ( null !== $chooser ) { $opts['chooser'] = $chooser; } if ( $d->text_size() ?? count( $columns ) >= 7 ) { $opts['text_size'] = true; } $dialogs = ''; if ( array() !== $bulk ) { list( $opts['bulk'], $dialogs ) = self::bulk( $bulk, $id ); } list( $by, $dir ) = '' !== $q->orderby ? array( $q->orderby, $q->order ) : $d->default_order(); $sizes = 'offset' === $d->paging() ? array_values( array_unique( array_map( 'intval', $d->per_page_options() ) ) ) : array(); $whole = self::whole( $d, $q, $page, $view, $columns ); if ( null !== $whole ) { $sorts = $d->local_sorts(); $default = $d->default_order(); foreach ( $columns as $i => $col ) { if ( isset( $col['sort'], $sorts[ (string) $col['key'] ]['type'] ) ) { $columns[ $i ]['sort_type'] = (string) $sorts[ (string) $col['key'] ]['type']; } } $opts['local'] = array( 'size' => $q->per_page, 'asked' => $q->per_page, 'sizes' => count( $sizes ) > 1 ? $sizes : array(), 'paged' => $q->page, 'keys' => array( 's' => $names['search'], 'orderby' => $names['orderby'], 'order' => $names['order'], 'paged' => $names['page'], 'per_page' => $names['per_page'], ), 'prefix' => $d->arg_prefix(), 'tie' => 'asc' === ( $sorts[ (string) $by ]['tie'] ?? '' ) ? 'asc' : 'dir', ); if ( '' !== (string) $default[0] ) { $opts['local']['sort'] = array( 'key' => (string) $default[0], 'dir' => 'desc' === $default[1] ? 'desc' : 'asc', ); } $rows = self::rows( $d, $page->rows, $columns, array() !== $bulk, true ); $opts['rows_template'] = self::rows( $d, $whole->rows, $columns, array() !== $bulk, true ); } elseif ( 'offset' === $d->paging() && null !== TableRegistry::get( $d->id(), $d->context() ) ) { $opts['remote'] = array( 'url' => esc_url_raw( rest_url( 'rooster-lms/v1/tables/' . $d->id() . '/rows' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'scope' => self::scope( $d, $q, $view, $id ), 'context' => 'admin', 'size' => $q->per_page, 'asked' => $q->per_page, 'sizes' => count( $sizes ) > 1 ? $sizes : array(), 'total' => null !== $page->total ? (int) $page->total : count( $page->rows ), 'paged' => $q->page, 'sort' => '' !== (string) $by ? array( 'key' => (string) $by, 'dir' => 'desc' === $dir ? 'desc' : 'asc', ) : null, 'keys' => array( 's' => $names['search'], 'orderby' => $names['orderby'], 'order' => $names['order'], 'paged' => $names['page'], 'per_page' => $names['per_page'], ), 'prefix' => $d->arg_prefix(), ); } elseif ( 'offset' === $d->paging() ) { $opts['footer'] = Pager::offset( $q->page, max( 1, (int) ceil( (int) $page->total / max( 1, $q->per_page ) ) ), static fn( int $n ): string => $q->url( $d, array( 'page' => $n ) ) ); } if ( null === $whole ) { $rows = self::rows( $d, $page->rows, $columns, array() !== $bulk ); } $html = self::head_html( \Rooster\Admin\Ui::table( $columns, $rows, $opts ), $columns ); if ( array() !== $bulk ) { $html .= '
'; foreach ( $d->bulk_fields() as $name => $value ) { $html .= ''; } $html = self::rl_ids( $html, $id . '-bulk' ) . '
'; } if ( array() !== $bulk || array() !== array_filter( $d->exports(), static fn( $e ): bool => $e instanceof ExportLink && ExportLink::REST_TOKEN === $e->kind ) ) { Assets::enqueue_table_library(); } $lifted = self::$lifted; self::$lifted = ''; return '
' . ( null !== $views ? $views->html() : '' ) . $html . '
' . $dialogs . $lifted; } private static function kind_opts( TableDefinition $d ): array { switch ( $d->kind() ) { case 'list': return array( 'cards' => true ); case 'grid': return array( 'sticky_first' => true ); default: return array(); } } public static function answer( TableDefinition $d, array $params ): array { $names = TableQuery::arg_names( $d ); $get = array( $names['page'] => $params['page'] ?? 1, $names['per_page'] => $params['per_page'] ?? 0, $names['orderby'] => $params['orderby'] ?? '', $names['order'] => $params['order'] ?? '', $names['search'] => $params['search'] ?? '', ); $given = array_merge( is_array( $params['fixed'] ?? null ) ? $params['fixed'] : array(), is_array( $params['params'] ?? null ) ? $params['params'] : array() ); foreach ( $d->filters() as $filter ) { if ( ! array_key_exists( $filter->key, $given ) ) { continue; } $value = $given[ $filter->key ]; if ( Filter::MULTI !== $filter->type && is_array( $value ) ) { $value = (string) ( array_values( $value )[0] ?? '' ); } $get[ $names['filters'][ $filter->key ] ] = $value; } list( $q, $page ) = DataTable::page_of( $d, TableQuery::from_args( $d, $get ) ); $columns = self::columns( $d, $q ); $total = null !== $page->total ? (int) $page->total : ( $q->offset() + count( $page->rows ) + ( $page->more ? 1 : 0 ) ); $answer = \Rooster\Admin\Ui::answer( $columns, self::rows( $d, $page->rows, $columns, array() !== $d->bulk() ), array( 'total' => $total, 'filtered' => $total, 'page' => $q->page, 'per_page' => $q->per_page, 'pages' => max( 1, (int) ceil( $total / max( 1, $q->per_page ) ) ), 'orderby' => $q->orderby, 'order' => $q->order, ), array( 'selectable' => array() !== $d->bulk(), 'cards' => 'list' === $d->kind(), ) ); $form = (string) ( $params['form'] ?? '' ); $answer['rows'] = self::rl_ids( (string) ( $answer['rows'] ?? '' ), 1 === preg_match( '/^[A-Za-z0-9_-]{1,80}$/', $form ) ? $form : '' ); return $answer + array( 'empty' => $d->empty_text( $q->filtered() ) ); } private static function rl_ids( string $html, string $form ): string { return str_replace( 'columns(), static fn( $c ): bool => $c instanceof Column ) ), 0, DataTable::MAX_COLUMNS ) as $column ) { $col = array( 'key' => $column->key, 'label' => $column->label, 'type' => 'html', 'align' => in_array( $column->align, array( 'start', 'center', 'end' ), true ) ? $column->align : 'start', ); if ( ! $column->hideable || '' === $column->label ) { $col['always'] = true; } if ( '' !== $column->html ) { $col['head_html'] = $column->html; } if ( '' !== $column->width ) { $col['width'] = $column->width; } $col += self::phone( $column ); $col['shown'] = $column->visible; if ( $column->sortable ) { $current = $q->orderby === $column->key; $col['sort'] = $current ? ( 'desc' === $q->order ? 'desc' : 'asc' ) : ''; $col['sort_url'] = $q->url( $d, array( 'orderby' => $column->key, 'order' => $current && 'asc' === $q->order ? 'desc' : 'asc', 'page' => 1, ) ); } $out[] = $col; } return $out; } private static function head_html( string $html, array $columns ): string { foreach ( $columns as $col ) { if ( '' === (string) ( $col['head_html'] ?? '' ) || isset( $col['sort'] ) ) { continue; } $label = esc_html( (string) $col['label'] ); $at = strpos( $html, 'scope="col">' . $label . '' ); if ( false !== $at ) { $html = substr_replace( $html, 'scope="col">' . $label . '', $at, strlen( 'scope="col">' . $label . '' ) ); } } return $html; } private static function chooser( array $columns ): ?array { $hideable = array_filter( array_slice( $columns, 1 ), static fn( array $c ): bool => empty( $c['always'] ) ); if ( array() === $hideable ) { return null; } return array( 'shown' => array_values( array_map( static fn( array $c ): string => (string) $c['key'], array_filter( $columns, static fn( array $c ): bool => ! empty( $c['shown'] ) ) ) ) ); } private static function rows( TableDefinition $d, array $source, array $columns, bool $bulk, bool $local = false ): array { $out = array(); foreach ( $source as $item ) { $tr = $d->row( $item ); if ( '' === trim( $tr ) ) { continue; } $values = $local ? self::local_values( $d, $item ) : array(); foreach ( self::split_rows( $tr ) as $parsed ) { $row = self::row( $parsed, $columns, $bulk ); if ( array() !== $values ) { $row['_attrs'] = $values + ( $row['_attrs'] ?? array() ); } if ( $local ) { $row = self::lift_dialogs( $row ); } $out[] = $row; } } return $out; } private static function lift_dialogs( array $row ): array { $lift = static function ( string $html ): string { return (string) preg_replace_callback( '#]*>.*?#s', static function ( array $m ): string { self::$lifted .= $m[0]; return ''; }, $html ); }; foreach ( $row as $key => $cell ) { if ( '_span' === $key && is_string( $cell ) ) { $row[ $key ] = $lift( $cell ); } elseif ( '_' !== substr( (string) $key, 0, 1 ) && is_array( $cell ) && isset( $cell['html'] ) ) { $row[ $key ]['html'] = $lift( (string) $cell['html'] ); } } return $row; } private static function local_values( TableDefinition $d, mixed $item ): array { $out = array(); $values = array(); foreach ( $d->filter_values( $item ) as $key => $list ) { $values[ (string) $key ] = array_values( array_map( 'strval', (array) $list ) ); } if ( array() !== $values ) { $out['data-ra-f'] = $values; } if ( array() !== $d->local_sorts() ) { $out['data-ra-s'] = $d->sort_values( $item ); } $text = $d->search_text( $item ); if ( '' !== $text ) { $out['data-ra-text'] = $text; } return $out; } private static function whole( TableDefinition $d, TableQuery $q, TablePage $page, string $view, array $columns ): ?TablePage { if ( 'offset' !== $d->paging() || $d->local_cap() <= 0 ) { return null; } $local = $d->local_filters(); foreach ( $d->filters() as $filter ) { if ( $filter instanceof Filter && $filter->key !== $view && ( ! isset( $local[ $filter->key ] ) || array() !== (array) $local[ $filter->key ] ) ) { return null; } } $sorts = $d->local_sorts(); foreach ( $columns as $col ) { if ( isset( $col['sort'] ) && ! isset( $sorts[ (string) $col['key'] ] ) ) { return null; } } $clear = array(); foreach ( array_keys( $local ) as $key ) { $set = $q->filter( (string) $key ); if ( (string) $key !== $view && '' !== $set && array() !== $set ) { $clear[ (string) $key ] = null; } } $cap = $d->local_cap(); if ( 1 === $q->page && '' === $q->search && array() === $clear && null !== $page->total && count( $page->rows ) >= $page->total ) { $read = $page; } else { $read = $d->source()->page( $q->with( array( 'page' => 1, 'per_page' => $cap, 'known_total' => null, 'search' => '', 'filters' => $clear, ) ) ); } if ( null === $read->total || $read->total > $cap || count( $read->rows ) < $read->total ) { return null; } if ( '' !== $d->search_label() && array() !== $read->rows && '' === $d->search_text( $read->rows[ array_key_first( $read->rows ) ] ) ) { return null; } return $read; } private static function row( array $parsed, array $columns, bool $bulk ): array { $row = array(); $attrs = array(); foreach ( $parsed['attrs'] as $name => $value ) { if ( str_starts_with( $name, 'data-' ) && 'data-ra-row' !== $name ) { $attrs[ $name ] = $value; } } $class = ' ' . ( $parsed['attrs']['class'] ?? '' ) . ' '; if ( ' ' !== $class ) { $attrs['data-rl-class'] = trim( $class ); } $cells = $parsed['cells']; if ( $bulk && isset( $cells[0] ) && 1 === preg_match( '/]*\bname="rl_ids\[\]"[^>]*\bvalue="(\d+)"/', $cells[0]['html'], $m ) ) { $row['_id'] = $m[1]; array_shift( $cells ); } elseif ( $bulk && isset( $cells[0] ) && str_contains( $cells[0]['attrs']['class'] ?? '', 'rooster-lms-table__check' ) ) { array_shift( $cells ); } if ( 1 === count( $cells ) && (int) ( $cells[0]['attrs']['colspan'] ?? 1 ) > 1 ) { $row['_span'] = $cells[0]['html']; } else { $i = 0; foreach ( $cells as $cell ) { if ( ! isset( $columns[ $i ] ) ) { break; } $span = max( 1, (int) ( $cell['attrs']['colspan'] ?? 1 ) ); $row[ (string) $columns[ $i ]['key'] ] = $span > 1 ? array( 'html' => self::cell_html( $cell ), 'colspan' => $span, ) : array( 'html' => self::cell_html( $cell ) ); $i += $span; } } if ( array() !== $attrs ) { $row['_attrs'] = $attrs; } return $row; } private static function cell_html( array $cell ): string { $own = array_diff( explode( ' ', (string) preg_replace( '/\s+/', ' ', trim( $cell['attrs']['class'] ?? '' ) ) ), array( '', 'num', 'is-num', 'rooster-lms-table__check' ) ); return array() === $own ? $cell['html'] : '
' . $cell['html'] . '
'; } public static function split_rows( string $html ): array { $out = array(); if ( false === preg_match_all( '#<(/?)(tr|td|th|table)\b((?:[^>"\']|"[^"]*"|\'[^\']*\')*)>#i', $html, $tags, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ) ) { return $out; } $depth = 0; $row = null; $cell = null; foreach ( $tags as $tag ) { $close = '/' === $tag[1][0]; $name = strtolower( $tag[2][0] ); $at = (int) $tag[0][1]; $end = $at + strlen( $tag[0][0] ); if ( 'table' === $name ) { $depth += $close ? -1 : 1; continue; } if ( $depth > 0 ) { continue; } if ( 'tr' === $name ) { if ( ! $close ) { $row = array( 'attrs' => self::attrs( $tag[3][0] ), 'cells' => array(), ); } elseif ( null !== $row ) { $out[] = $row; $row = null; } continue; } if ( null === $row ) { continue; } if ( ! $close ) { $cell = array( 'tag' => $name, 'attrs' => self::attrs( $tag[3][0] ), 'from' => $end, ); } elseif ( null !== $cell ) { $row['cells'][] = array( 'tag' => $cell['tag'], 'attrs' => $cell['attrs'], 'html' => substr( $html, $cell['from'], $at - $cell['from'] ), ); $cell = null; } } return $out; } private static function attrs( string $source ): array { $out = array(); if ( preg_match_all( '#([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s"\'=<>`]+)))?#', $source, $m, PREG_SET_ORDER ) ) { foreach ( $m as $a ) { $value = $a[2] ?? ''; $value = '' !== $value ? $value : ( $a[3] ?? '' ); $value = '' !== $value ? $value : ( $a[4] ?? '' ); $out[ strtolower( $a[1] ) ] = html_entity_decode( $value, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); } } return $out; } private static function filter_values( TableDefinition $d, TableQuery $q, array $decl ): array { if ( ! class_exists( '\Rooster\Admin\Filters' ) ) { return array(); } $names = TableQuery::arg_names( $d ); $source = array(); foreach ( $q->filters as $key => $value ) { if ( isset( $names['filters'][ $key ] ) && null !== $value && '' !== $value && array() !== $value ) { $source[ $names['filters'][ $key ] ] = $value; } } return \Rooster\Admin\Filters::values( $decl, $d->arg_prefix(), $source ); } private static function filters( TableDefinition $d, string $view ): array { $out = array(); foreach ( $d->filters() as $filter ) { if ( ! $filter instanceof Filter || $filter->key === $view ) { continue; } $options = array(); $any = ''; foreach ( $filter->options as $value => $label ) { if ( '' === (string) $value ) { $any = (string) $label; continue; } $options[ (string) $value ] = (string) $label; } if ( Filter::TOGGLE === $filter->type ) { $decl = array( 'key' => $filter->key, 'label' => $filter->label, 'control' => 'checks', 'options' => array_slice( $options, 0, 1, true ), ); } else { $decl = array( 'key' => $filter->key, 'label' => $filter->label, 'control' => Filter::MULTI === $filter->type ? 'chips' : ( count( $options ) > 15 ? 'combo' : 'select' ), 'options' => $options, ); if ( '' !== $any ) { $decl['any'] = $any; } } $out[] = $decl; } return $out; } private static function toolbar( TableDefinition $d, TableQuery $q, TablePage $page ): string { $html = ''; foreach ( $d->chips() as $chip ) { $key = (string) ( $chip['key'] ?? '' ); $value = (string) ( $chip['value'] ?? '' ); $on = $q->filter( $key ) === $value; $count = $page->counts[ $key ][ $value ] ?? null; $html .= \Rooster\Admin\Ui::button( (string) ( $chip['label'] ?? $value ) . ( null !== $count ? ' (' . \RoosterLMS\I18n\Format::number( (int) $count ) . ')' : '' ), array( 'variant' => $on ? 'primary' : 'outline', 'size' => 'sm', 'href' => $q->url( $d, array( 'filters' => array( $key => $on ? null : $value ), 'page' => 1, ) ), 'attrs' => $on ? array( 'aria-current' => 'true' ) : array(), ) ); } return $html; } private static function export( TableDefinition $d, string $id ): array { $out = array(); foreach ( $d->exports() as $export ) { if ( ! $export instanceof ExportLink || ! in_array( $export->format, array( 'csv', 'xlsx' ), true ) ) { continue; } if ( ExportLink::URL === $export->kind ) { $out[ $export->format ] = $export->target; if ( $export->shown ) { $out['columns'] = 'visible'; } if ( $export->sheets && 'xlsx' === $export->format ) { $out['views'] = 'sheets'; } continue; } $out[ $export->format ] = ''; $out['attrs'] = array( 'data-rl-export' => $export->target, 'data-rl-map' => (string) wp_json_encode( (object) $export->map ), 'data-rl-export-for' => $id, ); } if ( array() !== $out ) { $out['scope'] = isset( $out['columns'] ) ? __( 'Exports every row that matches the filters and the search, with the columns shown', 'rooster-lms' ) : __( 'Exports every row that matches the filters and the search, every column', 'rooster-lms' ); if ( isset( $out['views'] ) ) { $out['sheets'] = __( 'Excel: every view as a sheet', 'rooster-lms' ); } $out['id'] = $id . '-export'; } return $out; } private static function bulk( array $actions, string $id ): array { $bar = ''; $dialogs = ''; foreach ( $actions as $action ) { $attrs = array( 'data-rl-bulk' => $action->key, 'form' => $id . '-bulk', ); if ( '' !== $action->confirm ) { $dialog = $id . '-confirm-' . sanitize_html_class( $action->key ); $danger = 'destructive' === $action->variant; $dialogs .= \Rooster\Admin\States::confirm( $dialog, $danger ? 'delete' : 'apply', $action->confirm, /* translators: %s: the number of rows (a figure the table fills in). */ sprintf( esc_html__( 'Rows selected: %s.', 'rooster-lms' ), '0' ), array( 'confirm' => $action->label ) ); $attrs += self::confirm_attrs( $dialog ); } $bar .= \Rooster\Admin\Ui::button( $action->label, array( 'variant' => array( 'secondary' => 'outline', 'dim' => 'ghost', )[ $action->variant ] ?? $action->variant, 'size' => 'sm', 'attrs' => $attrs, ) ); } return array( $bar, $dialogs ); } public static function confirm_attrs( string $dialog ): array { $attrs = \Rooster\Admin\States::confirm_attrs( $dialog ); if ( ! wp_script_is( 'rooster-admin-chrome', 'registered' ) ) { wp_dequeue_script( 'rooster-admin-states' ); } return $attrs; } private static function scope( TableDefinition $d, TableQuery $q, string $view, string $id ): array { $scope = array( 'ctx' => (string) wp_json_encode( (object) $d->context() ) ); if ( array() !== $d->bulk() ) { $scope['form'] = $id . '-bulk'; } $lang = Switcher::current(); if ( '' !== $lang ) { $scope['lang'] = $lang; } if ( '' !== $view ) { $value = $q->filter( $view ); if ( is_string( $value ) && '' !== $value ) { $scope['fixed'] = (string) wp_json_encode( array( $view => $value ) ); } } return $scope; } private static function view_key( 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 dom_id( TableDefinition $d ): string { $base = 'rl-' . sanitize_html_class( str_replace( '.', '-', $d->id() ) ); self::$ids[ $base ] = ( self::$ids[ $base ] ?? 0 ) + 1; return 1 === self::$ids[ $base ] ? $base : $base . '-' . self::$ids[ $base ]; } }