mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 06:15:19 +08:00
Fixes #11415: Added yii\console\widgets\Table
to draw tables in console apps
This commit is contained in:

committed by
Alexander Makarov

parent
e38310546e
commit
b0ad73e361
@ -291,3 +291,19 @@ If you need to build string dynamically combining multiple styles it's better to
|
||||
$name = $this->ansiFormat('Alex', Console::FG_YELLOW);
|
||||
echo "Hello, my name is $name.";
|
||||
```
|
||||
|
||||
### Tables
|
||||
|
||||
Since version 2.0.13 there is a widget that allows you to format table data in console. It could be used as the following:
|
||||
|
||||
```php
|
||||
echo Table::widget([
|
||||
'headers' => ['Project', 'Status', 'Participant'],
|
||||
'rows' => [
|
||||
['Yii', 'OK', '@samdark'],
|
||||
['Yii', 'OK', '@cebe'],
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
For details please refer to [[yii\console\widgets\Table|API documentation]].
|
||||
|
@ -15,6 +15,7 @@ Yii Framework 2 Change Log
|
||||
- Enh #13586: Added `$preserveNonEmptyValues` property to the `yii\behaviors\AttributeBehavior` (Kolyunya)
|
||||
- Bug #14192: Fixed wrong default null value for TIMESTAMP when using PostgreSQL (Tigrov)
|
||||
- Enh #14081: Added `yii\caching\CacheInterface` to make custom cache extensions adoption easier (silverfire)
|
||||
- Enh #11415: Added `yii\console\widgets\Table` to draw tables in console apps (pana1990, rob006, samdark, tonykor)
|
||||
- Chg #14286: Used primary inputmask package name instead of an alias (samdark)
|
||||
- Enh #14298: The default response formatter configs defined by `yii\web\Response::defaultFormatters()` now use the array syntax (brandonkelly)
|
||||
- Bug #14304: Fixed `yii\validators\UniqueValidator` and `yii\validators\ExistValidator` to skip prefixes in case expressions are used (samdark)
|
||||
|
374
framework/console/widgets/Table.php
Normal file
374
framework/console/widgets/Table.php
Normal file
@ -0,0 +1,374 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\console\widgets;
|
||||
|
||||
use Yii;
|
||||
use yii\base\Object;
|
||||
use yii\base\Widget;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\helpers\Console;
|
||||
|
||||
/**
|
||||
* Table class displays a table in console.
|
||||
*
|
||||
* For example,
|
||||
*
|
||||
* ```php
|
||||
* $table = new Table();
|
||||
*
|
||||
* echo $table
|
||||
* ->setHeaders(['test1', 'test2', 'test3'])
|
||||
* ->setRows([
|
||||
* ['col1', 'col2', 'col3'],
|
||||
* ['col1', 'col2', ['col3-0', 'col3-1', 'col3-2']],
|
||||
* ])
|
||||
* ->run();
|
||||
* ```
|
||||
*
|
||||
* or
|
||||
*
|
||||
* ```php
|
||||
* echo Table::widget(
|
||||
* 'headers' => ['test1', 'test2', 'test3'],
|
||||
* 'rows' => [
|
||||
* ['col1', 'col2', 'col3'],
|
||||
* ['col1', 'col2', ['col3-0', 'col3-1', 'col3-2']],
|
||||
* ],
|
||||
* );
|
||||
*
|
||||
* @author Daniel Gomez Pan <pana_1990@hotmail.com>
|
||||
* @since 2.0.13
|
||||
*/
|
||||
class Table extends Widget
|
||||
{
|
||||
const CHAR_TOP = 'top';
|
||||
const CHAR_TOP_MID = 'top-mid';
|
||||
const CHAR_TOP_LEFT = 'top-left';
|
||||
const CHAR_TOP_RIGHT = 'top-right';
|
||||
const CHAR_BOTTOM = 'bottom';
|
||||
const CHAR_BOTTOM_MID = 'bottom-mid';
|
||||
const CHAR_BOTTOM_LEFT = 'bottom-left';
|
||||
const CHAR_BOTTOM_RIGHT = 'bottom-right';
|
||||
const CHAR_LEFT = 'left';
|
||||
const CHAR_LEFT_MID = 'left-mid';
|
||||
const CHAR_MID = 'mid';
|
||||
const CHAR_MID_MID = 'mid-mid';
|
||||
const CHAR_RIGHT = 'right';
|
||||
const CHAR_RIGHT_MID = 'right-mid';
|
||||
const CHAR_MIDDLE = 'middle';
|
||||
|
||||
/**
|
||||
* @var array table headers
|
||||
*/
|
||||
private $_headers = [];
|
||||
/**
|
||||
* @var array table rows
|
||||
*/
|
||||
private $_rows = [];
|
||||
/**
|
||||
* @var array table chars
|
||||
*/
|
||||
private $_chars = [
|
||||
self::CHAR_TOP => '═',
|
||||
self::CHAR_TOP_MID => '╤',
|
||||
self::CHAR_TOP_LEFT => '╔',
|
||||
self::CHAR_TOP_RIGHT => '╗',
|
||||
self::CHAR_BOTTOM => '═',
|
||||
self::CHAR_BOTTOM_MID => '╧',
|
||||
self::CHAR_BOTTOM_LEFT => '╚',
|
||||
self::CHAR_BOTTOM_RIGHT => '╝',
|
||||
self::CHAR_LEFT => '║',
|
||||
self::CHAR_LEFT_MID => '╟',
|
||||
self::CHAR_MID => '─',
|
||||
self::CHAR_MID_MID => '┼',
|
||||
self::CHAR_RIGHT => '║',
|
||||
self::CHAR_RIGHT_MID => '╢',
|
||||
self::CHAR_MIDDLE => '│',
|
||||
];
|
||||
/**
|
||||
* @var array table column widths
|
||||
*/
|
||||
private $_columnWidths = [];
|
||||
/**
|
||||
* @var int screen width
|
||||
*/
|
||||
private $_screenWidth;
|
||||
/**
|
||||
* @var string list prefix
|
||||
*/
|
||||
private $_listPrefix = '• ';
|
||||
|
||||
/**
|
||||
* Set table headers
|
||||
*
|
||||
* @param array $headers table headers
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeaders(array $headers)
|
||||
{
|
||||
$this->_headers = $headers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set table rows
|
||||
*
|
||||
* @param array $rows table rows
|
||||
* @return $this
|
||||
*/
|
||||
public function setRows(array $rows)
|
||||
{
|
||||
$this->_rows = $rows;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set table chars
|
||||
*
|
||||
* @param array $chars table chars
|
||||
* @return $this
|
||||
*/
|
||||
public function setChars(array $chars)
|
||||
{
|
||||
$this->_chars = $chars;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set screen width
|
||||
*
|
||||
* @param int $width screen width
|
||||
* @return $this
|
||||
*/
|
||||
public function setScreenWidth($width)
|
||||
{
|
||||
$this->_screenWidth = $width;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set list prefix
|
||||
*
|
||||
* @param string $listPrefix list prefix
|
||||
* @return $this
|
||||
*/
|
||||
public function setListPrefix($listPrefix)
|
||||
{
|
||||
$this->_listPrefix = $listPrefix;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the rendered table
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->calculateRowsSize();
|
||||
$buffer = $this->renderSeparator(
|
||||
$this->_chars[self::CHAR_TOP_LEFT],
|
||||
$this->_chars[self::CHAR_TOP_MID],
|
||||
$this->_chars[self::CHAR_TOP],
|
||||
$this->_chars[self::CHAR_TOP_RIGHT]
|
||||
);
|
||||
// Header
|
||||
$buffer .= $this->renderRow($this->_headers,
|
||||
$this->_chars[self::CHAR_LEFT],
|
||||
$this->_chars[self::CHAR_MIDDLE],
|
||||
$this->_chars[self::CHAR_RIGHT]
|
||||
);
|
||||
|
||||
// Content
|
||||
foreach ($this->_rows as $row) {
|
||||
$buffer .= $this->renderSeparator(
|
||||
$this->_chars[self::CHAR_LEFT_MID],
|
||||
$this->_chars[self::CHAR_MID_MID],
|
||||
$this->_chars[self::CHAR_MID],
|
||||
$this->_chars[self::CHAR_RIGHT_MID]
|
||||
);
|
||||
$buffer .= $this->renderRow($row,
|
||||
$this->_chars[self::CHAR_LEFT],
|
||||
$this->_chars[self::CHAR_MIDDLE],
|
||||
$this->_chars[self::CHAR_RIGHT]);
|
||||
}
|
||||
|
||||
$buffer .= $this->renderSeparator(
|
||||
$this->_chars[self::CHAR_BOTTOM_LEFT],
|
||||
$this->_chars[self::CHAR_BOTTOM_MID],
|
||||
$this->_chars[self::CHAR_BOTTOM],
|
||||
$this->_chars[self::CHAR_BOTTOM_RIGHT]
|
||||
);
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a row of data into a string
|
||||
*
|
||||
* @param array $row row of data
|
||||
* @param string $spanLeft character for left border
|
||||
* @param string $spanMiddle character for middle border
|
||||
* @param string $spanRight character for right border
|
||||
* @return string
|
||||
* @see \yii\console\widgets\Table::render()
|
||||
*/
|
||||
protected function renderRow(array $row, $spanLeft, $spanMiddle, $spanRight)
|
||||
{
|
||||
$size = $this->_columnWidths;
|
||||
|
||||
$buffer = '';
|
||||
$arrayPointer = [];
|
||||
for ($i = 0, $max = $this->calculateRowHeight($row); $i < $max; $i++) {
|
||||
$buffer .= $spanLeft . ' ';
|
||||
foreach ($row as $index => $cell) {
|
||||
$prefix = '';
|
||||
if ($index !== 0) {
|
||||
$buffer .= $spanMiddle . ' ';
|
||||
}
|
||||
if (is_array($cell)) {
|
||||
if (empty($finalChunk[$index])) {
|
||||
$finalChunk[$index] = '';
|
||||
$start = 0;
|
||||
$prefix = $this->_listPrefix;
|
||||
if (!isset($arrayPointer[$index])) {
|
||||
$arrayPointer[$index] = 0;
|
||||
}
|
||||
} else {
|
||||
$start = mb_strwidth($finalChunk[$index], Yii::$app->charset);
|
||||
}
|
||||
$chunk = mb_substr($cell[$arrayPointer[$index]], $start, $size[$index] - 4, Yii::$app->charset);
|
||||
$finalChunk[$index] .= $chunk;
|
||||
if (isset($cell[$arrayPointer[$index] + 1]) && $finalChunk[$index] === $cell[$arrayPointer[$index]]) {
|
||||
$arrayPointer[$index]++;
|
||||
$finalChunk[$index] = '';
|
||||
}
|
||||
} else {
|
||||
$chunk = mb_substr($cell, ($size[$index] * $i) - ($i * 2), $size[$index] - 2, Yii::$app->charset);
|
||||
}
|
||||
$chunk = $prefix . $chunk;
|
||||
$repeat = $size[$index] - mb_strwidth($chunk, Yii::$app->charset) - 1;
|
||||
$buffer .= $chunk;
|
||||
if ($repeat >= 0) {
|
||||
$buffer .= str_repeat(' ', $repeat);
|
||||
}
|
||||
}
|
||||
$buffer .= "$spanRight\n";
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders separator
|
||||
*
|
||||
* @param string $spanLeft character for left border
|
||||
* @param string $spanMid character for middle border
|
||||
* @param string $spanMidMid character for middle-middle border
|
||||
* @param string $spanRight character for right border
|
||||
* @return string the generated separator row
|
||||
* @see \yii\console\widgets\Table::render()
|
||||
*/
|
||||
protected function renderSeparator($spanLeft, $spanMid, $spanMidMid, $spanRight)
|
||||
{
|
||||
$separator = $spanLeft;
|
||||
foreach ($this->_columnWidths as $index => $rowSize) {
|
||||
if ($index !== 0) {
|
||||
$separator .= $spanMid;
|
||||
}
|
||||
$separator .= str_repeat($spanMidMid, $rowSize);
|
||||
}
|
||||
$separator .= $spanRight . "\n";
|
||||
return $separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the size of rows to draw anchor of columns in console
|
||||
*
|
||||
* @see \yii\console\widgets\Table::render()
|
||||
*/
|
||||
protected function calculateRowsSize()
|
||||
{
|
||||
$this->_columnWidths = $columns = [];
|
||||
$totalWidth = 0;
|
||||
$screenWidth = $this->getScreenWidth() - 3;
|
||||
|
||||
for ($i = 0, $count = count($this->_headers); $i < $count; $i++) {
|
||||
$columns[] = ArrayHelper::getColumn($this->_rows, $i);
|
||||
$columns[$i][] = $this->_headers[$i];
|
||||
}
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$columnWidth = max(array_map(function ($val) {
|
||||
if (is_array($val)) {
|
||||
$encodings = array_fill(0, count($val), Yii::$app->charset);
|
||||
return max(array_map('mb_strwidth', $val, $encodings)) + mb_strwidth($this->_listPrefix, Yii::$app->charset);
|
||||
}
|
||||
return mb_strwidth($val, Yii::$app->charset);
|
||||
}, $column)) + 2;
|
||||
$this->_columnWidths[] = $columnWidth;
|
||||
$totalWidth += $columnWidth;
|
||||
}
|
||||
|
||||
$relativeWidth = $screenWidth / $totalWidth;
|
||||
|
||||
if ($totalWidth > $screenWidth) {
|
||||
foreach ($this->_columnWidths as $j => $width) {
|
||||
$this->_columnWidths[$j] = (int) ($width * $relativeWidth);
|
||||
if ($j === count($this->_columnWidths)) {
|
||||
$this->_columnWidths = $totalWidth;
|
||||
}
|
||||
$totalWidth -= $this->_columnWidths[$j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the height of a row
|
||||
*
|
||||
* @param array $row
|
||||
* @return int maximum row per cell
|
||||
* @see \yii\console\widgets\Table::render()
|
||||
*/
|
||||
protected function calculateRowHeight($row)
|
||||
{
|
||||
$rowsPerCell = array_map(function ($size, $columnWidth) {
|
||||
if (is_array($columnWidth)) {
|
||||
$rows = 0;
|
||||
foreach ($columnWidth as $width) {
|
||||
$rows += ceil($width / ($size - 2));
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
return ceil($columnWidth / ($size - 2));
|
||||
}, $this->_columnWidths, array_map(function ($val) {
|
||||
if (is_array($val)) {
|
||||
$encodings = array_fill(0, count($val), Yii::$app->charset);
|
||||
return array_map('mb_strwidth', $val, $encodings);
|
||||
}
|
||||
return mb_strwidth($val, Yii::$app->charset);
|
||||
}, $row)
|
||||
);
|
||||
|
||||
return max($rowsPerCell);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getting screen width
|
||||
*
|
||||
* @return int screen width
|
||||
*/
|
||||
protected function getScreenWidth()
|
||||
{
|
||||
if (!$this->_screenWidth) {
|
||||
$size = Console::getScreenSize();
|
||||
if (isset($size[0])) {
|
||||
$this->_screenWidth = $size[0];
|
||||
}
|
||||
}
|
||||
return $this->_screenWidth;
|
||||
}
|
||||
}
|
174
tests/framework/console/widgets/TableTest.php
Normal file
174
tests/framework/console/widgets/TableTest.php
Normal file
@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yiiunit\framework\console;
|
||||
|
||||
use yii\console\widgets\Table;
|
||||
use yiiunit\TestCase;
|
||||
|
||||
/**
|
||||
* @group console
|
||||
*/
|
||||
class TableTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->mockApplication();
|
||||
}
|
||||
|
||||
public function testTable()
|
||||
{
|
||||
$table = new Table();
|
||||
|
||||
$expected = <<<EXPECTED
|
||||
╔═══════════════╤═══════════════╤═══════════════╗
|
||||
║ test1 │ test2 │ test3 ║
|
||||
╟───────────────┼───────────────┼───────────────╢
|
||||
║ testcontent1 │ testcontent2 │ testcontent3 ║
|
||||
╟───────────────┼───────────────┼───────────────╢
|
||||
║ testcontent21 │ testcontent22 │ testcontent23 ║
|
||||
╚═══════════════╧═══════════════╧═══════════════╝
|
||||
|
||||
EXPECTED;
|
||||
|
||||
$this->assertEqualsWithoutLE($expected, $table->setHeaders(['test1', 'test2', 'test3'])
|
||||
->setRows([
|
||||
['testcontent1', 'testcontent2', 'testcontent3'],
|
||||
['testcontent21', 'testcontent22', 'testcontent23'],
|
||||
])->setScreenWidth(200)->run()
|
||||
);
|
||||
}
|
||||
|
||||
public function testTableWithFullwidthChars()
|
||||
{
|
||||
$table = new Table();
|
||||
|
||||
// test fullwidth chars
|
||||
// @see https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms
|
||||
$expected = <<<EXPECTED
|
||||
╔═════════════════╤═════════════════╤═════════════════╗
|
||||
║ test1 │ test2 │ test3 ║
|
||||
╟─────────────────┼─────────────────┼─────────────────╢
|
||||
║ testcontent1 │ testcontent2 │ testcontent3 ║
|
||||
╟─────────────────┼─────────────────┼─────────────────╢
|
||||
║ testcontent21 │ testcontent22 │ testcontent23 ║
|
||||
╚═════════════════╧═════════════════╧═════════════════╝
|
||||
|
||||
EXPECTED;
|
||||
|
||||
$this->assertEqualsWithoutLE($expected, $table->setHeaders(['test1', 'test2', 'test3'])
|
||||
->setRows([
|
||||
['testcontent1', 'testcontent2', 'testcontent3'],
|
||||
['testcontent21', 'testcontent22', 'testcontent23'],
|
||||
])->setScreenWidth(200)->run()
|
||||
);
|
||||
}
|
||||
|
||||
public function testLists()
|
||||
{
|
||||
$table = new Table();
|
||||
|
||||
$expected = <<<EXPECTED
|
||||
╔═══════════════╤═══════════════╤══════════════╗
|
||||
║ test1 │ test2 │ test3 ║
|
||||
╟───────────────┼───────────────┼──────────────╢
|
||||
║ testcontent1 │ testcontent2 │ testcontent3 ║
|
||||
╟───────────────┼───────────────┼──────────────╢
|
||||
║ testcontent21 │ testcontent22 │ • col1 ║
|
||||
║ │ │ • col2 ║
|
||||
╚═══════════════╧═══════════════╧══════════════╝
|
||||
|
||||
EXPECTED;
|
||||
|
||||
$this->assertEqualsWithoutLE($expected, $table->setHeaders(['test1', 'test2', 'test3'])
|
||||
->setRows([
|
||||
['testcontent1', 'testcontent2', 'testcontent3'],
|
||||
['testcontent21', 'testcontent22', ['col1', 'col2']],
|
||||
])->setScreenWidth(200)->run()
|
||||
);
|
||||
}
|
||||
|
||||
public function testListPrefix()
|
||||
{
|
||||
$table = new Table();
|
||||
|
||||
$expected = <<<EXPECTED
|
||||
╔═══════════════╤═══════════════╤══════════════╗
|
||||
║ test1 │ test2 │ test3 ║
|
||||
╟───────────────┼───────────────┼──────────────╢
|
||||
║ testcontent1 │ testcontent2 │ testcontent3 ║
|
||||
╟───────────────┼───────────────┼──────────────╢
|
||||
║ testcontent21 │ testcontent22 │ * col1 ║
|
||||
║ │ │ * col2 ║
|
||||
╚═══════════════╧═══════════════╧══════════════╝
|
||||
|
||||
EXPECTED;
|
||||
|
||||
$this->assertEqualsWithoutLE($expected, $table->setHeaders(['test1', 'test2', 'test3'])
|
||||
->setRows([
|
||||
['testcontent1', 'testcontent2', 'testcontent3'],
|
||||
['testcontent21', 'testcontent22', ['col1', 'col2']],
|
||||
])->setScreenWidth(200)->setListPrefix('* ')->run()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCustomChars()
|
||||
{
|
||||
$table = new Table();
|
||||
|
||||
$expected = <<<EXPECTED
|
||||
*++++++++++++++++*+++++++++++++++++*++++++++++++++++++*
|
||||
/ test1 / test2 / test3 /
|
||||
*++++++++++++++++*+++++++++++++++++*++++++++++++++++++*
|
||||
/ testcontent1 / testcontent2 / testcontent3 /
|
||||
*++++++++++++++++*+++++++++++++++++*++++++++++++++++++*
|
||||
/ testcontent_21 / testcontent__22 / testcontent___23 /
|
||||
*++++++++++++++++*+++++++++++++++++*++++++++++++++++++*
|
||||
|
||||
EXPECTED;
|
||||
|
||||
$this->assertEqualsWithoutLE($expected, $table->setHeaders(['test1', 'test2', 'test3'])
|
||||
->setRows([
|
||||
['testcontent1', 'testcontent2', 'testcontent3'],
|
||||
['testcontent_21', 'testcontent__22', 'testcontent___23'],
|
||||
])->setChars([
|
||||
Table::CHAR_TOP => '+', Table::CHAR_TOP_MID => '*', Table::CHAR_TOP_LEFT => '*',
|
||||
Table::CHAR_TOP_RIGHT => '*', Table::CHAR_BOTTOM => '+', Table::CHAR_BOTTOM_MID => '*',
|
||||
Table::CHAR_BOTTOM_LEFT => '*', Table::CHAR_BOTTOM_RIGHT => '*', Table::CHAR_LEFT => '/',
|
||||
Table::CHAR_LEFT_MID => '*', Table::CHAR_MID => '+', Table::CHAR_MID_MID => '*',
|
||||
Table::CHAR_RIGHT => '/', Table::CHAR_RIGHT_MID => '*', Table::CHAR_MIDDLE => '/',
|
||||
])->setScreenWidth(200)->run()
|
||||
);
|
||||
}
|
||||
|
||||
public function testTableWidgetSyntax()
|
||||
{
|
||||
$expected = <<<EXPECTED
|
||||
╔═══════════════╤═══════════════╤═══════════════╗
|
||||
║ test1 │ test2 │ test3 ║
|
||||
╟───────────────┼───────────────┼───────────────╢
|
||||
║ testcontent1 │ testcontent2 │ testcontent3 ║
|
||||
╟───────────────┼───────────────┼───────────────╢
|
||||
║ testcontent21 │ testcontent22 │ testcontent23 ║
|
||||
╚═══════════════╧═══════════════╧═══════════════╝
|
||||
|
||||
EXPECTED;
|
||||
|
||||
$this->assertEqualsWithoutLE(
|
||||
$expected,
|
||||
Table::widget([
|
||||
'headers' => ['test1', 'test2', 'test3'],
|
||||
'rows' => [
|
||||
['testcontent1', 'testcontent2', 'testcontent3'],
|
||||
['testcontent21', 'testcontent22', 'testcontent23'],
|
||||
],
|
||||
'screenWidth' => 200,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user