Passed
Push — master ( d5c6a7...b7d446 )
by Alexander
02:56
created

Breadcrumbs::itemsOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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