Skip to content

Commit

Permalink
Unify CardDeck menu to use native Menu (#1965)
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Jan 10, 2023
1 parent 3b7eeb6 commit 4e1ce7a
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 124 deletions.
5 changes: 1 addition & 4 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,6 @@ parameters:
-
path: 'src/CardDeck.php'
message: '~^Call to an undefined method Atk4\\Ui\\AbstractView::addClass\(\)\.$~'
-
path: 'src/CardDeck.php'
message: '~^Call to an undefined method Atk4\\Ui\\AbstractView::on\(\)\.$~'
-
path: 'src/CardDeck.php'
message: '~^Call to an undefined method Atk4\\Ui\\AbstractView&Atk4\\Ui\\UserAction\\ExecutorInterface::onHook\(\)\.$~'
Expand Down Expand Up @@ -287,7 +284,7 @@ parameters:
message: '~^Property Atk4\\Ui\\CardDeck::\$container \(Atk4\\Ui\\View\|null\) does not accept Atk4\\Ui\\AbstractView\.$~'
-
path: 'src/CardDeck.php'
message: '~^Property Atk4\\Ui\\CardDeck::\$menu \(array\|false\|null\) does not accept Atk4\\Ui\\AbstractView\.$~'
message: '~^Property Atk4\\Ui\\CardDeck::\$menu \(Atk4\\Ui\\Menu\|false\) does not accept Atk4\\Ui\\AbstractView\.$~'
-
path: 'src/CardDeck.php'
message: '~^Property Atk4\\Ui\\CardDeck::\$search \(array\|Atk4\\Ui\\VueComponent\\ItemSearch\|false\) does not accept Atk4\\Ui\\AbstractView\.$~'
Expand Down
143 changes: 28 additions & 115 deletions src/CardDeck.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Atk4\Core\Factory;
use Atk4\Data\Model;
use Atk4\Ui\Js\JsExpressionable;
use Atk4\Ui\Js\JsFunction;
use Atk4\Ui\Js\JsToast;
use Atk4\Ui\UserAction\ExecutorFactory;
use Atk4\Ui\UserAction\ExecutorInterface;
Expand Down Expand Up @@ -38,7 +37,7 @@ class CardDeck extends View
public $useAction = true;

/** @var View|null The container view. The view that is reload when page or data changed. */
public $container = [View::class, 'ui' => 'basic segment'];
public $container = [View::class, 'ui' => 'vertical segment'];

/** @var View The view containing Cards. */
public $cardHolder = [View::class, 'ui' => 'cards'];
Expand All @@ -49,17 +48,11 @@ class CardDeck extends View
/** @var int The number of cards to be displayed per page. */
public $ipp = 9;

/** @var array|false|null A menu seed for displaying button inside. */
public $menu = [View::class, 'ui' => 'stackable grid'];
/** @var Menu|false Will be initialized to Menu object, however you can set this to false to disable menu. */
public $menu;

/** @var array|false|VueComponent\ItemSearch */
public $search = [VueComponent\ItemSearch::class, 'ui' => 'ui compact basic segment'];

/** @var View|null A view container for buttons. Added into menu when menu is set. */
private $btns;

/** @var string Button css class for menu. */
public $menuBtnStyle = 'primary';
/** @var array|VueComponent\ItemSearch|false */
public $search = [VueComponent\ItemSearch::class];

/** @var array Default notifier to perform when model action is successful * */
public $notifyDefault = [JsToast::class, 'settings' => ['displayTime' => 5000]];
Expand Down Expand Up @@ -99,8 +92,12 @@ protected function init(): void

$this->container = $this->add($this->container);

if ($this->menu !== false) {
$this->addMenuBar();
if ($this->menu !== false && !is_object($this->menu)) {
$this->menu = $this->add(Factory::factory([Menu::class, 'activateOnClick' => false], $this->menu), 'Menu');

if ($this->search !== false) {
$this->addMenuBarSeach();
}
}

$this->cardHolder = $this->container->add($this->cardHolder);
Expand All @@ -111,22 +108,13 @@ protected function init(): void
}
}

/**
* Add menu bar view to CardDeck.
*/
protected function addMenuBar(): void
protected function addMenuBarSeach(): void
{
$this->menu = $this->add(Factory::factory($this->menu), 'Menu');

$left = View::addTo($this->menu, ['ui' => $this->search !== false ? 'twelve wide column' : 'sixteen wide column']);
$this->btns = View::addTo($left, ['ui' => 'buttons']);
$view = View::addTo($this->menu->addMenuRight()->addItem()->setElement('div'));

if ($this->search !== false) {
$right = View::addTo($this->menu, ['ui' => 'four wide column']);
$this->search = $right->add(Factory::factory($this->search, ['context' => $this->container]));
$this->search->reload = $this->container;
$this->query = $this->stickyGet($this->search->queryArg);
}
$this->search = $view->add(Factory::factory($this->search, ['context' => $this->container]));
$this->search->reload = $this->container;
$this->query = $this->stickyGet($this->search->queryArg);
}

/**
Expand Down Expand Up @@ -171,24 +159,26 @@ public function setModel(Model $model, array $fields = null, array $extra = null
if ($this->useAction && $this->menu) {
foreach ($this->getModelActions(Model\UserAction::APPLIES_TO_NO_RECORDS) as $k => $action) {
$executor = $this->initActionExecutor($action);
$this->menuActions[$k]['btn'] = $this->addExecutorMenuButton($executor);
$this->menuActions[$k]['btn'] = $this->menu->addItem(
$this->getExecutorFactory()->createTrigger($action, ExecutorFactory::MENU_ITEM)
);
$this->menuActions[$k]['executor'] = $executor;
}
}

$this->setItemsAction();
}

/**
* Reset Menu button js event when reloading occur in order
* to have their arguments always in sync after container reload.
* Setup js for firing menu action - copied from Crud - TODO deduplicate.
*/
protected function applyReload(): void
protected function setItemsAction(): void
{
foreach ($this->menuActions as $menuAction) {
$ex = $menuAction['executor'];
if ($ex instanceof UserAction\JsExecutorInterface) {
$this->container->js(true, $menuAction['btn']->js()->off('click'));
$this->container->js(true, $menuAction['btn']->js()->on('click', new JsFunction([], $ex->jsExecute($this->getReloadArgs()))));
}
foreach ($this->menuActions as $item) {
// hack - render executor action via MenuItem::on() into container
$item['btn']->on('click.atk_crud_item', $item['executor']);
$jsAction = array_pop($item['btn']->_jsActions['click.atk_crud_item']);
$this->container->js(true, $jsAction);
}
}

Expand Down Expand Up @@ -324,83 +314,6 @@ private function getReloadArgs()
return $args;
}

/**
* Add button for executing Model user action in deck main menu.
*/
protected function addExecutorMenuButton(ExecutorInterface $executor): AbstractView
{
$defaults = [];

$args = $this->getReloadArgs();
if ($args) {
$defaults['args'] = $args;
}

$btn = $this->btns->add($this->getExecutorFactory()->createTrigger($executor->getAction(), ExecutorFactory::CARD_BUTTON));
if ($executor->getAction()->enabled === false) {
$btn->addClass('disabled');
}

$btn->on('click', $executor, $defaults);

return $btn;
}

/**
* Add button to menu bar on top of deck card.
*
* @param Button|string|array $button a button object, a model action or a string representing a model action
* @param JsExpressionable|\Closure|null $callback an model action, js expression or callback function
* @param string|array $confirm A confirmation string or View::on method defaults when passed has an array,
*
* @return mixed
*/
public function addMenuButton($button, $callback = null, $confirm = null, bool $isDisabled = false, array $args = null)
{
$defaults = [];

if ($confirm) {
$defaults['confirm'] = $confirm;
}

if ($args) {
$defaults['args'] = $args;
}

if (!is_object($button)) {
if (is_string($button)) {
$button = [$button, 'ui' => 'button ' . $this->menuBtnStyle];
}
$button = Factory::factory([Button::class], $button);
}

if ($button->icon && !is_object($button->icon)) {
$button->icon = Factory::factory([Icon::class], $button->icon);
}

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

$btn = $this->btns->add($button);
$btn->on('click', $callback, $defaults);

return $btn;
}

protected function renderView(): void
{
if (($this->menu && count($this->menuActions) > 0) || $this->search !== false) {
View::addTo($this, ['ui' => 'divider'], ['Divider']);
}

if ($this->container->name === ($_GET['__atk_reload'] ?? null)) {
$this->applyReload();
}

parent::renderView();
}

/**
* Return proper action need to setup menu or action column.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Crud.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ protected function getNotifier(string $msg = null)
*/
protected function setItemsAction(): void
{
foreach ($this->menuItems as $k => $item) {
foreach ($this->menuItems as $item) {
// hack - render executor action via MenuItem::on() into container
$item['item']->on('click.atk_crud_item', $item['executor']);
$jsAction = array_pop($item['item']->_jsActions['click.atk_crud_item']);
Expand Down
4 changes: 1 addition & 3 deletions src/Grid.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ protected function init(): void
$this->sortTrigger = $this->name . '_sort';
}

// if menu not disabled ot not already assigned as existing object
if ($this->menu !== false && !is_object($this->menu)) {
$this->menu = $this->add(Factory::factory([Menu::class, 'activateOnClick' => false], $this->menu), 'Menu');
}
Expand Down Expand Up @@ -334,8 +333,7 @@ public function addQuickSearch($fields = [], $hasAutoQuery = false): void
throw new Exception('Unable to add QuickSearch without Menu');
}

$view = View::addTo($this->menu
->addMenuRight()->addItem()->setElement('div'));
$view = View::addTo($this->menu->addMenuRight()->addItem()->setElement('div'));

$this->quickSearch = JsSearch::addTo($view, ['reload' => $this->container, 'autoQuery' => $hasAutoQuery]);
$q = $this->stickyGet($this->quickSearch->name . '_q') ?? '';
Expand Down
2 changes: 1 addition & 1 deletion tests-behat/card-deck.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: CardDeck
Given I am on "_unit-test/card-deck.php"

Scenario: add
Then I press button "Add"
Then I press menu button "Add Country" using selector ".atk-card-deck .ui.menu"
Then I fill in "atk_fp_country__name" with "Test"
Then I fill in "atk_fp_country__iso" with "TT"
Then I fill in "atk_fp_country__iso3" with "TTT"
Expand Down

0 comments on commit 4e1ce7a

Please sign in to comment.