1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Ui\Table\Column;
6:
7: use Atk4\Core\Factory;
8: use Atk4\Data\Field;
9: use Atk4\Data\Model;
10: use Atk4\Ui\Button;
11: use Atk4\Ui\Js\Jquery;
12: use Atk4\Ui\Js\JsExpressionable;
13: use Atk4\Ui\Modal;
14: use Atk4\Ui\Table;
15: use Atk4\Ui\UserAction\ExecutorInterface;
16: use Atk4\Ui\View;
17:
18: /**
19: * Formatting action buttons column.
20: *
21: * @phpstan-type JsCallbackSetClosure \Closure(Jquery, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed): (JsExpressionable|View|string|void)
22: */
23: class ActionButtons extends Table\Column
24: {
25: /** @var array Stores all the buttons that have been added. */
26: public $buttons = [];
27:
28: /** @var array<string, \Closure(Model): bool> Callbacks as defined in UserAction->enabled for evaluating row-specific if an action is enabled. */
29: protected $callbacks = [];
30:
31: #[\Override]
32: protected function init(): void
33: {
34: parent::init();
35:
36: $this->addClass('right aligned');
37: }
38:
39: /**
40: * Adds a new button which will execute $callback when clicked.
41: *
42: * @param string|array|View $button
43: * @param JsExpressionable|JsCallbackSetClosure|ExecutorInterface $action
44: * @param bool|\Closure(Model): bool $isDisabled
45: *
46: * @return View
47: */
48: public function addButton($button, $action = null, string $confirmMsg = '', $isDisabled = false)
49: {
50: $name = $this->name . '_button_' . (count($this->buttons) + 1);
51:
52: if (!is_object($button)) {
53: if (is_string($button)) {
54: $button = [1 => $button];
55: }
56:
57: $button = Factory::factory([Button::class], Factory::mergeSeeds($button, ['name' => false]));
58: }
59:
60: if ($isDisabled === true) {
61: $button->addClass('disabled');
62: } elseif ($isDisabled !== false) {
63: $this->callbacks[$name] = $isDisabled;
64: }
65:
66: $button->setApp($this->table->getApp());
67:
68: $this->buttons[$name] = $button->addClass('{$_' . $name . '_disabled} compact b_' . $name);
69:
70: $this->table->on('click', '.b_' . $name, $action, [$this->table->jsRow()->data('id'), 'confirm' => $confirmMsg]);
71:
72: return $button;
73: }
74:
75: /**
76: * Adds a new button which will open a modal dialog and dynamically
77: * load contents through $callback. Will pass a virtual page.
78: *
79: * @param string|array|View $button
80: * @param string|array $defaults modal title or modal defaults array
81: * @param \Closure(View, string|null): void $callback
82: * @param View $owner
83: * @param array $args
84: *
85: * @return View
86: */
87: public function addModal($button, $defaults, \Closure $callback, $owner = null, $args = [])
88: {
89: if ($owner === null) { // TODO explicit owner should not be needed
90: $owner = $this->getOwner()->getOwner();
91: }
92:
93: if (is_string($defaults)) {
94: $defaults = ['title' => $defaults];
95: }
96:
97: $modal = Modal::addTo($owner, $defaults);
98:
99: $modal->set(function (View $t) use ($callback) {
100: $callback($t, $t->stickyGet($this->name));
101: });
102:
103: return $this->addButton($button, $modal->jsShow(array_merge([$this->name => $this->getOwner()->jsRow()->data('id')], $args)));
104: }
105:
106: #[\Override]
107: public function getTag(string $position, $value, $attr = []): string
108: {
109: if ($this->table->hasCollapsingCssActionColumn && $position === 'body') {
110: $attr['class'][] = 'collapsing';
111: }
112:
113: return parent::getTag($position, $value, $attr);
114: }
115:
116: #[\Override]
117: public function getDataCellTemplate(Field $field = null): string
118: {
119: if (count($this->buttons) === 0) {
120: return '';
121: }
122:
123: // render our buttons
124: $outputHtml = '';
125: foreach ($this->buttons as $button) {
126: $outputHtml .= $button->getHtml();
127: }
128:
129: return $this->getApp()->getTag('div', ['class' => 'ui buttons'], [$outputHtml]);
130: }
131:
132: #[\Override]
133: public function getHtmlTags(Model $row, ?Field $field): array
134: {
135: $tags = [];
136: foreach ($this->callbacks as $name => $callback) {
137: // if action is enabled then do not set disabled class
138: if ($callback($row)) {
139: continue;
140: }
141:
142: $tags['_' . $name . '_disabled'] = 'disabled';
143: }
144:
145: return $tags;
146: }
147:
148: // rest will be implemented for crud
149: }
150: