diff --git a/src/Component/Table.php b/src/Component/Table.php index c6750bf..1bf7149 100644 --- a/src/Component/Table.php +++ b/src/Component/Table.php @@ -29,7 +29,6 @@ use Fohn\Ui\Js\JsRenderInterface; use Fohn\Ui\Js\Type\ArrayLiteral; use Fohn\Ui\Js\Type\Integer; -use Fohn\Ui\Js\Type\ObjectLiteral; use Fohn\Ui\Js\Type\Variable; use Fohn\Ui\Service\Ui; use Fohn\Ui\Tailwind\Tw; @@ -91,9 +90,6 @@ class Table extends View implements VueInterface public string $idColumnName = 'id'; - /** @var array An array of table action */ - protected array $actions = []; - protected ?Data $tableDataCb = null; protected string $payloadClass = Payload::class; @@ -218,23 +214,24 @@ public function addActionColumn(string $columnName, string $actionName, View\But ['template' => Ui::templateFromFile('vue-component/table/column/header-empty.html')] ); } + /** @var Column\Action $actionColumn */ + $actionColumn = Column\Action::factory(['columnHeader' => $header])->alignText('center'); + $this->addColumn( $columnName, - Column\Action::factory( - [ - 'columnHeader' => $header, - ] - )->alignText('center') + $actionColumn ); + } else { + /** @var Column\Action $actionColumn */ + $actionColumn = $this->getTableColumn($columnName); } + $button->setViewName($actionName); + static::bindVueAttr($button, 'disabled', "cell?.state?.{$actionName} || false"); static::bindVueEvent($button, $eventName, "executeRowAction('{$actionName}', cell)"); - $column = $this->getTableColumn($columnName); - $column->addView($button); - - $this->actions[$actionName] = JsFunction::arrow([Variable::set('cell')]); + $actionColumn->addView($button); - return $this->actions[$actionName]; + return $actionColumn->addRowActionFn($actionName, JsFunction::arrow([Variable::set('cell')])); } public function addFilter(Filter $filter, string $regionName = self::FILTER_REGION_NAME): Filter @@ -377,7 +374,6 @@ private function renderTableProps(): void $this->getTemplate()->setJs('columns', ArrayLiteral::set($this->getColumnsDefinition())); $this->getTemplate()->setJs('itemsPerPage', Integer::set($this->paginatorItemsPerPage)); $this->getTemplate()->setJs('itemsPerPages', ArrayLiteral::set($this->paginatorItemsPerPages)); - $this->getTemplate()->setJs('tableRowActions', ObjectLiteral::set($this->actions)); } private function getColumnsDefinition(): array diff --git a/src/Component/Table/Column/Action.php b/src/Component/Table/Column/Action.php index aab4b90..8aa891e 100644 --- a/src/Component/Table/Column/Action.php +++ b/src/Component/Table/Column/Action.php @@ -7,18 +7,71 @@ namespace Fohn\Ui\Component\Table\Column; +use Fohn\Ui\Component\Table; +use Fohn\Ui\Js\JsFunction; +use Fohn\Ui\Js\Type\ObjectLiteral; use Fohn\Ui\View; class Action extends Generic implements ActionInterface { + public const HOOK_DISABLED = self::class . '@action:enable'; + public string $defaultTemplate = 'vue-component/table/column/action.html'; + /** @var array Column action may contain more than one row action as js function. */ + protected array $rowActions = []; + + public function getRowActions(): array + { + return $this->rowActions; + } + + public function renderInTableTemplate(Table $table): void + { + parent::renderInTableTemplate($table); + + $table->getTemplate()->setJs('tableRowActions', ObjectLiteral::set($this->rowActions)); + } + protected function beforeHtmlRender(): void { $this->clearIdAttribute($this); parent::beforeHtmlRender(); } + /** + * Callback function in order to determine if a row action button should be disabled or not. + * The closure function will receive to record of the row being evaluated. + * The closure function should return true if disabling is need. + * + * Suppose you have a column name 'action' that contains an action name call 'edit' and + * you would like to disable the action base on row value. + * You can disable the button assign to the action name 'edit' this way: + * $table->getTableColumn('action')->disableActionName('edit', static function ($rowValue) { + * return true or false; + * }); + */ + public function disableActionName(string $actionName, \Closure $fx): self + { + $this->onHook(static::HOOK_DISABLED, static function (string $name, array $rowValue) use ($actionName, $fx): bool { + $return = false; + if ($actionName === $name) { + $return = $fx($rowValue); + } + + return $return; + }); + + return $this; + } + + public function addRowActionFn(string $name, JsFunction $fn): JsFunction + { + $this->rowActions[$name] = $fn; + + return $fn; + } + /** * Action column content is duplicate within each row of a table, * therefore, make sure that each Views inside column has the Id diff --git a/src/Component/Table/Result/Set.php b/src/Component/Table/Result/Set.php index ec11e05..c8ec8d8 100644 --- a/src/Component/Table/Result/Set.php +++ b/src/Component/Table/Result/Set.php @@ -46,9 +46,18 @@ public function outputData(array $columns, string $idColumnName): array $rowTws = $this->table->callHook(Table::HOOK_ROW_TW, HookFn::withTw([(string) $id, (object) $row])); $cells = []; foreach ($columns as $name => $column) { + $attr = []; $value = null; $cellTws = null; - if (!$column instanceof Column\ActionInterface) { + if ($column instanceof Column\Action) { + // @var Column\Action $column + // call hook function with row value and actionName as params. Callback must return a boolean. + foreach ($column->getRowActions() as $action => $fn) { + $attr[$action] = $column->callHook(Column\Action::HOOK_DISABLED, HookFn::withTypeFn(static function ($fn, $args): bool { + return $fn(...$args); + }, [$action, $row])); + } + } else { $value = $column->getDisplayValue($row[$name], $id); // call hook function with cell value as params. Callback must return a Tw object. $cellTws = $column->callHook(Column::HOOK_CELL_TW, HookFn::withTw([$row[$name]])); @@ -59,6 +68,7 @@ public function outputData(array $columns, string $idColumnName): array 'name' => $column->getColumnName(), 'value' => $value, 'css' => $cellTws !== null ? $cellTws->toString() : '', + 'state' => $attr, ]; } $results['rows'][] = ['id' => (string) $id, 'cells' => $cells, 'css' => $rowTws->toString()];