Test Failed
Pull Request — master (#127)
by
unknown
13:20
created

Collapse::renderCollapse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 8
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use Stringable;
8
use Yiisoft\Arrays\ArrayHelper;
9
use Yiisoft\Html\Html;
10
use function array_key_exists;
11
12
/**
13
 * Collapse renders an collapse bootstrap JavaScript component.
14
 *
15
 * For example
16
 *
17
 * $collapse =Collapse::widget()
18
 *      ->withTitle('Foo')
19
 *      ->withContent('Bar');
20
 *
21
 * echo $collapse->render();
22
 *
23
 * Or
24
 *
25
 * $collapse = $collapse->withToggle(false);
26
 *
27
 * echo '<p>' . $collapse->renderToggle() . '</p><div>Some other content</div>' . $collapse->render();
28
 */
29
final class Collapse extends AbstractToggleWidget
30
{
31
    private array $options = [];
32
    private array $bodyOptions = [
33
        'tag' => 'div',
34
    ];
35
    private string|Stringable $content = '';
36
    private bool $horizontal = false;
37
    private bool $collapsed = false;
38
    private ?string $tagName = null;
39
40
    public function getId(?string $suffix = '-collapse'): ?string
41
    {
42
        return $this->options['id'] ?? parent::getId($suffix);
43
    }
44
45
    protected function bsToggle(): string
46
    {
47
        return 'collapse';
48
    }
49
50
    public function withOptions(array $options): self
51
    {
52
        $new = clone $this;
53
        $new->options = $options;
54
55
        return $new;
56
    }
57
58
    public function withBodyOptions(array $options): self
59
    {
60
        $new = clone $this;
61
        $new->bodyOptions = $options;
62
63
        if (!array_key_exists('tag', $this->bodyOptions)) {
64
            $this->bodyOptions['tag'] = 'div';
65
        }
66
67
        return $new;
68
    }
69
70
    public function withContent(string|Stringable $content): self
71
    {
72
        $new = clone $this;
73
        $new->content = $content;
74
75
        return $new;
76
    }
77
78
    public function withHorizontal(bool $horizontal): self
79
    {
80
        $new = clone $this;
81
        $new->horizontal = $horizontal;
82
83
        return $new;
84
    }
85
86
    public function withCollapsed(bool $collapsed): self
87
    {
88
        $new = clone $this;
89
        $new->collapsed = $collapsed;
90
91
        return $new;
92
    }
93
94
    private function prepareOptions(): array
95
    {
96
        $options = $this->options;
97
        $options['id'] = $this->getId();
98
99
        $classNames = [
100
            'widget' => 'collapse',
101
        ];
102
103
        if ($this->horizontal) {
104
            $classNames[] = 'collapse-horizontal';
105
        }
106
107
        if ($this->collapsed) {
108
            $classNames[] = 'show';
109
        }
110
111
        Html::addCssClass($options, $classNames);
112
113
        return $options;
114
    }
115
116
    private function prepareBodyOptions(): array
117
    {
118
        $options = $this->bodyOptions;
119
        Html::addCssClass($options, ['widget' => 'card card-body']);
120
121
        return $options;
122
    }
123
124
    protected function prepareToggleOptions(): array
125
    {
126
        list($tagName, $options, $encode) = parent::prepareToggleOptions();
127
128
        $options['aria-expanded'] = $this->collapsed ? 'true' : 'false';
129
130
        return [$tagName, $options, $encode];
131
    }
132
133
    public function begin(): ?string
134
    {
135
        parent::begin();
136
137
        $options = $this->prepareOptions();
138
        $bodyOptions = $this->prepareBodyOptions();
139
        $this->tagName = ArrayHelper::remove($options, 'tag', 'div');
140
        $html = $this->renderToggle ? $this->renderToggle() : '';
141
        $html .= Html::openTag($this->tagName, $options);
0 ignored issues
show
Bug introduced by
It seems like $this->tagName can also be of type null; however, parameter $name of Yiisoft\Html\Html::openTag() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

141
        $html .= Html::openTag(/** @scrutinizer ignore-type */ $this->tagName, $options);
Loading history...
142
143
        if ($bodyTag = ArrayHelper::remove($bodyOptions, 'tag')) {
144
            $html .= Html::openTag($bodyTag, $bodyOptions);
145
        }
146
147
        return $html;
148
    }
149
150
    /**
151
     * @inheritDoc
152
     */
153
    public function render(): string
154
    {
155
        if ($tagName = $this->tagName) {
156
            $this->tagName = null;
157
158
            if (isset($this->bodyOptions['tag'])) {
159
                return Html::closeTag($this->bodyOptions['tag']) . Html::closeTag($tagName);
160
            }
161
162
            return Html::closeTag($tagName);
163
        }
164
165
        if ($this->renderToggle) {
166
            return $this->renderToggle() . $this->renderCollapse();
167
        }
168
169
        return $this->renderCollapse();
170
    }
171
172
    private function renderCollapse(): string
173
    {
174
        $options = $this->prepareOptions();
175
        $tagName = ArrayHelper::remove($options, 'tag', 'div');
176
177
        return Html::tag($tagName, $this->renderBody(), $options)
178
            ->encode(false)
179
            ->render();
180
    }
181
182
    private function renderBody(): string
183
    {
184
        $options = $this->prepareBodyOptions();
185
        $tagName = ArrayHelper::remove($options, 'tag', 'div');
186
        $encode = ArrayHelper::remove($options, 'encode');
187
188
        if ($tagName === null) {
189
            return $encode ? Html::encode($this->content) : (string) $this->content;
190
        }
191
192
        return Html::tag($tagName, $this->content, $options)
193
            ->encode($encode)
194
            ->render();
195
    }
196
}
197