Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce record specific enabling/disabling of actions in Grid/CRUD #874

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/CRUD.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public function setModel(\atk4\data\Model $m, $fields = null) : \atk4\data\Model
if ($this->useMenuActions) {
$this->addActionMenuItem($action);
} else {
$this->addAction($action);
$this->addActionButton($action);
}
}

Expand Down
30 changes: 15 additions & 15 deletions src/Grid.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ class Grid extends View
public $ipp = 50;

/**
* Calling addAction will add a new column inside $table, and will be re-used
* for next addAction().
* Calling addActionButton will add a new column inside $table, and will be re-used
* for next addActionButton().
*
* @var TableColumn\Actions
* @var TableColumn\ActionButtons
*/
public $actions = null;
public $actionButtons = null;

/**
* Calling addAction will add a new column inside $table with dropdown menu,
Expand Down Expand Up @@ -106,12 +106,12 @@ class Grid extends View
public $defaultTemplate = 'grid.html';

/**
* TableColumn\Action seed.
* Defines which Table Decorator to use for Actions.
* TableColumn\ActionButtons seed.
* Defines which Table Decorator to use for ActionButtons.
*
* @var string
*/
protected $actionDecorator = 'Actions';
protected $actionButtonsDecorator = 'ActionButtons';

/**
* TableColumn\ActionMenu seed.
Expand Down Expand Up @@ -153,7 +153,7 @@ public function init()
*/
public function setActionDecorator($seed)
{
$this->actionDecorator = $seed;
$this->actionButtonsDecorator = $seed;
}

/**
Expand Down Expand Up @@ -395,13 +395,13 @@ public function jsReload($args = [], $afterSuccess = null, $apiConfig = [])
*
* @return object
*/
public function addAction($button, $action = null, $confirm = false, $isDisabeld = false)
public function addActionButton($button, $action = null, $confirm = false, $isDisabeld = false)
{
if (!$this->actions) {
$this->actions = $this->table->addColumn(null, $this->actionDecorator);
if (!$this->actionButtons) {
$this->actionButtons = $this->table->addColumn(null, $this->actionButtonsDecorator);
}

return $this->actions->addAction($button, $action, $confirm, $isDisabeld);
return $this->actionButtons->addButton($button, $action, $confirm, $isDisabeld);
}

/**
Expand Down Expand Up @@ -545,11 +545,11 @@ public function addPopup($columnName, $popup = null, $icon = 'caret square down'
*/
public function addModalAction($button, $title, $callback, $args = [])
{
if (!$this->actions) {
$this->actions = $this->table->addColumn(null, 'Actions');
if (!$this->actionButtons) {
$this->actionButtons = $this->table->addColumn(null, 'Actions');
}

return $this->actions->addModal($button, $title, $callback, $this, $args);
return $this->actionButtons->addModal($button, $title, $callback, $this, $args);
}

/**
Expand Down
154 changes: 154 additions & 0 deletions src/TableColumn/ActionButtons.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php

namespace atk4\ui\TableColumn;

use atk4\core\FactoryTrait;

/**
* Formatting action buttons column.
*/
class ActionButtons extends Generic
{
use FactoryTrait;

public $buttons = [];
georgehristov marked this conversation as resolved.
Show resolved Hide resolved

/**
* Callbacks as defined in $action->enabled for evaluating row-specific if an action is enabled
*
* @var array
*/
protected $callbacks = [];

public function init()
{
parent::init();
$this->addClass('right aligned');
}

/**
* Adds a new button which will execute $callback when clicked.
*
* Returns button object
*
* @param View|string $button
* @param null|callable|atk4\data\UserAction\Generic $action
* @param bool $confirm
* @param bool $isDisabled
*
* @throws \atk4\core\Exception
* @throws \atk4\data\Exception
*
* @return object
*/
public function addButton($button, $action = null, $confirm = false, $isDisabled = false)
{
// If action is not specified, perhaps it is defined in the model
if (! $action) {
if (is_string($button)) {
$action = $this->table->model->getAction($button);
}
elseif ($button instanceof \atk4\data\UserAction\Generic){
$action = $button;
}

if ($action) {
$button = $action->caption;
}
}

$name = $this->name.'_button_'.(count($this->buttons) + 1);

if ($action instanceof \atk4\data\UserAction\Generic) {
$button = $action->ui['button'] ?? $button;

$confirm = $action->ui['confirm'] ?? $confirm;

$isDisabled = ! $action->enabled;

if (is_callable($action->enabled)) {
$this->callbacks[$name] = $action->enabled;
}
}

if (!is_object($button)) {
$button = $this->factory('Button', [$button, 'id' => false], 'atk4\ui');
}

if ($button->icon && !is_object($button->icon)) {
$button->icon = $this->factory('Icon', [$button->icon, 'id' => false], 'atk4\ui');
}

$button->app = $this->table->app;

$this->buttons[$name] = $button->addClass('{$_'.$name.'_disabled} compact b_'.$name);

if ($isDisabled) {
$button->addClass('disabled');
}
$this->table->on('click', '.b_'.$name, $action, [$this->table->jsRow()->data('id'), 'confirm' => $confirm]);

return $button;
}

/**
* Adds a new button which will open a modal dialog and dynamically
* load contents through $callback. Will pass a virtual page.
*/
georgehristov marked this conversation as resolved.
Show resolved Hide resolved
public function addModal($button, $title, $callback, $owner = null, $args = [])
{
$owner = $owner?: $this->owner->owner;

$modal = $owner->add(['Modal', compact('title')]);

$modal->observeChanges(); // adds scrollbar if needed

$modal->set(function ($t) use ($callback) {
call_user_func($callback, $t, $this->app->stickyGet($this->name));
});

return $this->addButton($button, $modal->show(array_merge([$this->name=>$this->owner->jsRow()->data('id')], $args)));
}

/**
* {@inheritdoc}
*/
public function getTag($position, $value, $attr = [])
{
if ($this->table->hasCollapsingCssActionColumn && $position === 'body') {
$attr['class'][] = 'collapsing';
}

return parent::getTag($position, $value, $attr);
}

public function getDataCellTemplate(\atk4\data\Field $f = null)
{
if (!$this->buttons) {
return '';
}

// render our buttons
$output = '';
foreach ($this->buttons as $button) {
$output .= $button->getHTML();
}

return '<div class="ui buttons">'.$output.'</div>';
}

public function getHTMLTags($row, $field)
{
$tags = [];
foreach ($this->callbacks as $name => $callback) {
// if action is enabled then do not set disabled class
if ($callback($row)) continue;

$tags['_'.$name.'_disabled'] = 'disabled';
}

return $tags;
}

// rest will be implemented for crud
}
84 changes: 55 additions & 29 deletions src/TableColumn/ActionMenu.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ class ActionMenu extends Generic
*
* @var array
*/
protected $actions = [];
protected $items = [];

/**
* Callbacks as defined in $action->enabled for evaluating row-specific if an action is enabled
*
* @var array
*/
protected $callbacks = [];

/**
* Dropdown label.
Expand Down Expand Up @@ -68,54 +75,60 @@ public function getTag($position, $value, $attr = [])
/**
* Add a menu item in Dropdown.
*
* @param View|string $item View
* @param null|callable $callback
* @param null|string $confirm
* @param bool $isDisabled
* @param View|string $item
* @param null|callable|atk4\data\UserAction\Generic $action
* @param null|string $confirm
* @param bool $isDisabled
*
* @throws \atk4\core\Exception
* @throws \atk4\data\Exception
*
* @return object|string
*/
public function addActionMenuItem($item, $callback = null, $confirm = null, $isDisabled = false)
public function addActionMenuItem($item, $action = null, $confirm = null, $isDisabled = false)
{
// If action is not specified, perhaps it is defined in the model
if (!$callback && is_string($item)) {
$model_action = $this->table->model->getAction($item);
if ($model_action) {
$isDisabled = !$model_action->enabled;
$callback = $model_action;
$item = $callback->caption;
if ($model_action->ui['confirm'] ?? null) {
$confirm = $model_action->ui['confirm'];
}
if (! $action) {
if (is_string($item)) {
$action = $this->table->model->getAction($item);
}
} elseif (!$callback && $item instanceof \atk4\data\UserAction\Generic) {
$isDisabled = !$item->enabled;
if ($item->ui['confirm'] ?? null) {
$confirm = $item->ui['confirm'];
elseif ($item instanceof \atk4\data\UserAction\Generic){
$action = $item;
}

if ($action) {
$item = $action->caption;
}
$callback = $item;
$item = $item->caption;
}

$name = $this->name.'_action_'.(count($this->actions) + 1);


$name = $this->name.'_action_'.(count($this->items) + 1);

if ($action instanceof \atk4\data\UserAction\Generic) {
$confirm = $action->ui['confirm'] ?? $confirm;

$isDisabled = ! $action->enabled;

if (is_callable($action->enabled)) {
$this->callbacks[$name] = $action->enabled;
}
}

if (!is_object($item)) {
$item = $this->factory('View', ['id' => false, 'ui' => 'item', 'content' => $item], 'atk4\ui');
}

$this->actions[] = $item;
$item->addClass('i_'.$name);
$this->items[] = $item;

$item->addClass('{$_'.$name.'_disabled} i_'.$name);

if ($isDisabled) {
$item->addClass('disabled');
}

// set executor context.
$context = (new jQuery())->closest('.ui.button');

$this->table->on('click', '.i_'.$name, $callback, [$this->table->jsRow()->data('id'), 'confirm' => $confirm, 'apiConfig' => ['stateContext' => $context]]);
$this->table->on('click', '.i_'.$name, $action, [$this->table->jsRow()->data('id'), 'confirm' => $confirm, 'apiConfig' => ['stateContext' => $context]]);

return $item;
}
Expand Down Expand Up @@ -145,13 +158,13 @@ public function getHeaderCellHTML(\atk4\data\Field $f = null, $value = null)
*/
public function getDataCellTemplate(\atk4\data\Field $f = null)
{
if (!$this->actions) {
if (!$this->items) {
return '';
}

// render our menus
$output = '';
foreach ($this->actions as $item) {
foreach ($this->items as $item) {
$output .= $item->getHTML();
}

Expand All @@ -164,4 +177,17 @@ public function getDataCellTemplate(\atk4\data\Field $f = null)

return $s;
}

public function getHTMLTags($row, $field)
{
$tags = [];
foreach ($this->callbacks as $name => $callback) {
// if action is enabled then do not set disabled class
if ($callback($row)) continue;

$tags['_'.$name.'_disabled'] = 'disabled';
}

return $tags;
}
}
Loading