diff --git a/demos/_includes/CustomCard.php b/demos/_includes/CustomCard.php new file mode 100644 index 0000000000..2f53508c79 --- /dev/null +++ b/demos/_includes/CustomCard.php @@ -0,0 +1,25 @@ +btnContainer) { + // $this->btnContainer = $this->addExtraContent(new View(['ui' => 'buttons'])); + $this->btnContainer = $this->add(new View(['ui' => 'buttons bottom attached'])); // attach buttons to bottom + $this->getButtonContainer()->addClass('wrapping'); + if ($this->hasFluidButton) { + $this->getButtonContainer()->addClass('fluid'); + } + } + + return $this->btnContainer; + } +} diff --git a/demos/collection/card-deck.php b/demos/collection/card-deck.php index e0b3bee9de..77c918b7e6 100644 --- a/demos/collection/card-deck.php +++ b/demos/collection/card-deck.php @@ -6,15 +6,17 @@ use Atk4\Data\Model; use Atk4\Ui\Button; +use Atk4\Ui\Card; use Atk4\Ui\CardDeck; use Atk4\Ui\Form; use Atk4\Ui\Header; use Atk4\Ui\UserAction\ExecutorFactory; +use Atk4\Ui\View; /** @var \Atk4\Ui\App $app */ require_once __DIR__ . '/../init-app.php'; -Header::addTo($app, ['Card Deck', 'size' => 1, 'subHeader' => 'Card can be display in a deck, also using model action.']); +Header::addTo($app, ['Card Deck - Booking', 'size' => 1, 'subHeader' => 'Card can be display in a deck, also using model action.']); $countries = new Country($app->db); $countries->addCalculatedField('cost', ['type' => 'atk4_money', 'expr' => function (Country $country) { @@ -50,3 +52,20 @@ $deck = CardDeck::addTo($app, ['noRecordScopeActions' => ['request_info'], 'singleScopeActions' => ['book']]); $deck->setModel($countries, ['cost'], [$countries->fieldName()->iso, $countries->fieldName()->iso3]); + +// Another Card Deck example below +Header::addTo($app, ['Card Deck - Editable', 'size' => 1, 'subHeader' => 'Cards can also use custom templates and have full editing support like Grid.']); + +$countries = new Country($app->db); +$countries->addCalculatedField('iso_lower', ['expr' => function (Country $m) {return strtolower($m->get($m->fieldName()->iso)); }]); +$deck = CardDeck::addTo($app, [ + 'cardHolder' => [View::class, 'ui' => 'cards six'], + 'ipp' => 6 * 2, + 'card' => [ + CustomCard::class, + 'ui' => 'card atk-card blue', + 'addFields' => false, + 'defaultTemplate' => __DIR__ . '/templates/card.html', + ], +]); +$deck->setModel($countries); diff --git a/demos/collection/templates/card.html b/demos/collection/templates/card.html new file mode 100644 index 0000000000..a98cd9696b --- /dev/null +++ b/demos/collection/templates/card.html @@ -0,0 +1,11 @@ +
+
+ + + +
+ {$Image} + {$Content} + {$Section} + {$ExtraContent} +
diff --git a/src/Card.php b/src/Card.php index efb7399b67..8e29911c4c 100644 --- a/src/Card.php +++ b/src/Card.php @@ -64,15 +64,21 @@ class Card extends View /** @var View|null The button Container for Button */ public $btnContainer; + /** @var bool Do we want to add fields in section or just set values in template */ + public $addFields = true; + /** @var bool Display model field as table inside card holder content */ public $useTable = false; - /** @var bool Use Field label with value data. */ + /** @var bool Use Field label with value data */ public $useLabel = false; - /** @var string Default executor class. */ + /** @var string Default executor class */ public $executor = UserAction\ModalExecutor::class; + /** @var View|null Header view, useful to add additional views to it, icons for example */ + public $headerView; + protected function init(): void { parent::init(); @@ -166,7 +172,18 @@ public function addContent(View $view) } /** - * If Fields are past with $model that field will be add + * Returns array of names of fields to automatically include them in view. + * This includes all editable or visible fields of the model. + * + * @return array + */ + protected function getModelFields(Model $model) + { + return array_keys($model->getFields(['editable', 'visible'])); + } + + /** + * If Fields are past with $model that field will be added * to the main section of this card. * * @param array|null $fields @@ -178,13 +195,17 @@ public function setModel(Model $entity, array $fields = null): void parent::setModel($entity); if ($fields === null) { - $fields = array_keys($this->model->getFields(['editable', 'visible'])); + $fields = $this->getModelFields($entity); } $this->template->trySet('dataId', (string) $this->model->getId()); - View::addTo($this->getSection(), [$entity->getTitle(), 'class.header' => true]); - $this->getSection()->addFields($entity, $fields, $this->useLabel, $this->useTable); + $this->headerView = View::addTo($this->getSection(), [$entity->getTitle(), 'class.header' => true]); + if ($this->addFields) { + $this->getSection()->addFields($entity, $fields, $this->useLabel, $this->useTable); + } else { + $this->template->set($this->model); + } } /** diff --git a/src/CardDeck.php b/src/CardDeck.php index cb1cb5db64..f7236cecdf 100644 --- a/src/CardDeck.php +++ b/src/CardDeck.php @@ -22,7 +22,7 @@ class CardDeck extends View public $defaultTemplate = 'card-deck.html'; - /** @var class-string Card type inside this deck. */ + /** @var class-string|array Card type inside this deck. */ public $card = Card::class; /** @var bool Whether card should use table display or not. */ @@ -85,10 +85,10 @@ class CardDeck extends View ]; /** @var array A collection of menu button added in Menu. */ - private $menuActions = []; + protected $menuActions = []; /** @var string|null The current search query string. */ - private $query; + protected $query; protected function init(): void { @@ -99,11 +99,7 @@ protected function init(): void $this->container = $this->add($this->container); 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->addMenuBar(); } $this->cardHolder = $this->container->add($this->cardHolder); @@ -114,6 +110,15 @@ protected function init(): void } } + protected function addMenuBar(): void + { + $this->menu = $this->add(Factory::factory([Menu::class, 'activateOnClick' => false], $this->menu), 'Menu'); + + if ($this->search !== false) { + $this->addMenuBarSeach(); + } + } + protected function addMenuBarSeach(): void { $view = View::addTo($this->menu->addMenuRight()->addItem()->setElement('div')); @@ -147,7 +152,7 @@ public function setModel(Model $model, array $fields = null, array $extra = null if ($count) { foreach ($this->model as $m) { /** @var Card */ - $c = $this->cardHolder->add(Factory::factory([$this->card], ['useLabel' => $this->useLabel, 'useTable' => $this->useTable])); + $c = $this->cardHolder->add(Factory::factory((array) $this->card, ['useLabel' => $this->useLabel, 'useTable' => $this->useTable])); $c->setModel($m, $fields); if ($extra) { $c->addExtraFields($m, $extra, $this->extraGlue); @@ -262,7 +267,7 @@ protected function jsCreateNotifier(Model\UserAction $action, string $msg = null * * @return mixed */ - private function getReloadArgs() + protected function getReloadArgs() { $args = []; if ($this->paginator !== false) { @@ -278,7 +283,7 @@ private function getReloadArgs() /** * Return proper action need to setup menu or action column. */ - private function getModelActions(string $appliesTo): array + protected function getModelActions(string $appliesTo): array { if ($appliesTo === Model\UserAction::APPLIES_TO_SINGLE_RECORD && $this->singleScopeActions !== []) { $actions = array_map(fn ($v) => $this->model->getUserAction($v), $this->singleScopeActions); diff --git a/src/CardSection.php b/src/CardSection.php index 083667b815..cb8d3cf6df 100644 --- a/src/CardSection.php +++ b/src/CardSection.php @@ -64,15 +64,15 @@ public function addFields(Model $model, array $fields, bool $useLabel = false, b /** * Add fields label and value to section. */ - private function addSectionFields(Model $model, array $fields, bool $useLabel = false): void + protected function addSectionFields(Model $model, array $fields, bool $useLabel = false): void { foreach ($fields as $field) { if ($model->titleField === $field) { continue; } - $label = $model->getField($field)->getCaption(); $value = $this->getApp()->uiPersistence->typecastSaveField($model->getField($field), $model->get($field)); if ($useLabel) { + $label = $model->getField($field)->getCaption(); $value = $label . $this->glue . $value; } @@ -85,7 +85,7 @@ private function addSectionFields(Model $model, array $fields, bool $useLabel = /** * Add field into section using a CardTable View. */ - private function addTableSection(Model $model, array $fields): void + protected function addTableSection(Model $model, array $fields): void { $cardTable = CardTable::addTo($this, ['class' => $this->tableClass]); $cardTable->setModel($model, $fields); diff --git a/src/HtmlTemplate.php b/src/HtmlTemplate.php index 0a3a2480d4..62a6d92f8f 100644 --- a/src/HtmlTemplate.php +++ b/src/HtmlTemplate.php @@ -157,7 +157,7 @@ protected function _setOrAppend($tag, string $value = null, bool $encodeHtml = t // in this case we don't throw exception if tags don't exist if (is_array($tag) && $value === null) { foreach ($tag as $k => $v) { - $this->_setOrAppend($k, $v, $encodeHtml, $append, false); + $this->_setOrAppend($k, (string) $v, $encodeHtml, $append, false); } return;