1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Ui;
6:
7: use Atk4\Ui\Js\JsChain;
8: use Atk4\Ui\Js\JsExpressionable;
9:
10: /**
11: * Accordion is a View holding accordion sections.
12: *
13: * You can add static content to an accordion section or pass a callback
14: * for adding content dynamically.
15: */
16: class Accordion extends View
17: {
18: public $defaultTemplate = 'accordion.html';
19:
20: public $ui = 'accordion';
21:
22: /** @var array|string|null The CSS class for Fomantic-UI accordion type. */
23: public $type;
24:
25: /** @var array Settings as per Fomantic-UI accordion settings. */
26: public $settings = [];
27:
28: /** @var array A collection of AccordionSection in this Accordion. */
29: public $sections = [];
30:
31: /** @var int The AccordionSection index number to activate on load. */
32: public $activeSection = -1;
33:
34: /**
35: * Add an accordion section.
36: * You can add static View within your section or pass
37: * a callback for dynamic content.
38: *
39: * @param string $title
40: * @param \Closure(VirtualPage, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed, mixed): void $callback
41: * @param string $icon
42: *
43: * @return AccordionSection
44: */
45: public function addSection($title, \Closure $callback = null, $icon = 'dropdown')
46: {
47: $section = AccordionSection::addTo($this, ['title' => $title, 'icon' => $icon]);
48:
49: // if there is callback action, then use VirtualPage
50: if ($callback) {
51: $section->virtualPage = VirtualPage::addTo($section, ['ui' => '']);
52: $section->virtualPage->set($callback);
53: }
54:
55: $this->sections[] = $section;
56:
57: return $section;
58: }
59:
60: /**
61: * Activate or open an accordion section.
62: *
63: * @param AccordionSection $section the section to activate
64: */
65: public function activate($section): void
66: {
67: $this->activeSection = $this->getSectionIdx($section);
68: }
69:
70: /**
71: * @param AccordionSection $section
72: * @param bool $when
73: *
74: * @return JsChain
75: */
76: public function jsOpen($section, $when = false): JsExpressionable
77: {
78: return $this->jsBehavior('open', [$this->getSectionIdx($section)], $when);
79: }
80:
81: /**
82: * @param bool $when
83: *
84: * @return JsChain
85: */
86: public function jsCloseOthers($when = false): JsExpressionable
87: {
88: return $this->jsBehavior('close others', [], $when);
89: }
90:
91: /**
92: * @param AccordionSection $section
93: * @param bool $when
94: *
95: * @return JsChain
96: */
97: public function jsClose($section, $when = false): JsExpressionable
98: {
99: return $this->jsBehavior('close', [$this->getSectionIdx($section)], $when);
100: }
101:
102: /**
103: * @param AccordionSection $section
104: * @param bool $when
105: *
106: * @return JsChain
107: */
108: public function jsToggle($section, $when = false): JsExpressionable
109: {
110: return $this->jsBehavior('toggle', [$this->getSectionIdx($section)], $when);
111: }
112:
113: /**
114: * Return an accordion JS behavior command
115: * as in Fomantic-UI behavior for Accordion module.
116: * Ex: toggle an accordion from it's index value.
117: * $accordion->jsBehavior('toggle', 1).
118: *
119: * @param string $behavior the name of the behavior for the module
120: * @param bool $when
121: *
122: * @return JsChain
123: */
124: public function jsBehavior($behavior, array $args, $when = false): JsExpressionable
125: {
126: return $this->js($when)->accordion($behavior, ...$args);
127: }
128:
129: /**
130: * Return the index of an accordion section in collection.
131: *
132: * @return int
133: */
134: public function getSectionIdx(AccordionSection $section)
135: {
136: $idx = -1;
137: foreach ($this->sections as $k => $v) {
138: if ($v->name === $section->name) {
139: $idx = $k;
140:
141: break;
142: }
143: }
144:
145: return $idx;
146: }
147:
148: #[\Override]
149: protected function renderView(): void
150: {
151: if ($this->type) {
152: $this->addClass($this->type);
153: }
154:
155: // initialize top accordion only, otherwise nested accordion won't work
156: // https://github.com/fomantic/Fomantic-UI/issues/254
157: if ($this->getClosestOwner(AccordionSection::class) === null) {
158: $this->js(true)->accordion($this->settings);
159: }
160:
161: if ($this->activeSection > -1) {
162: $this->jsBehavior('open', [$this->activeSection], true);
163: }
164:
165: parent::renderView();
166: }
167: }
168: