Carousel   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 223
Duplicated Lines 0 %

Test Coverage

Coverage 75.34%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 75
c 1
b 0
f 0
dl 0
loc 223
ccs 55
cts 73
cp 0.7534
rs 10
wmc 22

10 Methods

Rating   Name   Duplication   Size   Complexity  
A crossfade() 0 5 1
A showIndicators() 0 5 1
A renderItems() 0 9 2
A options() 0 5 1
A controls() 0 5 1
A renderIndicators() 0 17 4
A renderItem() 0 29 5
A renderControls() 0 21 3
A items() 0 5 1
A run() 0 21 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bootstrap4;
6
7
use JsonException;
8
use RuntimeException;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Html\Html;
11
12
use function count;
13
use function implode;
14
use function is_string;
15
16
/**
17
 * Carousel renders a carousel bootstrap javascript component.
18
 *
19
 * For example:
20
 *
21
 * ```php
22
 * echo Carousel::widget()
23
 *     ->items([
24
 *         // the item contains only the image
25
 *         '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-01.jpg"/>',
26
 *         // equivalent to the above
27
 *         ['content' => '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-02.jpg"/>'],
28
 *         // the item contains both the image and the caption
29
 *         [
30
 *             'content' => '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-03.jpg"/>',
31
 *             'caption' => '<h4>This is title</h4><p>This is the caption text</p>',
32
 *             'captionOptions' => ['class' => ['d-none', 'd-md-block']],
33
 *             'options' => [...],
34
 *         ],
35
 *     ]);
36
 * ```
37
 */
38
class Carousel extends Widget
39
{
40
    private ?array $controls = [
41
        '<span class="carousel-control-prev-icon" aria-hidden="true"></span><span class="sr-only">Previous</span>',
42
        '<span class="carousel-control-next-icon" aria-hidden="true"></span><span class="sr-only">Next</span>',
43
    ];
44
    private bool $showIndicators = true;
45
    private array $items = [];
46
    private bool $crossfade = false;
47
    private array $options = ['data-ride' => 'carousel'];
48
49 2
    protected function run(): string
50
    {
51 2
        if (!isset($this->options['id'])) {
52 2
            $this->options['id'] = "{$this->getId()}-carousel";
53
        }
54
55 2
        Html::addCssClass($this->options, ['widget' => 'carousel', 'slide']);
56
57 2
        if ($this->crossfade) {
58 1
            Html::addCssClass($this->options, 'carousel-fade');
59
        }
60
61 2
        $this->registerPlugin('carousel', $this->options);
62
63 2
        return implode("\n", [
64 2
            Html::beginTag('div', $this->options),
65 2
            $this->renderIndicators(),
66 2
            $this->renderItems(),
67 2
            $this->renderControls(),
68 2
            Html::endTag('div'),
69 2
        ]) . "\n";
70
    }
71
72
    /**
73
     * Renders carousel indicators.
74
     */
75 2
    public function renderIndicators(): string
76
    {
77 2
        if ($this->showIndicators === false) {
78
            return '';
79
        }
80
81 2
        $indicators = [];
82
83 2
        for ($i = 0, $count = count($this->items); $i < $count; $i++) {
84 2
            $options = ['data-target' => '#' . $this->options['id'], 'data-slide-to' => $i];
85 2
            if ($i === 0) {
86 2
                Html::addCssClass($options, 'active');
87
            }
88 2
            $indicators[] = Html::tag('li', '', $options);
89
        }
90
91 2
        return Html::tag('ol', implode("\n", $indicators), ['class' => ['carousel-indicators']]);
92
    }
93
94
    /**
95
     * Renders carousel items as specified on {@see items}.
96
     */
97 2
    public function renderItems(): string
98
    {
99 2
        $items = [];
100
101 2
        foreach ($this->items as $i => $iValue) {
102 2
            $items[] = $this->renderItem($iValue, $i);
103
        }
104
105 2
        return Html::tag('div', implode("\n", $items), ['class' => 'carousel-inner']);
106
    }
107
108
    /**
109
     * Renders a single carousel item
110
     *
111
     * @param array|string $item a single item from {@see items}
112
     * @param int $index the item index as the first item should be set to `active`.
113
     *
114
     * @throws JsonException|RuntimeException if the item is invalid.
115
     *
116
     * @return string the rendering result.
117
     */
118 2
    public function renderItem($item, int $index): string
119
    {
120 2
        if (is_string($item)) {
121
            $content = $item;
122
            $caption = null;
123
            $options = [];
124 2
        } elseif (isset($item['content'])) {
125 2
            $content = $item['content'];
126 2
            $caption = ArrayHelper::getValue($item, 'caption');
127
128 2
            if ($caption !== null) {
129 2
                $captionOptions = ArrayHelper::remove($item, 'captionOptions', []);
130 2
                Html::addCssClass($captionOptions, ['widget' => 'carousel-caption']);
131
132 2
                $caption = Html::tag('div', $caption, $captionOptions);
133
            }
134
135 2
            $options = ArrayHelper::getValue($item, 'options', []);
136
        } else {
137
            throw new RuntimeException('The "content" option is required.');
138
        }
139
140 2
        Html::addCssClass($options, ['widget' => 'carousel-item']);
141
142 2
        if ($index === 0) {
143 2
            Html::addCssClass($options, 'active');
144
        }
145
146 2
        return Html::tag('div', $content . "\n" . $caption, $options);
147
    }
148
149
    /**
150
     * Renders previous and next control buttons.
151
     *
152
     * @throws JsonException|RuntimeException if {@see controls} is invalid.
153
     */
154 2
    public function renderControls(): ?string
155
    {
156 2
        if (isset($this->controls[0], $this->controls[1])) {
157 2
            return Html::a($this->controls[0], '#' . $this->options['id'], [
158 2
                'class' => 'carousel-control-prev',
159
                'data-slide' => 'prev',
160
                'role' => 'button',
161 2
            ]) . "\n"
162 2
                . Html::a($this->controls[1], '#' . $this->options['id'], [
163 2
                    'class' => 'carousel-control-next',
164
                    'data-slide' => 'next',
165
                    'role' => 'button',
166
                ]);
167
        }
168
169
        if ($this->controls === null) {
170
            return '';
171
        }
172
173
        throw new RuntimeException(
174
            'The "controls" property must be either null or an array of two elements.'
175
        );
176
    }
177
178
    /**
179
     * The labels for the previous and the next control buttons.
180
     *
181
     * If null, it means the previous and the next control buttons should not be displayed.
182
     *
183
     * @param array|null $value
184
     *
185
     * @return $this
186
     */
187
    public function controls(?array $value): self
188
    {
189
        $this->controls = $value;
190
191
        return $this;
192
    }
193
194
    /**
195
     * Animate slides with a fade transition instead of a slide. Defaults to `false`.
196
     *
197
     * @param bool $value
198
     *
199
     * @return $this
200
     */
201 1
    public function crossfade(bool $value): self
202
    {
203 1
        $this->crossfade = $value;
204
205 1
        return $this;
206
    }
207
208
    /**
209
     * List of slides in the carousel. Each array element represents a single slide with the following structure:
210
     *
211
     * ```php
212
     * [
213
     *     // required, slide content (HTML), such as an image tag
214
     *     'content' => '<img src="http://twitter.github.io/bootstrap/assets/img/bootstrap-mdo-sfmoma-01.jpg"/>',
215
     *     // optional, the caption (HTML) of the slide
216
     *     'caption' => '<h4>This is title</h4><p>This is the caption text</p>',
217
     *     // optional the HTML attributes of the slide container
218
     *     'options' => [],
219
     * ]
220
     * ```
221
     *
222
     * @param array $value
223
     *
224
     * @return $this
225
     */
226 2
    public function items(array $value): self
227
    {
228 2
        $this->items = $value;
229
230 2
        return $this;
231
    }
232
233
    /**
234
     * The HTML attributes for the container tag. The following special options are recognized.
235
     *
236
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
237
     *
238
     * @param array $value
239
     *
240
     * @return $this
241
     */
242
    public function options(array $value): self
243
    {
244
        $this->options = $value;
245
246
        return $this;
247
    }
248
249
    /**
250
     * Whether carousel indicators (<ol> tag with anchors to items) should be displayed or not.
251
     *
252
     * @param bool $value
253
     *
254
     * @return $this
255
     */
256
    public function showIndicators(bool $value): self
257
    {
258
        $this->showIndicators = $value;
259
260
        return $this;
261
    }
262
}
263