Skip to content

Commit

Permalink
Add psalm types for closures (#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
vjik authored Feb 14, 2025
1 parent 479cd37 commit 87f5ab2
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 46 deletions.
11 changes: 10 additions & 1 deletion src/Column/ActionButton.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@

use Closure;
use Stringable;
use Yiisoft\Yii\DataView\Column\Base\DataContext;

/**
* `ActionButton` represents a button in an action column of a grid.
*
* @psalm-type TContentClosure = Closure(array|object $data, DataContext $context): (string|Stringable)
* @psalm-type TUrlClosure = Closure(array|object $data, DataContext $context): string
* @psalm-type TAttributesClosure = Closure(array|object $data, DataContext $context): array
* @psalm-type TClassClosure = Closure(array|object $data, DataContext $context): (array<array-key, string|null>|string|null)
*/
final class ActionButton
{
Expand All @@ -24,7 +30,10 @@ final class ActionButton
* @param string|null $title Button title attribute.
* @param bool $overrideAttributes Whether to override default attributes with custom ones instead of merging.
*
* @psalm-param array<array-key,string|null>|Closure|false|string|null $class
* @psalm-param TContentClosure|string|Stringable $content
* @psalm-param TUrlClosure|string|null $url
* @psalm-param TAttributesClosure|array|null $attributes
* @psalm-param TClassClosure|array<array-key,string|null>|false|string|null $class
*/
public function __construct(
public readonly Closure|string|Stringable $content = '',
Expand Down
46 changes: 15 additions & 31 deletions src/Column/ActionColumnRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Yiisoft\Yii\DataView\Column;

use Closure;
use Stringable;
use Yiisoft\Html\Html;
use Yiisoft\Yii\DataView\Column\Base\Cell;
use Yiisoft\Yii\DataView\Column\Base\GlobalContext;
Expand Down Expand Up @@ -157,42 +158,25 @@ private function renderButton(ActionButton|callable $button, string $name, DataC
return $button($url);
}

if ($button->content instanceof Closure) {
$closure = $button->content;
/** @var string $content */
$content = $closure($context->data, $context);
} else {
$content = $button->content;
}
/** @var string|Stringable $content */
$content = $button->content instanceof Closure
? ($button->content)($context->data, $context)
: $button->content;

if ($button->url === null) {
$url = $this->createUrl($name, $context);
} elseif ($button->url instanceof Closure) {
$closure = $button->url;
/** @var string $url */
$url = $closure($context->data, $context);
} else {
$url = $button->url;
}
$url = $button->url instanceof Closure
? ($button->url)($context->data, $context)
: ($button->url ?? $this->createUrl($name, $context));

if ($button->attributes instanceof Closure) {
$closure = $button->attributes;
/** @var array $attributes */
$attributes = $closure($context->data, $context);
} else {
$attributes = $button->attributes ?? [];
}
$attributes = $button->attributes instanceof Closure
? ($button->attributes)($context->data, $context)
: ($button->attributes ?? []);
if (!$button->overrideAttributes && !empty($this->buttonAttributes)) {
$attributes = array_merge($this->buttonAttributes, $attributes);
}

if ($button->class instanceof Closure) {
$closure = $button->class;
/** @var array<array-key,string|null>|string|null $class */
$class = $closure($context->data, $context);
} else {
$class = $button->class;
}
$class = $button->class instanceof Closure
? ($button->class)($context->data, $context)
: $button->class;

if ($class === false) {
Html::addCssClass($attributes, $this->buttonClass);
Expand All @@ -207,7 +191,7 @@ private function renderButton(ActionButton|callable $button, string $name, DataC
$attributes['title'] = $button->title;
}

return (string) Html::a($content, $url, $attributes);
return Html::a($content, $url, $attributes)->render();
}

/**
Expand Down
9 changes: 8 additions & 1 deletion src/Column/CheckboxColumn.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
namespace Yiisoft\Yii\DataView\Column;

use Closure;
use Stringable;
use Yiisoft\Html\Tag\Input\Checkbox;
use Yiisoft\Yii\DataView\Column\Base\DataContext;

/**
* `CheckboxColumn` displays a column of checkboxes in a grid view.
*
* @psalm-type TContentClosure = Closure(Checkbox, DataContext): (string|Stringable)
*/
final class CheckboxColumn implements ColumnInterface
{
Expand All @@ -23,8 +28,10 @@ final class CheckboxColumn implements ColumnInterface
* @param bool $multiple Whether to allow multiple selection. If true, checkbox names will be
* an array like `name[]`; if false, checkbox names will be single strings.
* @param Closure|null $content Closure for generating custom checkbox content. If set, it should have
* the signature: `function(array|object $data, DataContext $context): string`.
* the signature: `function(Checkbox $input, DataContext $context): string|Stringable`.
* @param bool $visible Whether the column is visible.
*
* @psalm-param TContentClosure|null $content
*/
public function __construct(
public readonly ?string $header = null,
Expand Down
9 changes: 5 additions & 4 deletions src/Column/CheckboxColumnRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

namespace Yiisoft\Yii\DataView\Column;

use Stringable;
use Yiisoft\Html\Html;
use Yiisoft\Yii\DataView\Column\Base\Cell;
use Yiisoft\Yii\DataView\Column\Base\DataContext;
use Yiisoft\Yii\DataView\Column\Base\GlobalContext;
use Yiisoft\Yii\DataView\Column\Base\HeaderContext;

use function array_key_exists;

/**
* `CheckboxColumnRenderer` renders checkbox inputs in a grid column.
*
Expand Down Expand Up @@ -54,9 +55,9 @@ public function renderBody(ColumnInterface $column, Cell $cell, DataContext $con

$input = Html::checkbox($name, $value, $inputAttributes);

$contentClosure = $column->content;
/** @var string|Stringable $content */
$content = $contentClosure === null ? $input : $contentClosure($input, $context);
$content = $column->content === null
? $input
: ($column->content)($input, $context);

return $cell
->addAttributes($column->bodyAttributes)
Expand Down
22 changes: 13 additions & 9 deletions src/GridView.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
use Yiisoft\Yii\DataView\Filter\Factory\IncorrectValueException;

use function array_key_exists;
use function call_user_func;
use function call_user_func_array;
use function is_callable;

Expand All @@ -42,17 +41,20 @@
* The look and feel of a grid view can be customized using many properties.
*
* @psalm-import-type UrlCreator from BaseListView
* @psalm-type BodyRowAttributes = array|(Closure(array|object, BodyRowContext): array)|(array<array-key, Closure(array|object, BodyRowContext): mixed>)
* @psalm-type TBodyRowAttributes = array|(Closure(array|object, BodyRowContext): array)|(array<array-key, Closure(array|object, BodyRowContext): mixed>)
* @psalm-type TBeforeAfterRowClosure = Closure(array|object, array-key, int, GridView): (Tr|null)
*/
final class GridView extends BaseListView
{
/**
* @var Closure|null Callback executed after rendering each data row. The Result is appended after the row.
* @psalm-var TBeforeAfterRowClosure|null
*/
private Closure|null $afterRowCallback = null;

/**
* @var Closure|null Callback executed before rendering each data row. The Result is prepended before the row.
* @psalm-var TBeforeAfterRowClosure|null
*/
private Closure|null $beforeRowCallback = null;

Expand Down Expand Up @@ -103,7 +105,7 @@ final class GridView extends BaseListView

/**
* @var array|Closure HTML attributes for body rows.
* @psalm-var BodyRowAttributes
* @psalm-var TBodyRowAttributes
*/
private Closure|array $bodyRowAttributes = [];

Expand Down Expand Up @@ -322,6 +324,8 @@ public function keepPageOnSort(bool $enabled = true): self
* - or `null` if no additional row should be rendered
*
* @return self New instance with the after row callback.
*
* @psalm-param TBeforeAfterRowClosure|null $callback
*/
public function afterRow(Closure|null $callback): self
{
Expand Down Expand Up @@ -350,6 +354,8 @@ public function afterRow(Closure|null $callback): self
* - or `null` if no additional row should be rendered
*
* @return self New instance with the before row callback.
*
* @psalm-param TBeforeAfterRowClosure|null $callback
*/
public function beforeRow(Closure|null $callback): self
{
Expand Down Expand Up @@ -504,7 +510,7 @@ public function headerRowAttributes(array $attributes): self
* function (array|object $data, BodyRowContext $context): mixed
* ```
*
* @psalm-param BodyRowAttributes $attributes
* @psalm-param TBodyRowAttributes $attributes
*
* @return self New instance with the body row attributes.
*/
Expand Down Expand Up @@ -881,8 +887,7 @@ protected function renderItems(
$index = 0;
foreach ($items as $key => $value) {
if ($this->beforeRowCallback !== null) {
/** @var Tr|null $row */
$row = call_user_func($this->beforeRowCallback, $value, $key, $index, $this);
$row = ($this->beforeRowCallback)($value, $key, $index, $this);
if (!empty($row)) {
$rows[] = $row;
}
Expand All @@ -906,8 +911,7 @@ protected function renderItems(
$rows[] = Html::tr($bodyRowAttributes)->cells(...$tags);

if ($this->afterRowCallback !== null) {
/** @var Tr|null $row */
$row = call_user_func($this->afterRowCallback, $value, $key, $index, $this);
$row = ($this->afterRowCallback)($value, $key, $index, $this);
if (!empty($row)) {
$rows[] = $row;
}
Expand Down Expand Up @@ -998,7 +1002,7 @@ protected function getOrderProperties(): array
*
* @return array The prepared attributes.
*
* @psalm-param BodyRowAttributes $attributes
* @psalm-param TBodyRowAttributes $attributes
*/
private function prepareBodyRowAttributes(array|Closure $attributes, BodyRowContext $context): array
{
Expand Down

0 comments on commit 87f5ab2

Please sign in to comment.