Passed
Push — master ( 82bb4d...6d0312 )
by Evgeniy
04:37 queued 01:56
created

Breadcrumbs::renderHomeLink()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Bulma;
6
7
use InvalidArgumentException;
8
use JsonException;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Html\Html;
11
12
use function array_key_exists;
13
use function array_merge;
14
use function implode;
15
use function is_array;
16
use function strtr;
17
18
/**
19
 * The Bulma breadcrumb is a simple navigation component.
20
 *
21
 * ```php
22
 * echo Breadcrumbs::widget()->items([
23
 *     ['label' => 'Info'],
24
 *     ['label' => 'Contacts'],
25
 * ]);
26
 * ```
27
 *
28
 * @link https://bulma.io/documentation/components/breadcrumb/
29
 */
30
final class Breadcrumbs extends Widget
31
{
32
    private bool $encodeLabels = true;
33
    private ?array $homeItem = ['label' => 'Home', 'url' => '/'];
34
    private string $itemTemplate = "<li>{icon}{link}</li>\n";
35
    private string $activeItemTemplate = "<li class=\"is-active\"><a aria-current=\"page\">{icon}{label}</a></li>\n";
36
    private array $items = [];
37
    private array $options = [];
38
    private array $itemsOptions = [];
39
40 13
    protected function run(): string
41
    {
42 13
        if (empty($this->items)) {
43 1
            return '';
44
        }
45
46 12
        $this->buildOptions();
47
48
        return
49 12
            Html::openTag('nav', $this->options) . "\n" .
50 12
                Html::openTag('ul', $this->itemsOptions) . "\n" .
51 12
                    implode('', $this->renderItems()) .
52 11
                Html::closeTag('ul') . "\n" .
53 11
            Html::closeTag('nav');
54
    }
55
56
    /**
57
     * Disables encoding for labels.
58
     *
59
     * @return self
60
     */
61 1
    public function withoutEncodeLabels(): self
62
    {
63 1
        $new = clone $this;
64 1
        $new->encodeLabels = false;
65 1
        return $new;
66
    }
67
68
    /**
69
     * Returns a new instance with the specified first item in the breadcrumbs (called home link).
70
     *
71
     * If a null is specified, the home item will not be rendered.
72
     *
73
     * @param array|null $value Please refer to {@see items()} on the format.
74
     *
75
     * @throws InvalidArgumentException If an empty array is specified.
76
     *
77
     * @return self
78
     */
79 9
    public function homeItem(?array $value): self
80
    {
81 9
        if ($value === []) {
82 1
            throw new InvalidArgumentException(
83 1
                'The home item cannot be an empty array. To disable rendering of the home item, specify null.',
84
            );
85
        }
86
87 8
        $new = clone $this;
88 8
        $new->homeItem = $value;
89 8
        return $new;
90
    }
91
92
    /**
93
     * The template used to render each inactive item in the breadcrumbs. The token `{link}` will be replaced with the
94
     * actual HTML link for each inactive item.
95
     *
96
     * @param string $value
97
     *
98
     * @return self
99
     */
100 2
    public function itemTemplate(string $value): self
101
    {
102 2
        $new = clone $this;
103 2
        $new->itemTemplate = $value;
104 2
        return $new;
105
    }
106
107
    /**
108
     * The template used to render each active item in the breadcrumbs. The token `{link}` will be replaced with the
109
     * actual HTML link for each active item.
110
     *
111
     * @param string $value
112
     *
113
     * @return self
114
     */
115 2
    public function activeItemTemplate(string $value): self
116
    {
117 2
        $new = clone $this;
118 2
        $new->activeItemTemplate = $value;
119 2
        return $new;
120
    }
121
122
    /**
123
     * List of items to appear in the breadcrumb. If this property is empty, the widget will not render anything. Each
124
     * array element represents a single link in the breadcrumb with the following structure:
125
     *
126
     * ```php
127
     * [
128
     *     'label' => 'label of the link',  // required
129
     *     'url' => 'url of the link',      // optional, will be processed by Url::to()
130
     *     'template' => 'own template of the item', // optional, if not set $this->itemTemplate will be used
131
     * ]
132
     * ```
133
     *
134
     * @param array $value
135
     *
136
     * @return self
137
     */
138 14
    public function items(array $value): self
139
    {
140 14
        $new = clone $this;
141 14
        $new->items = $value;
142 14
        return $new;
143
    }
144
145
    /**
146
     * The HTML attributes for the widget container nav tag.
147
     *
148
     * @param array $value
149
     *
150
     * @return self
151
     *
152
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
153
     */
154 3
    public function options(array $value): self
155
    {
156 3
        $new = clone $this;
157 3
        $new->options = $value;
158 3
        return $new;
159
    }
160
161
    /**
162
     * The HTML attributes for the items widget.
163
     *
164
     * @param array $value
165
     *
166
     * @return self
167
     *
168
     * {@see Html::renderTagAttributes()} for details on how attributes are being rendered.
169
     */
170 2
    public function itemsOptions(array $value): self
171
    {
172 2
        $new = clone $this;
173 2
        $new->itemsOptions = $value;
174 2
        return $new;
175
    }
176
177 12
    private function buildOptions(): void
178
    {
179 12
        if (!isset($this->options['id'])) {
180 12
            $this->options['id'] = "{$this->getId()}-breadcrumbs";
181
        }
182
183 12
        $this->options = $this->addOptions(
184 12
            array_merge(
185 12
                $this->options,
186 12
                ['aria-label' => 'breadcrumbs']
187
            ),
188 12
            'breadcrumb'
189
        );
190 12
    }
191
192 12
    private function renderIcon(string $icon, array $iconOptions): string
193
    {
194 12
        $html = '';
195
196 12
        if ($icon !== '') {
197 1
            $html = Html::openTag('span', $iconOptions) .
198 1
                Html::tag('i', '', ['class' => $icon]) .
199 1
                Html::closeTag('span');
200
        }
201
202 12
        return $html;
203
    }
204
205
    /**
206
     * Renders a single breadcrumb item.
207
     *
208
     * @param array $item The item to be rendered. It must contain the "label" element. The "url" element is optional.
209
     * @param string $template The template to be used to rendered the link. The token "{link}" will be replaced by the
210
     * link.
211
     *
212
     * @throws InvalidArgumentException|JsonException If `$item` does not have "label" element.
213
     *
214
     * @return string The rendering result.
215
     */
216 12
    private function renderItem(array $item, string $template): string
217
    {
218 12
        $encodeLabel = ArrayHelper::remove($item, 'encode', $this->encodeLabels);
219
220 12
        $icon = '';
221 12
        $iconOptions = [];
222
223 12
        if (isset($item['icon'])) {
224 1
            $icon = $item['icon'];
225
        }
226
227 12
        if (isset($item['iconOptions']) && is_array($item['iconOptions'])) {
228 1
            $iconOptions = $this->addOptions($iconOptions, 'icon');
229
        }
230
231 12
        unset($item['icon'], $item['iconOptions']);
232
233 12
        if (array_key_exists('label', $item)) {
234 12
            $label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
235
        } else {
236 1
            throw new InvalidArgumentException('The "label" element is required for each link.');
237
        }
238
239 12
        if (isset($item['template'])) {
240 1
            $template = $item['template'];
241
        }
242
243 12
        if (isset($item['url'])) {
244 12
            $options = $item;
245 12
            unset($options['template'], $options['label'], $options['url'], $options['icon']);
246 12
            $link = Html::a($label, $item['url'], $options)->encode(false)->render();
247
        } else {
248 2
            $link = $label;
249
        }
250
251 12
        return strtr(
252 12
            $template,
253 12
            ['{label}' => $label, '{link}' => $link, '{icon}' => $this->renderIcon($icon, $iconOptions)],
254
        );
255
    }
256
257 12
    private function renderItems(): array
258
    {
259 12
        $items = [];
260
261 12
        if ($this->homeItem !== null) {
262 11
            $items[] = $this->renderItem($this->homeItem, $this->itemTemplate);
263
        }
264
265 12
        foreach ($this->items as $item) {
266 12
            if (!is_array($item)) {
267 1
                $item = ['label' => $item];
268
            }
269
270 12
            $items[] = $this->renderItem($item, isset($item['url']) ? $this->itemTemplate : $this->activeItemTemplate);
271
        }
272
273 11
        return $items;
274
    }
275
}
276