<?php
/**
 * Dynamic Table element.
 *
 * @package BricksExtras
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Dynamic Table element class.
 */
class X_Dynamic_Table extends \Bricks\Element {

	/**
	 * Element category.
	 *
	 * @var string
	 */
	public $category = 'extras';

	/**
	 * Element name.
	 *
	 * @var string
	 */
	public $name = 'xdynamictable';

	/**
	 * Element icon.
	 *
	 * @var string
	 */
	public $icon = 'ti-layout-cta-btn-left';

	/**
	 * CSS selector.
	 *
	 * @var string
	 */
	public $css_selector = '';

	/**
	 * Required scripts.
	 *
	 * @var array
	 */
	public $scripts = array( 'xDynamicTable' );

	/**
	 * Loop index.
	 *
	 * @var int
	 */
	public $loop_index = 0;

	/**
	 * Script localized flag.
	 *
	 * @var bool
	 */
	private static $script_localized = false;

	/**
	 * Get element label.
	 */
	public function get_label() {
		return esc_html__( 'Dynamic Table', 'bricksextras' );
	}

	/**
	 * Get element keywords.
	 */
	public function get_keywords() {
		return array( 'dynamic table', 'table' );
	}

	/**
	 * Set control groups.
	 */
	public function set_control_groups() {

		$this->control_groups['table_content_group'] = array(
			'title' => esc_html__( 'Columns', 'bricks' ),
			'tab'   => 'content',
		);

		$this->control_groups['table_content_rows_group'] = array(
			'title'    => esc_html__( 'Table cell', 'bricks' ) . ' / ' . esc_html__( 'Row', 'bricks' ),
			'tab'      => 'content',
			'required' => array(
				array( 'maybeDynamic', '=', 'static' ),
			),
		);

		$this->control_groups['header_group'] = array(
			'title' => esc_html__( 'Header', 'bricks' ),
			'tab'   => 'content',
		);

		$this->control_groups['rows_group'] = array(
			'title' => esc_html__( 'Table cell', 'bricks' ) . ' / ' . esc_html__( 'Row', 'bricks' ) . ' (' . esc_html__( 'Styles', 'bricks' ) . ')',
			'tab'   => 'content',
		);

		$this->control_groups['search_group'] = array(
			'title' => esc_html__( 'Search', 'bricks' ),
			'tab'   => 'content',
		);

		$this->control_groups['pagination_group'] = array(
			'title' => esc_html__( 'Footer', 'bricks' ) . ' / ' . esc_html__( 'Pagination', 'bricks' ),
			'tab'   => 'content',
		);

		$this->control_groups['wrapper_group'] = array(
			'title' => esc_html__( 'Body', 'bricks' ),
			'tab'   => 'content',
		);

		$this->control_groups['mobile_group'] = array(
			'title' => esc_html__( 'Stack columns', 'bricksextras' ),
			'tab'   => 'content',
		);
	}


	/**
	 * Set element controls.
	 */
	public function set_controls() {

		$this->controls['maybeDynamic'] = array(
			'tab'         => 'content',
			'label'       => esc_html__( 'Populate table rows', 'bricksextras' ),
			'type'        => 'select',
			'options'     => array(
				'dynamic' => esc_html__( 'Dynamic Data', 'bricks' ) . ' (' . esc_html__( 'Populate rows with a query loop', 'bricksextras' ) . ')',
				'static'  => esc_html__( 'Static', 'bricks' ) . ' (' . esc_html__( 'Add rows & cells manually', 'bricksextras' ) . ')',
			),
			'clearable'   => false,
			'placeholder' => esc_html__( 'Dynamic Data', 'bricks' ) . ' (' . esc_html__( 'Populate rows with a query loop', 'bricksextras' ) . ')',
		);

		$this->controls['hasLoop'] = array(
			'tab'      => 'content',
			'label'    => esc_html__( 'Query loop', 'bricks' ),
			'type'     => 'checkbox',
			'required' => array(
				array( 'maybeDynamic', '!=', 'static' ),
			),
		);

		$this->controls['query'] = array(
			'tab'      => 'content',
			'label'    => esc_html__( 'Query', 'bricks' ),
			'type'     => 'query',
			'popup'    => true,
			'inline'   => true,
			'required' => array(
				array( 'hasLoop', '!=', '' ),
				array( 'maybeDynamic', '!=', 'static' ),
			),
		);

		$this->controls['column_sep'] = array(
			'tab'      => 'content',
			'type'     => 'separator',
			'group'    => 'table_content_group',
			'label'    => esc_html__( 'Columns', 'bricks' ),
			'required' => array(
				array( 'maybeDynamic', '!=', 'static' ),
			),
		);

		$this->controls['column_sep_static'] = array(
			'tab'         => 'content',
			'type'        => 'separator',
			'group'       => 'table_content_group',
			'label'       => esc_html__( 'Columns', 'bricks' ),
			'description' => esc_html__( 'Add the required number of columns', 'bricksextras' ),
			'required'    => array(
				array( 'maybeDynamic', '=', 'static' ),
			),
		);

		$this->controls['content_items'] = array(
			'tab'           => 'content',
			'type'          => 'repeater',
			'group'         => 'table_content_group',
			'label'         => esc_html__( 'Columns', 'bricks' ),
			'placeholder'   => esc_html__( 'Column', 'bricks' ),
			'titleProperty' => 'title',
			'fields'        => array(
				'title'            => array(
					'type'  => 'text',
					'label' => esc_html__( 'Column heading', 'bricksextras' ),
				),
				'data'             => array(
					'type'  => 'text',
					'label' => esc_html__( 'Cell data for this column', 'bricksextras' ),
				),
				'width'            => array(
					'type'   => 'number',
					'label'  => esc_html__( 'Min. width', 'bricks' ),
					'inline' => true,
					'units'  => array(
						'px' => array(
							'min'  => 1,
							'max'  => 1000,
							'step' => 1,
						),
					),
				),
				'attributes'       => array(
					'type'        => 'repeater',
					'label'       => esc_html__( 'Attributes', 'bricks' ),
					'placeholder' => esc_html__( 'Attribute', 'bricks' ),
					'fields'      => array(
						'name'  => array(
							'type'           => 'text',
							'label'          => esc_html__( 'Name', 'bricks' ),
							'hasDynamicData' => false,
						),
						'value' => array(
							'type'           => 'text',
							'label'          => esc_html__( 'Value', 'bricks' ),
							'hasDynamicData' => false,
						),
					),
				),
				'data_type'        => array(
					'inline'   => true,
					'small'    => true,
					'label'    => esc_html__( 'Sort column as numbers', 'bricksextras' ),
					'type'     => 'checkbox',
					'required' => array(
						array( 'data_type_date', '!=', true ),
					),
				),
				'data_type_date'   => array(
					'inline'   => true,
					'small'    => true,
					'label'    => esc_html__( 'Sort column as dates', 'bricksextras' ),
					'type'     => 'checkbox',
					'required' => array(
						array( 'data_type', '!=', true ),
					),
				),
				'initial_sort'     => array(
					'inline'      => true,
					'small'       => true,
					'label'       => esc_html__( 'Initial sort order', 'bricksextras' ),
					'type'        => 'select',
					'options'     => array(
						'none' => esc_html__( 'None', 'bricks' ),
						'asc'  => esc_html__( 'Ascending', 'bricks' ),
						'desc' => esc_html__( 'Descending', 'bricks' ),
					),
					'placeholder' => esc_html__( 'None', 'bricks' ),
					'info'        => esc_html__( 'Use this column to control the initial sort order', 'bricksextras' ),
				),
				'prevent_sortable' => array(
					'inline' => true,
					'small'  => true,
					'label'  => esc_html__( 'Disable sorting for this column', 'bricksextras' ),
					'type'   => 'checkbox',
				),
				'scope'            => array(
					'inline'      => true,
					'small'       => true,
					'label'       => esc_html__( 'Table head scope', 'bricksextras' ),
					'type'        => 'select',
					'options'     => array(
						'none' => esc_html__( 'None', 'bricks' ),
						'col'  => 'col',
						'row'  => 'row',
					),
					'placeholder' => 'col',
				),
			),
			'default'       => array(
				array(
					'title' => esc_html__( 'Column', 'bricks' ),
				),
			),
			'required'      => array(
				array( 'maybeDynamic', '!=', 'static' ),
			),
		);

		$this->controls['content_items_static'] = array(
			'tab'           => 'content',
			'type'          => 'repeater',
			'group'         => 'table_content_group',
			'label'         => esc_html__( 'Columns', 'bricks' ),
			'placeholder'   => esc_html__( 'Column', 'bricks' ),
			'titleProperty' => 'title',
			'fields'        => array(
				'title'            => array(
					'type'  => 'text',
					'label' => esc_html__( 'Column heading', 'bricksextras' ),
				),
				'width'            => array(
					'type'   => 'number',
					'label'  => esc_html__( 'Min. width', 'bricks' ),
					'inline' => true,
					'units'  => array(
						'px' => array(
							'min'  => 1,
							'max'  => 1000,
							'step' => 1,
						),
					),
				),
				'attributes'       => array(
					'type'        => 'repeater',
					'label'       => esc_html__( 'Attributes', 'bricks' ),
					'placeholder' => esc_html__( 'Attribute', 'bricks' ),
					'fields'      => array(
						'name'  => array(
							'type'           => 'text',
							'label'          => esc_html__( 'Name', 'bricks' ),
							'hasDynamicData' => false,
						),
						'value' => array(
							'type'           => 'text',
							'label'          => esc_html__( 'Value', 'bricks' ),
							'hasDynamicData' => false,
						),
					),
				),
				'data_type'        => array(
					'inline'   => true,
					'small'    => true,
					'label'    => esc_html__( 'Sort column as numbers', 'bricksextras' ),
					'type'     => 'checkbox',
					'required' => array(
						array( 'data_type_date', '!=', true ),
					),
				),
				'data_type_date'   => array(
					'inline'   => true,
					'small'    => true,
					'label'    => esc_html__( 'Sort column as dates', 'bricksextras' ),
					'type'     => 'checkbox',
					'required' => array(
						array( 'data_type', '!=', true ),
					),
				),
				'initial_sort'     => array(
					'inline'      => true,
					'small'       => true,
					'label'       => esc_html__( 'Initial sort order', 'bricksextras' ),
					'type'        => 'select',
					'options'     => array(
						'none' => esc_html__( 'None', 'bricks' ),
						'asc'  => esc_html__( 'Ascending', 'bricks' ),
						'desc' => esc_html__( 'Descending', 'bricks' ),
					),
					'placeholder' => esc_html__( 'None', 'bricks' ),
					'info'        => esc_html__( 'Use this column to control the initial sort order', 'bricksextras' ),
				),
				'prevent_sortable' => array(
					'inline' => true,
					'small'  => true,
					'label'  => esc_html__( 'Prevent column from being sortable', 'bricksextras' ),
					'type'   => 'checkbox',
				),
				'scope'            => array(
					'inline'      => true,
					'small'       => true,
					'label'       => esc_html__( 'Table head scope', 'bricksextras' ),
					'type'        => 'select',
					'options'     => array(
						'none' => esc_html__( 'None', 'bricks' ),
						'col'  => 'col',
						'row'  => 'row',
					),
					'placeholder' => 'col',
				),
			),
			'default'       => array(
				array(
					'title' => esc_html__( 'Column', 'bricks' ),
				),
			),
			'required'      => array(
				array( 'maybeDynamic', '=', 'static' ),
			),
		);

		$this->controls['row_sep'] = array(
			'tab'         => 'content',
			'type'        => 'separator',
			'group'       => 'table_content_rows_group',
			'label'       => esc_html__( 'Rows', 'bricks' ),
			'description' => esc_html__( 'Add rows and cells. The number of visible rows depends on pagination settings.', 'bricksextras' ),
			'required'    => array( 'maybeDynamic', '=', 'static' ),
		);

		$this->controls['row_items'] = array(
			'tab'           => 'content',
			'type'          => 'repeater',
			'group'         => 'table_content_rows_group',
			'label'         => esc_html__( 'Rows', 'bricks' ) . ' > ' . esc_html__( 'Cells', 'bricksextras' ),
			'placeholder'   => esc_html__( 'Row', 'bricks' ),
			'titleProperty' => 'title',
			'fields'        => array(
				'cell' => array(
					'type'          => 'repeater',
					'label'         => esc_html__( 'Cell', 'bricksextras' ),
					'placeholder'   => esc_html__( 'Cell', 'bricksextras' ),
					'titleProperty' => 'title',
					'fields'        => array(
						'cell_content_text'   => array(
							'type'           => 'text',
							'label'          => esc_html__( 'Content', 'bricks' ),
							'hasDynamicData' => false,
							'required'       => array( 'cell_content_type', '!=', true ),
						),
						'cell_content_editor' => array(
							'type'     => 'editor',
							'label'    => esc_html__( 'Content', 'bricks' ),
							'required' => array( 'cell_content_type', '=', true ),
						),
						'cell_content_type'   => array(
							'type'  => 'checkbox',
							'label' => esc_html__( 'Text editor', 'bricks' ),
						),
					),
				),
			),
			'default'       => array(
				array(
					'title' => esc_html__( 'Row', 'bricks' ),
				),
			),
			'required'      => array( 'maybeDynamic', '=', 'static' ),
		);

		$this->controls['config_sep'] = array(
			'tab'   => 'content',
			'type'  => 'separator',
			'group' => 'table_content_group',
		);

		$this->controls['maybe_sortable'] = array(
			'tab'         => 'content',
			'label'       => esc_html__( 'Sortable columns', 'bricksextras' ),
			'group'       => 'table_content_group',
			'type'        => 'select',
			'options'     => array(
				'true'  => esc_html__( 'Enable', 'bricks' ),
				'false' => esc_html__( 'Disable', 'bricks' ),
			),
			'inline'      => true,
			'clearable'   => false,
			'placeholder' => esc_html__( 'Disable', 'bricks' ),
		);

			$this->controls['resizable'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Resizable columns', 'bricksextras' ),
				'group'       => 'table_content_group',
				'type'        => 'select',
				'options'     => array(
					'true'  => esc_html__( 'Enable', 'bricks' ),
					'false' => esc_html__( 'Disable', 'bricks' ),
				),
				'inline'      => true,
				'clearable'   => false,
				'placeholder' => esc_html__( 'Disable', 'bricks' ),
			);

			$this->controls['whitespace'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Prevent text wrap', 'bricksextras' ),
				'group'       => 'table_content_group',
				'type'        => 'select',
				'options'     => array(
					'nowrap' => esc_html__( 'Enable', 'bricks' ),
					'normal' => esc_html__( 'Disable', 'bricks' ),
				),
				'inline'      => true,
				'clearable'   => false,
				'placeholder' => esc_html__( 'Enable', 'bricks' ),
			);

			$this->controls['cellOverflow'] = array(
				'tab'         => 'content',
				'group'       => 'table_content_group',
				'label'       => esc_html__( 'Overflow', 'bricks' ) . ' (' . esc_html__( 'Table cell', 'bricks' ) . ')',
				'type'        => 'select',
				'options'     => array(
					'auto'    => 'auto',
					'scroll'  => 'scroll',
					'visible' => 'visible',
					'hidden'  => 'hidden',
					'clip'    => 'clip',
				),
				'inline'      => true,
				'css'         => array(
					array(
						'property' => 'overflow',
						'selector' => 'td.gridjs-td',
					),
				),
				'placeholder' => esc_html__( 'Auto', 'bricks' ),
			);

			$this->controls['noRecordsFound'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Text', 'bricks' ) . ' (' . esc_html__( 'No results found', 'bricks' ) . ')',
				'group'       => 'table_content_group',
				'type'        => 'text',
				'placeholder' => esc_attr__( 'No matching records found', 'bricksextras' ),
			);

			/* searchable */

			$this->controls['maybe_searchable'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Enable', 'bricks' ),
				'type'        => 'select',
				'group'       => 'search_group',
				'options'     => array(
					'true'  => esc_html__( 'Enable', 'bricks' ),
					'false' => esc_html__( 'Disable', 'bricks' ),
				),
				'inline'      => true,
				'clearable'   => false,
				'placeholder' => esc_html__( 'Disable', 'bricks' ),
			);

			$this->controls['search_text'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Placeholder', 'bricks' ),
				'group'       => 'search_group',
				'type'        => 'text',
				'placeholder' => 'Search..',
				'required'    => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['search_sep'] = array(
				'tab'      => 'content',
				'group'    => 'search_group',
				'type'     => 'separator',
				'label'    => esc_html__( 'Input', 'bricks' ),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['search_input_padding'] = array(
				'tab'      => 'content',
				'group'    => 'search_group',
				'label'    => esc_html__( 'Padding', 'bricks' ),
				'type'     => 'spacing',
				'css'      => array(
					array(
						'property' => 'padding',
						'selector' => '.gridjs-search-input',
					),
				),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['search_input_margin'] = array(
				'tab'      => 'content',
				'group'    => 'search_group',
				'label'    => esc_html__( 'Margin', 'bricks' ),
				'type'     => 'spacing',
				'css'      => array(
					array(
						'property' => 'margin',
						'selector' => '.gridjs-search-input',
					),
				),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['search_width'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Width', 'bricks' ),
				'group'    => 'search_group',
				'type'     => 'number',
				'min'      => 0,
				'max'      => 10,
				'step'     => '1',
				'units'    => 'px',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'width',
						'selector' => '.gridjs-search',
					),
				),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['search_float'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Float', 'bricks' ),
				'type'        => 'select',
				'group'       => 'search_group',
				'options'     => array(
					'left'  => esc_html__( 'Left', 'bricks' ),
					'right' => esc_html__( 'Right', 'bricks' ),
					'none'  => esc_html__( 'None', 'bricks' ),
				),
				'css'         => array(
					array(
						'property' => 'float',
						'selector' => '.gridjs-search',
					),
				),
				'placeholder' => esc_html__( 'Left', 'bricks' ),
				'inline'      => true,
				'clearable'   => false,
				'required'    => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['search_color'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Color', 'bricks' ),
				'group'    => 'search_group',
				'type'     => 'color',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'color',
						'selector' => 'input.gridjs-input',
					),
				),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['placeholdersearch_color'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Placeholder color', 'bricks' ),
				'group'    => 'search_group',
				'type'     => 'color',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'color',
						'selector' => 'input.gridjs-input::placeholder',
					),
				),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['search_bg'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Background', 'bricks' ),
				'group'    => 'search_group',
				'type'     => 'color',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'background-color',
						'selector' => 'input.gridjs-input',
					),
				),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['searchBorder'] = array(
				'tab'      => 'content',
				'group'    => 'search_group',
				'type'     => 'border',
				'label'    => esc_html__( 'Border', 'bricks' ),
				'css'      => array(
					array(
						'property' => 'border',
						'selector' => 'input.gridjs-input',
					),
				),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			$this->controls['searchTypography'] = array(
				'tab'      => 'content',
				'group'    => 'search_group',
				'type'     => 'typography',
				'label'    => esc_html__( 'Typography', 'bricks' ),
				'css'      => array(
					array(
						'property' => 'font',
						'selector' => 'input.gridjs-input',
					),
				),
				'required' => array( 'maybe_searchable', '=', 'true' ),
			);

			/* pagination */
			$this->controls['maybe_pagination'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Pagination', 'bricks' ),
				'type'        => 'select',
				'group'       => 'pagination_group',
				'options'     => array(
					'true'  => esc_html__( 'Enable', 'bricks' ),
					'false' => esc_html__( 'Disable', 'bricks' ),
				),
				'inline'      => true,
				'clearable'   => false,
				'placeholder' => esc_html__( 'Enable', 'bricks' ),
			);

			$this->controls['rows_per_page'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Rows per page', 'bricksextras' ),
				'group'       => 'pagination_group',
				'type'        => 'number',
				'min'         => 0,
				'max'         => 100,
				'step'        => 1,
				'inline'      => true,
				'placeholder' => esc_html__( '5', 'bricksextras' ),
				'required'    => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['buttons_count'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Count', 'bricks' ) . ' (' . esc_html__( 'Buttons', 'bricks' ) . ')',
				'group'       => 'pagination_group',
				'type'        => 'number',
				'min'         => 0,
				'max'         => 100,
				'step'        => 1,
				'inline'      => true,
				'placeholder' => esc_html__( '2', 'bricksextras' ),
				'required'    => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['maybe_summary'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Summary', 'bricksextras' ),
				'type'        => 'select',
				'group'       => 'pagination_group',
				'options'     => array(
					'true'  => esc_html__( 'Enable', 'bricks' ),
					'false' => esc_html__( 'Disable', 'bricks' ),
				),
				'inline'      => true,
				'clearable'   => false,
				'placeholder' => esc_html__( 'Enable', 'bricks' ),
				'required'    => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['pagination_text_sep'] = array(
				'tab'      => 'content',
				'group'    => 'pagination_group',
				'type'     => 'separator',
				'label'    => esc_html__( 'Summary', 'bricksextras' ),
				'required' => array(
					array( 'maybe_pagination', '!=', 'false' ),
					array( 'maybe_summary', '!=', 'false' ),
				),
			);

			$this->controls['showing_text'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Showing', 'bricksextras' ),
				'group'       => 'pagination_group',
				'type'        => 'text',
				'inline'      => true,
				'placeholder' => esc_attr__( 'Showing', 'bricksextras' ),
				'required'    => array(
					array( 'maybe_pagination', '!=', 'false' ),
					array( 'maybe_summary', '!=', 'false' ),
				),
			);

			$this->controls['to_text'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'to', 'bricksextras' ),
				'group'       => 'pagination_group',
				'type'        => 'text',
				'inline'      => true,
				'placeholder' => esc_attr__( 'to', 'bricksextras' ),
				'required'    => array(
					array( 'maybe_pagination', '!=', 'false' ),
					array( 'maybe_summary', '!=', 'false' ),
				),
			);

			$this->controls['of_text'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'of', 'bricksextras' ),
				'group'       => 'pagination_group',
				'type'        => 'text',
				'inline'      => true,
				'placeholder' => esc_attr__( 'of', 'bricksextras' ),
				'required'    => array(
					array( 'maybe_pagination', '!=', 'false' ),
					array( 'maybe_summary', '!=', 'false' ),
				),
			);

			$this->controls['results_text'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Results', 'bricks' ),
				'group'       => 'pagination_group',
				'type'        => 'text',
				'inline'      => true,
				'placeholder' => esc_attr__( 'Results', 'bricksextras' ),
				'required'    => array(
					array( 'maybe_pagination', '!=', 'false' ),
					array( 'maybe_summary', '!=', 'false' ),
				),
			);

			$this->controls['style_footer_sep'] = array(
				'tab'      => 'content',
				'group'    => 'pagination_group',
				'type'     => 'separator',
				'label'    => esc_html__( 'Footer', 'bricks' ),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['pagination_typography'] = array(
				'tab'      => 'content',
				'group'    => 'pagination_group',
				'type'     => 'typography',
				'label'    => esc_html__( 'Typography', 'bricks' ),
				'css'      => array(
					array(
						'property' => 'font',
						'selector' => '.gridjs-pagination',
					),
				),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['footer_color'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Text color', 'bricks' ),
				'group'    => 'pagination_group',
				'type'     => 'color',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'color',
						'selector' => '.gridjs-pagination',
					),
				),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['footer_bg'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Background', 'bricks' ),
				'group'    => 'pagination_group',
				'type'     => 'color',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'background-color',
						'selector' => '.gridjs-footer',
					),
				),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['footer_border'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Border', 'bricks' ),
				'group'    => 'pagination_group',
				'type'     => 'border',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'border',
						'selector' => '.gridjs-footer',
					),
				),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['footer_boxshadow'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Box shadow', 'bricks' ),
				'group'    => 'pagination_group',
				'type'     => 'box-shadow',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'box-shadow',
						'selector' => '.gridjs-footer',
					),
				),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['footer_padding'] = array(
				'tab'         => 'content',
				'group'       => 'pagination_group',
				'label'       => esc_html__( 'Padding', 'bricks' ),
				'type'        => 'spacing',
				'css'         => array(
					array(
						'property' => 'padding',
						'selector' => '.gridjs-footer',
					),
				),
				'placeholder' => array(
					'top'    => '12px',
					'right'  => '24px',
					'bottom' => '12px',
					'left'   => '24px',
				),
				'required'    => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['pagination_buttons_sep'] = array(
				'tab'      => 'content',
				'group'    => 'pagination_group',
				'type'     => 'separator',
				'label'    => esc_html__( 'Buttons', 'bricks' ),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['maybe_pagination_buttons'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Previous', 'bricks' ) . ' / ' . esc_html__( 'Next', 'bricks' ),
				'type'        => 'select',
				'group'       => 'pagination_group',
				'options'     => array(
					'true'  => esc_html__( 'Enable', 'bricks' ),
					'false' => esc_html__( 'Disable', 'bricks' ),
				),
				'inline'      => true,
				'clearable'   => false,
				'placeholder' => esc_html__( 'Enable', 'bricks' ),
				'required'    => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['pagination_buttons_sep'] = array(
				'tab'      => 'content',
				'group'    => 'pagination_group',
				'type'     => 'separator',
				'label'    => esc_html__( 'Button text', 'bricks' ),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['previous_text'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Previous', 'bricks' ),
				'group'       => 'pagination_group',
				'type'        => 'text',
				'inline'      => true,
				'placeholder' => 'Previous',
				'required'    => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['next_text'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Next', 'bricks' ),
				'group'       => 'pagination_group',
				'type'        => 'text',
				'inline'      => true,
				'placeholder' => 'Next',
				'required'    => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['pagination_color'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Color', 'bricks' ),
				'group'    => 'pagination_group',
				'type'     => 'color',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'color',
						'selector' => '.gridjs-pagination .gridjs-pages button',
					),
				),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['pagination_bg'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Background', 'bricks' ),
				'group'    => 'pagination_group',
				'type'     => 'color',
				'inline'   => true,
				'css'      => array(
					array(
						'property' => 'background-color',
						'selector' => '.gridjs-pagination .gridjs-pages button',
					),
				),
				'required' => array( 'maybe_pagination', '!=', 'false' ),
			);

			$this->controls['pagination_padding'] = array(
				'tab'         => 'content',
				'group'       => 'pagination_group',
				'label'       => esc_html__( 'Padding', 'bricks' ),
				'type'        => 'spacing',
				'css'         => array(
					array(
						'property' => 'padding',
						'selector' => '.gridjs-pagination .gridjs-pages button',
					),
				),
				'placeholder' => array(
					'top'    => '5px',
					'right'  => '14px',
					'bottom' => '5px',
					'left'   => '14px',
				),
				'required'    => array( 'maybe_pagination', '!=', 'false' ),
			);

			/* header */

			$this->controls['header_bg'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Background', 'bricks' ),
				'group'  => 'header_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'background-color',
						'selector' => 'th.gridjs-th',
					),
					array(
						'property' => 'background-color',
						'selector' => '.x-dynamic-table_stacked td:before',
					),
				),
			);

			$this->controls['header_color'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Text', 'bricks' ),
				'group'  => 'header_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'color',
						'selector' => 'th.gridjs-th',
					),
					array(
						'property' => 'color',
						'selector' => '.x-dynamic-table_stacked td:before',
					),
				),
			);

			$this->controls['header_typography'] = array(
				'tab'   => 'content',
				'group' => 'header_group',
				'type'  => 'typography',
				'label' => esc_html__( 'Typography', 'bricks' ),
				'css'   => array(
					array(
						'property' => 'font',
						'selector' => 'th.gridjs-th',
					),
					array(
						'property' => 'font',
						'selector' => '.x-dynamic-table_stacked td:before',
					),
				),
			);

			$this->controls['header_padding'] = array(
				'tab'         => 'content',
				'group'       => 'header_group',
				'label'       => esc_html__( 'Padding', 'bricks' ),
				'type'        => 'spacing',
				'css'         => array(
					array(
						'property' => 'padding',
						'selector' => 'th.gridjs-th',
					),
				),
				'placeholder' => array(
					'top'    => '14px',
					'right'  => '24px',
					'bottom' => '14px',
					'left'   => '24px',
				),
			);

			$this->controls['sort_arrow_sep'] = array(
				'tab'   => 'content',
				'group' => 'header_group',
				'type'  => 'separator',
				'label' => esc_html__( 'Arrows', 'bricks' ),
			);

			$this->controls['sort_arrow_color'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Color', 'bricks' ),
				'group'  => 'header_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => '--x-sort-arrow-color',
						'selector' => '.x-dynamic-table_container',
					),
				),
			);

			$this->controls['sort_arrow_opacity'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Opacity', 'bricks' ),
				'group'       => 'header_group',
				'type'        => 'number',
				'units'       => false,
				'placeholder' => '0.3',
				'inline'      => true,
				'css'         => array(
					array(
						'property' => '--x-sort-arrow-opacity',
						'selector' => '.x-dynamic-table_container',
					),
				),
			);

			/* table rows */

			$this->controls['row_bg'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Background', 'bricks' ),
				'group'  => 'rows_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'background-color',
						'selector' => 'td.gridjs-td',
					),
				),
			);

			$this->controls['row_bg_even'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Background', 'bricks' ) . ' (' . esc_html__( 'Even', 'bricksextras' ) . ')',
				'group'  => 'rows_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'background-color',
						'selector' => '.gridjs-tr:nth-child(even) td.gridjs-td',
					),
				),
			);

			$this->controls['row_color'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Text color', 'bricks' ),
				'group'  => 'rows_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'color',
						'selector' => 'td.gridjs-td',
					),
				),
			);

			$this->controls['row_color_even'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Text color', 'bricks' ) . ' (' . esc_html__( 'Even', 'bricksextras' ) . ')',
				'group'  => 'rows_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'color',
						'selector' => '.gridjs-tr:nth-child(even) td.gridjs-td',
					),
				),
			);

			$this->controls['row_border'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Border color', 'bricks' ),
				'group'  => 'rows_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => '--x-grid-border-color',
						'selector' => '',
					),
				),
			);

			$this->controls['row_typography'] = array(
				'tab'   => 'content',
				'group' => 'rows_group',
				'type'  => 'typography',
				'label' => esc_html__( 'Typography', 'bricks' ),
				'css'   => array(
					array(
						'property' => 'font',
						'selector' => 'td.gridjs-td',
					),
				),
			);

			$this->controls['row_padding'] = array(
				'tab'         => 'content',
				'group'       => 'rows_group',
				'label'       => esc_html__( 'Padding', 'bricks' ),
				'type'        => 'spacing',
				'css'         => array(
					array(
						'property' => 'padding',
						'selector' => 'td.gridjs-td',
					),
				),
				'placeholder' => array(
					'top'    => '14px',
					'right'  => '24px',
					'bottom' => '14px',
					'left'   => '24px',
				),
			);

			$this->controls['row_min_height'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Row height', 'bricksextras' ),
				'group'  => 'rows_group',
				'type'   => 'number',
				'units'  => true,
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'height',
						'selector' => 'td.gridjs-td',
					),
				),
			);

			/* table wrapper */

			$this->controls['wrapper_bg'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Background', 'bricks' ),
				'group'  => 'wrapper_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'background-color',
						'selector' => '.gridjs-wrapper',
					),
				),
			);

			$this->controls['wrapper_color'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Text color', 'bricks' ),
				'group'  => 'wrapper_group',
				'type'   => 'color',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'color',
						'selector' => '.gridjs-wrapper',
					),
				),
			);

			$this->controls['wrapper_border'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Border', 'bricks' ),
				'group'  => 'wrapper_group',
				'type'   => 'border',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'border',
						'selector' => '.gridjs-wrapper',
					),
				),
			);

			$this->controls['wrapper_shadow'] = array(
				'tab'    => 'content',
				'label'  => esc_html__( 'Box shadow', 'bricks' ),
				'group'  => 'wrapper_group',
				'type'   => 'box-shadow',
				'inline' => true,
				'css'    => array(
					array(
						'property' => 'box-shadow',
						'selector' => '.gridjs-wrapper',
					),
				),
			);

			$this->controls['wrapper_typography'] = array(
				'tab'   => 'content',
				'group' => 'wrapper_group',
				'type'  => 'typography',
				'label' => esc_html__( 'Typography', 'bricks' ),
				'css'   => array(
					array(
						'property' => 'font',
						'selector' => '.gridjs-wrapper',
					),
				),
			);

			$this->controls['wrapper_padding'] = array(
				'tab'   => 'content',
				'group' => 'wrapper_group',
				'label' => esc_html__( 'Padding', 'bricks' ),
				'type'  => 'spacing',
				'css'   => array(
					array(
						'property' => 'padding',
						'selector' => '.gridjs-wrapper',
					),
				),
			);

			$this->controls['body_height'] = array(
				'tab'   => 'content',
				'group' => 'wrapper_group',
				'type'  => 'number',
				'units' => true,
				'label' => esc_html__( 'Height', 'bricks' ) . ' (' . esc_html__( 'Fixed', 'bricks' ) . ')',
				'css'   => array(
					array(
						'property'  => 'height',
						'selector'  => '.x-dynamic-table_container:not(.x-dynamic-table_stacked) .gridjs-wrapper',
						'important' => true,
					),
				),
			);

			/* mobile version */
			$breakpoint_options = array();

			foreach ( \Bricks\Breakpoints::$breakpoints as $breakpoint ) {
				$breakpoint_options[ $breakpoint['width'] ] = $breakpoint['label'] . ' (<= ' . $breakpoint['width'] . 'px )';
			}

			$breakpoint_options['false'] = 'None';

			$this->controls['maybeStack'] = array(
				'tab'         => 'content',
				'group'       => 'mobile_group',
				'label'       => esc_html__( 'Stack columns vertically at breakpoint', 'bricksextras' ),
				'type'        => 'select',
				'options'     => $breakpoint_options,
				'placeholder' => 'None',
			);

			$this->controls['stackRowGap'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Row gap', 'bricks' ),
				'group'       => 'mobile_group',
				'type'        => 'number',
				'min'         => 0,
				'max'         => 100,
				'step'        => 1,
				'inline'      => true,
				'placeholder' => '',
				'units'       => true,
				'css'         => array(
					array(
						'property' => 'margin-bottom',
						'selector' => '.x-dynamic-table_stacked .gridjs-tr',
					),
				),
			);

			$this->controls['stackLabelsSep'] = array(
				'tab'   => 'content',
				'type'  => 'separator',
				'label' => esc_html__( 'Column labels', 'bricksextras' ),
				'group' => 'mobile_group',
			);

			$this->controls['stackWidth'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Width', 'bricks' ),
				'group'    => 'mobile_group',
				'type'     => 'number',
				'min'      => 0,
				'max'      => 100,
				'step'     => 1,
				'inline'   => true,
				'units'    => true,
				'css'      => array(
					array(
						'property' => 'width',
						'selector' => '&:not([data-x-table*=stackLabels]) .x-dynamic-table_stacked td:before',
					),
				),
				'required' => array( 'stackLabels', '!=', 'true' ),
			);

			$this->controls['stackMinWidth'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Min. width', 'bricks' ),
				'group'       => 'mobile_group',
				'type'        => 'number',
				'min'         => 0,
				'max'         => 1000,
				'step'        => 1,
				'inline'      => true,
				'placeholder' => '150px',
				'units'       => true,
				'css'         => array(
					array(
						'property' => 'min-width',
						'selector' => '&:not([data-x-table*=stackLabels]) .x-dynamic-table_stacked td:before',
					),
				),
				'required'    => array( 'stackLabels', '!=', 'true' ),
			);

			$this->controls['stackPadding'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Cell inline padding', 'bricksextras' ),
				'info'        => esc_html__( 'Padding behind the label', 'bricksextras' ),
				'group'       => 'mobile_group',
				'type'        => 'number',
				'min'         => 0,
				'max'         => 1000,
				'inline'      => true,
				'placeholder' => '150px',
				'units'       => true,
				'css'         => array(
					array(
						'property' => 'padding-inline-start',
						'selector' => '&:not([data-x-table*=stackLabels]) .x-dynamic-table_stacked td',
					),
				),
				'required'    => array( 'stackLabels', '!=', 'true' ),
			);

			$this->controls['stackLabels'] = array(
				'tab'         => 'content',
				'label'       => esc_html__( 'Move cell content onto new line', 'bricksextras' ),
				'type'        => 'select',
				'group'       => 'mobile_group',
				'options'     => array(
					'true'  => esc_html__( 'True', 'bricks' ),
					'false' => esc_html__( 'False', 'bricks' ),
				),
				'inline'      => true,
				'small'       => false,
				'placeholder' => esc_html__( 'Disable', 'bricks' ),
			);

			$this->controls['stackCellTypography'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Typography', 'bricks' ) . ' ' . esc_html__( 'Cell', 'bricksextras' ),
				'group'    => 'mobile_group',
				'type'     => 'typography',
				'units'    => true,
				'css'      => array(
					array(
						'property' => 'font',
						'selector' => '&[data-x-table*=stackLabels] .x-dynamic-table_stacked td span',
					),
				),
				'required' => array( 'stackLabels', '=', 'true' ),
			);

			$this->controls['stackCellPadding'] = array(
				'tab'      => 'content',
				'label'    => esc_html__( 'Padding', 'bricks' ) . ' ' . esc_html__( 'Cell', 'bricksextras' ),
				'group'    => 'mobile_group',
				'type'     => 'spacing',
				'units'    => true,
				'css'      => array(
					array(
						'property' => 'padding',
						'selector' => '&[data-x-table*=stackLabels] .x-dynamic-table_stacked td span',
					),
				),
				'required' => array( 'stackLabels', '=', 'true' ),
			);
	}


	/**
	 * Enqueue scripts.
	 */
	public function enqueue_scripts() {

		if ( bricks_is_builder_main() ) {
			return;
		}

		wp_enqueue_script( 'x-gridjs', BRICKSEXTRAS_URL . 'components/assets/js/grid.js', array( 'bricks-scripts' ), '5.0.1', true );
		wp_enqueue_script( 'x-table', BRICKSEXTRAS_URL . 'components/assets/js/' . \BricksExtras\Helpers::maybe_minify_scripts( 'dynamictable' ) . '.js', array( 'x-gridjs' ), \BricksExtras\Plugin::VERSION, true );

		if ( ! self::$script_localized ) {

			wp_localize_script(
				'x-table',
				'xTable',
				array(
					'Instances' => array(),
					'Columns'   => array(),
					'Data'      => array(),
				)
			);

			self::$script_localized = true;

		}

		if ( ! \BricksExtras\Helpers::element_css_added( $this->name ) ) {
			wp_enqueue_style( 'x-table', BRICKSEXTRAS_URL . 'components/assets/css/dynamictable.css', array(), \BricksExtras\Plugin::VERSION );
		}
	}

	/**
	 * Render element output.
	 */
	public function render() {

		$settings = $this->settings;

		$header_items         = ! empty( $settings['header_items'] ) ? $settings['header_items'] : false;
		$content_items        = ! empty( $settings['content_items'] ) ? $settings['content_items'] : false;
		$content_items_static = ! empty( $settings['content_items_static'] ) ? $settings['content_items_static'] : array();
		$row_items            = ! empty( $settings['row_items'] ) ? $settings['row_items'] : false;
		$maybe_dynamic        = isset( $settings['maybeDynamic'] ) ? $settings['maybeDynamic'] : 'dynamic';

		$stack_labels = isset( $settings['stackLabels'] ) ? $settings['stackLabels'] : 'false';

		$config = array(
			'sort'           => isset( $settings['maybe_sortable'] ) ? $settings['maybe_sortable'] : false,
			'resizable'      => isset( $settings['resizable'] ) ? $settings['resizable'] : false,
			'whitespace'     => isset( $settings['whitespace'] ) ? $settings['whitespace'] : 'nowrap',
			'search'         => isset( $settings['maybe_searchable'] ) ? $settings['maybe_searchable'] : false,
			'placeholder'    => isset( $settings['search_text'] ) ? bricks_render_dynamic_data( $settings['search_text'] ) : 'Search..',
			'stack'          => isset( $settings['maybeStack'] ) ? $settings['maybeStack'] : 'false',
			'noRecordsFound' => isset( $settings['noRecordsFound'] ) ? bricks_render_dynamic_data( $settings['noRecordsFound'] ) : __( 'No matching records found', 'bricksextras' ),
		);

		if ( ! isset( $settings['maybe_pagination'] ) || 'true' === $settings['maybe_pagination'] ) {
			$config += array(
				'rowsPerPage'       => isset( $settings['rows_per_page'] ) ? intval( $settings['rows_per_page'] ) : 5,
				'previous'          => isset( $settings['previous_text'] ) ? wp_kses_post( bricks_render_dynamic_data( $settings['previous_text'] ) ) : esc_attr__( 'Previous', 'bricksextras' ),
				'next'              => isset( $settings['next_text'] ) ? wp_kses_post( bricks_render_dynamic_data( $settings['next_text'] ) ) : esc_attr__( 'Next', 'bricksextras' ),
				'showing'           => isset( $settings['showing_text'] ) ? wp_kses_post( bricks_render_dynamic_data( $settings['showing_text'] ) ) : esc_attr__( 'Showing', 'bricksextras' ),
				'results'           => isset( $settings['results_text'] ) ? wp_kses_post( bricks_render_dynamic_data( $settings['results_text'] ) ) : esc_attr__( 'Results', 'bricksextras' ),
				'paginationButtons' => ! empty( $settings['maybe_pagination_buttons'] ) ? $settings['maybe_pagination_buttons'] : 'true',
				'summary'           => isset( $settings['maybe_summary'] ) ? $settings['maybe_summary'] : 'true',
				'to'                => isset( $settings['to_text'] ) ? wp_kses_post( bricks_render_dynamic_data( $settings['to_text'] ) ) : esc_attr__( 'to', 'bricksextras' ),
				'of'                => isset( $settings['of_text'] ) ? wp_kses_post( bricks_render_dynamic_data( $settings['of_text'] ) ) : esc_attr__( 'of', 'bricksextras' ),
				'buttonsCount'      => isset( $settings['buttons_count'] ) ? intval( $settings['buttons_count'] ) : 2,
			);
		}

		if ( 'true' === $stack_labels ) {
			$config += array(
				'stackLabels' => $stack_labels,
			);
		}

		$this->set_attribute( '_root', 'data-x-table', wp_json_encode( $config ) );

		// Generate and set a unique identifier for this instance.
		$indentifier = \BricksExtras\Helpers::set_identifier_attribute( $this );

		$output = '';

		$output .= "<div {$this->render_attributes( '_root' )}>";

		$output .= "<table class='x-dynamic-table_table'>";

		if ( 'dynamic' === $maybe_dynamic ) {

			/* table header */
			if ( $content_items ) {

				$output .= '<thead><tr>';

				foreach ( $content_items as $index => $content_item ) {
					$output .= $this->render_table_header_cell( $content_item );
				}

				$output .= '</tr></thead>';

			}
		} elseif ( $content_items_static ) {

			/* table header */
			$output .= '<thead><tr>';

			foreach ( $content_items_static as $index => $content_item ) {
				$output .= $this->render_table_header_cell( $content_item );
			}

			$output .= '</tr></thead>';

		}

		/* table content */

		$output .= '<tbody>';

		/* dynamic */

		if ( 'dynamic' === $maybe_dynamic ) {

			// Query Loop.
			if ( isset( $settings['hasLoop'] ) ) {
				$query = new \Bricks\Query(
					array(
						'id'       => $this->id,
						'settings' => $settings,
					)
				);

				if ( 0 === $query->count ) {
					// No results.
					$no_results_content = $query->get_no_results_content();

					if ( empty( $no_results_content ) ) {
						$output .= $this->render_element_placeholder( array( 'title' => esc_html__( 'No results', 'bricks' ) ) );
					}
				}

				$content_item = $content_items[0];

				$output .= wp_kses_post( $query->render( array( $this, 'render_repeater_item' ), compact( 'content_items' ) ) );
				$query->destroy();
				unset( $query );
			}
		} elseif ( $row_items ) { /* static */

			foreach ( $row_items as $index => $row_item ) {

				$output .= wp_kses_post( $this->render_repeater_item_static( $row_item, $content_items_static ) );

			}
		}

		$output .= '</tbody>';

		$output .= '</table>';

		$output .= "<div class='x-dynamic-table_container'></div>";

		$output .= '</div>';

		echo $output;
	}

	/**
	 * Render repeater item.
	 *
	 * @param array $content_items Content items array.
	 */
	public function render_repeater_item( $content_items ) {

		$settings = $this->settings;
		$index    = $this->loop_index;

		// Render.
		$html = '<tr>';

		if ( $content_items ) {

			foreach ( $content_items as $index => $content_item ) {

				$data_type = 'string';

				if ( isset( $content_item['data_type'] ) ) {
					$data_type = 'number';
				}

				if ( isset( $content_item['data_type_date'] ) ) {
					$data_type = 'date';
				}

				if ( isset( $content_item['prevent_sortable'] ) ) {
					$data_type = 'prevent_sortable';
				}

				$html .= '<td data-x-type="' . esc_attr( $data_type ) . '">';

				$content_datas = ! empty( $content_item['data'] ) ? $content_item['data'] : '';

				$html .= bricks_render_dynamic_data( $content_datas );

				$html .= '</td>';

			}
		}

		$html .= '</tr>';

		++$this->loop_index;

		return $html;
	}

	/**
	 * Render repeater item static.
	 *
	 * @param array $row_item Row item data.
	 * @param array $content_items_static Static content items array.
	 */
	public function render_repeater_item_static( $row_item, $content_items_static ) {

		$settings = $this->settings;

		// Render.
		$html = '<tr>';

		$data_types = array();

		/* get column data types */
		if ( $content_items_static ) {

			foreach ( $content_items_static as $index => $content_item_static ) {

				$data_type = 'string';

				if ( isset( $content_item_static['data_type'] ) ) {
					$data_type = 'number';
				}

				if ( isset( $content_item_static['data_type_date'] ) ) {
					$data_type = 'date';
				}

				if ( isset( $content_item_static['prevent_sortable'] ) ) {
					$data_type = 'prevent_sortable';
				}

				$data_types[] = $data_type;

			}
		}

		if ( isset( $row_item['cell'] ) ) {

			foreach ( array_slice( $row_item['cell'], 0, count( $content_items_static ) ) as $index => $cell ) {

				if ( isset( $cell['cell_content_type'] ) ) {
					$cell_content = ! empty( $cell['cell_content_editor'] ) ? $cell['cell_content_editor'] : false;
				} else {
					$cell_content = ! empty( $cell['cell_content_text'] ) ? $cell['cell_content_text'] : false;
				}

				$html .= '<td rowspan="2" data-x-type="' . esc_attr( $data_types[ $index ] ) . '">';
				if ( $cell_content ) {
					$html .= wp_kses_post( $cell_content );
				}
				$html .= '</td>';

			}

			$missing_cells = count( $content_items_static ) - count( $row_item['cell'] );
			for ( $i = 0; $i < $missing_cells; $i++ ) {
				$html .= '<td data-x-type=""></td>';
			}
		} else {
			$total_cells = count( $content_items_static );
			for ( $i = 0; $i < $total_cells; $i++ ) {
				$html .= '<td data-x-type=""></td>';
			}
		}

		$html .= '</tr>';

		return $html;
	}

	/**
	 * Generate table header HTML for a content item.
	 *
	 * @param array $content_item The content item data.
	 * @return string HTML for the table header cell.
	 */
	private function render_table_header_cell( $content_item ) {
		$content_widths = ! empty( $content_item['width'] ) ? $content_item['width'] : null;

		$data_type = 'string';

		if ( isset( $content_item['data_type'] ) ) {
			$data_type = 'number';
		}

		if ( isset( $content_item['data_type_date'] ) ) {
			$data_type = 'date';
		}

		if ( isset( $content_item['prevent_sortable'] ) ) {
			$data_type = 'prevent_sortable';
		}

		$attributes = ! empty( $content_item['attributes'] ) ? $content_item['attributes'] : false;

		$column_attributes = array();

		if ( $attributes ) {
			foreach ( $attributes as $attribute ) {

				$attribute_name  = isset( $attribute['name'] ) ? esc_attr( $attribute['name'] ) : false;
				$attribute_value = isset( $attribute['value'] ) ? esc_attr( $attribute['value'] ) : false;

				if ( (bool) $attribute_name && (bool) $attribute_value ) {

					$column_attributes += array(
						$attribute_name => $attribute_value,
					);

				}
			}
		}

		$scope = isset( $content_item['scope'] ) ? $content_item['scope'] : 'col';

		$initial_sort = isset( $content_item['initial_sort'] ) && 'none' !== $content_item['initial_sort'] ? $content_item['initial_sort'] : '';

		$html = '<th class="label" ';
		if ( 'none' !== $scope ) {
			$html .= 'scope="' . esc_attr( $scope ) . '" ';
		}
		$html .= 'data-x-type="' . esc_attr( $data_type ) . '" data-x-column="' . esc_attr( $content_widths ) . '"';
		if ( $initial_sort ) {
			$html .= ' data-x-initial-sort="' . esc_attr( $initial_sort ) . '"';
		}
		if ( ! empty( $column_attributes ) ) {
			$html .= ' data-x-column-attributes=\'' . wp_json_encode( $column_attributes ) . '\'';
		}
		$html .= '>' . esc_html( ! empty( $content_item['title'] ) ? bricks_render_dynamic_data( $content_item['title'] ) : '' ) . '</th>';

		return $html;
	}
}
