Passed
Push — master ( 5f6e57...34e66c )
by Sergei
05:58 queued 03:17
created

Offcanvas::renderCloseButton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 9
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 13
ccs 10
cts 10
cp 1
crap 1
rs 9.9666
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap5;
6
7
use Yiisoft\Arrays\ArrayHelper;
8
use Yiisoft\Html\Html;
9
10
final class Offcanvas extends AbstractCloseButtonWidget
11
{
12
    public const PLACEMENT_TOP = 'offcanvas-top';
13
    public const PLACEMENT_END = 'offcanvas-end';
14
    public const PLACEMENT_BOTTOM = 'offcanvas-bottom';
15
    public const PLACEMENT_START = 'offcanvas-start';
16
17
    private array $options = [];
18
    private array $headerOptions = [];
19
    private array $titleOptions = [];
20
    private array $bodyOptions = [];
21
    private ?string $title = null;
22
    private string $placement = self::PLACEMENT_START;
23
    private bool $scroll = false;
24
    private bool $withoutBackdrop = false;
25
    protected bool $renderToggle = false;
26
27 19
    public function getId(?string $suffix = '-offcanvas'): ?string
28
    {
29 19
        return $this->options['id'] ?? parent::getId($suffix);
30
    }
31
32 19
    protected function toggleComponent(): string
33
    {
34 19
        return 'offcanvas';
35
    }
36
37
    /**
38
     * Enable/disable body scroll when offcanvas show
39
     *
40
     * @link https://getbootstrap.com/docs/5.1/components/offcanvas/#backdrop
41
     */
42 1
    public function scroll(bool $scroll = true): self
43
    {
44 1
        $new = clone $this;
45 1
        $new->scroll = $scroll;
46
47 1
        return $new;
48
    }
49
50
    /**
51
     * Enable/disable offcanvas backdrop
52
     *
53
     * @link https://getbootstrap.com/docs/5.1/components/offcanvas/#backdrop
54
     */
55 5
    public function withoutBackdrop(bool $withoutBackdrop = true): self
56
    {
57 5
        $new = clone $this;
58 5
        $new->withoutBackdrop = $withoutBackdrop;
59
60 5
        return $new;
61
    }
62
63
    /**
64
     * Set placement for opened offcanvas
65
     *
66
     * @link https://getbootstrap.com/docs/5.1/components/offcanvas/#placement
67
     */
68 4
    public function placement(string $placement): self
69
    {
70 4
        $new = clone $this;
71 4
        $new->placement = $placement;
72
73 4
        return $new;
74
    }
75
76
    /**
77
     * The HTML attributes for the widget container tag. The following special options are recognized.
78
     *
79
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
80
     */
81 5
    public function options(array $options): self
82
    {
83 5
        $new = clone $this;
84 5
        $new->options = $options;
85
86 5
        return $new;
87
    }
88
89
    /**
90
     * The HTML attributes for the widget header tag. The following special options are recognized.
91
     *
92
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
93
     */
94 1
    public function headerOptions(array $options): self
95
    {
96 1
        $new = clone $this;
97 1
        $new->headerOptions = $options;
98
99 1
        return $new;
100
    }
101
102
    /**
103
     * Set/remove offcanvas title
104
     */
105 14
    public function title(?string $title): self
106
    {
107 14
        $new = clone $this;
108 14
        $new->title = $title;
109
110 14
        return $new;
111
    }
112
113
    /**
114
     * The HTML attributes for the widget title tag. The following special options are recognized.
115
     *
116
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
117
     */
118 1
    public function titleOptions(array $options): self
119
    {
120 1
        $new = clone $this;
121 1
        $new->titleOptions = $options;
122
123 1
        return $new;
124
    }
125
126
    /**
127
     * The HTML attributes for the widget body tag. The following special options are recognized.
128
     *
129
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
130
     */
131 1
    public function bodyOptions(array $options): self
132
    {
133 1
        $new = clone $this;
134 1
        $new->bodyOptions = $options;
135
136 1
        return $new;
137
    }
138
139 18
    public function begin(): string
140
    {
141 18
        parent::begin();
142
143 18
        $options = $this->options;
144 18
        $bodyOptions = $this->bodyOptions;
145 18
        $tag = ArrayHelper::remove($options, 'tag', 'div');
146 18
        $bodyTag = ArrayHelper::remove($bodyOptions, 'tag', 'div');
147
148 18
        Html::addCssClass($options, ['widget' => 'offcanvas', 'placement' => $this->placement]);
149 18
        Html::addCssClass($bodyOptions, ['widget' => 'offcanvas-body']);
150
151 18
        $options['id'] = $this->getId();
152 18
        $options['tabindex'] = -1;
153
154 18
        if (!empty($this->title)) {
155 13
            if (isset($this->titleOptions['id'])) {
156
                $options['aria-labelledby'] = $this->titleOptions['id'];
157 13
            } elseif ($options['id']) {
158 13
                $options['aria-labelledby'] = $options['id'] . '-title';
159
            }
160
        }
161
162 18
        if ($this->scroll) {
163 1
            $options['data-bs-scroll'] = 'true';
164
        }
165
166 18
        if ($this->withoutBackdrop) {
167 5
            $options['data-bs-backdrop'] = 'false';
168
        }
169
170 18
        if ($this->theme) {
171 4
            $options['data-bs-theme'] = $this->theme;
172
        }
173
174 18
        $html = $this->renderToggle ? $this->renderToggle() : '';
175 18
        $html .= Html::openTag($tag, $options);
176 18
        $html .= $this->renderHeader();
177 18
        $html .= Html::openTag($bodyTag, $bodyOptions);
178
179 18
        return $html;
180
    }
181
182 18
    public function render(): string
183
    {
184 18
        $tag = $this->options['tag'] ?? 'div';
185 18
        $bodyTag = $this->bodyOptions['tag'] ?? 'div';
186
187 18
        return Html::closeTag($bodyTag) . Html::closeTag($tag);
188
    }
189
190
    /**
191
     * Renders offcanvas header.
192
     *
193
     * @return string the rendering header.
194
     */
195 18
    private function renderHeader(): string
196
    {
197 18
        $options = $this->headerOptions;
198 18
        $tag = ArrayHelper::remove($options, 'tag', 'header');
199
200 18
        Html::addCssClass($options, ['widget' => 'offcanvas-header']);
201
202 18
        $title = (string) $this->renderTitle();
203 18
        $closeButton = $this->renderCloseButton();
204
205 18
        return Html::tag($tag, $title . $closeButton, $options)
206 18
            ->encode(false)
207 18
            ->render();
208
    }
209
210
    /**
211
     * Renders offcanvas title.
212
     *
213
     * @return string|null the rendering header.
214
     */
215 18
    private function renderTitle(): ?string
216
    {
217 18
        if ($this->title === null) {
218 5
            return null;
219
        }
220
221 14
        $options = $this->titleOptions;
222 14
        $tag = ArrayHelper::remove($options, 'tag', 'h5');
223 14
        $encode = ArrayHelper::remove($options, 'encode');
224
225 14
        Html::addCssClass($options, ['offcanvas-title']);
226
227 14
        if (!isset($options['id']) && $id = $this->getId()) {
228 14
            $options['id'] = $id . '-title';
229
        }
230
231 14
        return Html::tag($tag, $this->title, $options)
232 14
            ->encode($encode)
233 14
            ->render();
234
    }
235
}
236