Passed
Pull Request — master (#94)
by Wilmer
03:07 queued 16s
created

Normalizer::renderLabel()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 30
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 7
eloc 12
c 3
b 1
f 0
nc 12
nop 5
dl 0
loc 30
rs 8.8333
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Widgets\Helper;
6
7
use InvalidArgumentException;
8
use Yiisoft\Html\Html;
9
use Yiisoft\Html\Tag\Span;
10
11
final class Normalizer
12
{
13
    /**
14
     * Normalize the given array of items for the dropdown.
15
     */
16
    public static function dropdown(array $items): array
17
    {
18
        /**
19
         * @psalm-var array[] $items
20
         * @psalm-suppress RedundantConditionGivenDocblockType
21
         */
22
        foreach ($items as $i => $child) {
23
            if (is_array($child)) {
24
                $items[$i]['label'] = self::renderLabel(
25
                    self::label($child),
26
                    self::icon($child),
27
                    self::iconAttributes($child),
28
                    self::iconClass($child),
29
                    self::iconContainerAttributes($child),
30
                );
31
                $items[$i]['active'] = self::active($child, '', '', false);
32
                $items[$i]['disabled'] = self::disabled($child);
33
                $items[$i]['enclose'] = self::enclose($child);
34
                $items[$i]['headerAttributes'] = self::headerAttributes($child);
35
                $items[$i]['itemContainerAttributes'] = self::itemContainerAttributes($child);
36
                $items[$i]['link'] = self::link($child, '/');
37
                $items[$i]['linkAttributes'] = self::linkAttributes($child);
38
                $items[$i]['toggleAttributes'] = self::toggleAttributes($child);
39
                $items[$i]['visible'] = self::visible($child);
40
41
                if (isset($child['items']) && is_array($child['items'])) {
42
                    $items[$i]['items'] = self::dropdown($child['items']);
43
                } else {
44
                    $items[$i]['items'] = [];
45
                }
46
            }
47
        }
48
49
        return $items;
50
    }
51
52
    /**
53
     * Normalize the given array of items for the menu.
54
     */
55
    public static function menu(
56
        array $items,
57
        string $currentPath,
58
        bool $activateItems,
59
        array $iconContainerAttributes = []
60
    ): array {
61
        /**
62
         * @psalm-var array[] $items
63
         * @psalm-suppress RedundantConditionGivenDocblockType
64
         */
65
        foreach ($items as $i => $child) {
66
            if (is_array($child)) {
67
                if (isset($child['items']) && is_array($child['items'])) {
68
                    $items[$i]['items'] = self::menu(
69
                        $child['items'],
70
                        $currentPath,
71
                        $activateItems,
72
                        $iconContainerAttributes,
73
                    );
74
                } else {
75
                    $items[$i]['link'] = self::link($child);
76
                    $items[$i]['linkAttributes'] = self::linkAttributes($child);
77
                    $items[$i]['active'] = self::active(
78
                        $child,
79
                        $items[$i]['link'],
80
                        $currentPath,
81
                        $activateItems
82
                    );
83
                    $items[$i]['disabled'] = self::disabled($child);
84
                    $items[$i]['visible'] = self::visible($child);
85
                    $items[$i]['label'] = self::renderLabel(
86
                        self::label($child),
87
                        self::icon($child),
88
                        self::iconAttributes($child),
89
                        self::iconClass($child),
90
                        self::iconContainerAttributes($child, $iconContainerAttributes),
91
                    );
92
                }
93
            }
94
        }
95
96
        return $items;
97
    }
98
99
    public static function renderLabel(
100
        string $label,
101
        string $icon,
102
        array $iconAttributes,
103
        string $iconClass,
104
        array $iconContainerAttributes
105
    ): string {
106
        $html = '';
107
108
        $tagName = self::iconTagName($iconAttributes);
109
110
        unset($iconAttributes['tag']);
111
112
        if ($iconClass !== '') {
113
            Html::addCssClass($iconAttributes, $iconClass);
114
        }
115
116
        if ($icon !== '' || $iconAttributes !== [] || $iconClass !== '') {
117
            $html = Html::tag($tagName)->attributes($iconAttributes)->content($icon);
118
119
            if ($tagName === 'i') {
120
                $html = Span::tag()->attributes($iconContainerAttributes)->content($html)->encode(false)->render();
121
            }
122
        }
123
124
        if ($label !== '') {
125
            $html .= $label;
126
        }
127
128
        return $html;
129
    }
130
131
    private static function active(array $item, string $link, string $currentPath, bool $activateItems): bool
132
    {
133
        if (!array_key_exists('active', $item)) {
134
            return self::isItemActive($link, $currentPath, $activateItems);
135
        }
136
137
        return is_bool($item['active']) ? $item['active'] : false;
138
    }
139
140
    private static function disabled(array $item): bool
141
    {
142
        return array_key_exists('disabled', $item) && is_bool($item['disabled']) ? $item['disabled'] : false;
143
    }
144
145
    private static function enclose(array $item): bool
146
    {
147
        return array_key_exists('enclose', $item) && is_bool($item['enclose']) ? $item['enclose'] : true;
148
    }
149
150
    private static function headerAttributes(array $item): array
151
    {
152
        return array_key_exists('headerAttributes', $item) && is_array($item['headerAttributes'])
153
            ? $item['headerAttributes']
154
            : [];
155
    }
156
157
    private static function icon(array $item): string
158
    {
159
        return array_key_exists('icon', $item) && is_string($item['icon']) ? $item['icon'] : '';
160
    }
161
162
    private static function iconAttributes(array $item): array
163
    {
164
        return array_key_exists('iconAttributes', $item) && is_array($item['iconAttributes'])
165
            ? $item['iconAttributes'] : [];
166
    }
167
168
    private static function iconClass(array $item): string
169
    {
170
        return array_key_exists('iconClass', $item) && is_string($item['iconClass']) ? $item['iconClass'] : '';
171
    }
172
173
    private static function iconContainerAttributes(array $item, array $iconContainerAttributes = []): array
174
    {
175
        return array_key_exists('iconContainerAttributes', $item) && is_array($item['iconContainerAttributes'])
176
            ? $item['iconContainerAttributes'] : $iconContainerAttributes;
177
    }
178
179
    /**
180
     * @psalm-return non-empty-string
181
     */
182
    private static function iconTagName(array $item): string
183
    {
184
        return array_key_exists('tag', $item) && is_string($item['tag']) && $item['tag'] !== '' ? $item['tag'] : 'i';
185
    }
186
187
    /**
188
     * Checks whether a menu item is active.
189
     *
190
     * This is done by checking match that specified in the `url` option of the menu item.
191
     *
192
     * @param string $link The link of the menu item.
193
     * @param string $currentPath The current path.
194
     * @param bool $activateItems Whether to activate items having no link.
195
     *
196
     * @return bool Whether the menu item is active.
197
     */
198
    private static function isItemActive(string $link, string $currentPath, bool $activateItems): bool
199
    {
200
        return $link === $currentPath && $activateItems;
201
    }
202
203
    private static function itemContainerAttributes(array $item): array
204
    {
205
        return array_key_exists('itemContainerAttributes', $item) && is_array($item['itemContainerAttributes'])
206
            ? $item['itemContainerAttributes'] : [];
207
    }
208
209
    private static function label(array $item): string
210
    {
211
        if (!isset($item['label'])) {
212
            throw new InvalidArgumentException('The "label" option is required.');
213
        }
214
215
        if (!is_string($item['label'])) {
216
            throw new InvalidArgumentException('The "label" option must be a string.');
217
        }
218
219
        if ($item['label'] === '' && !isset($item['icon'])) {
220
            throw new InvalidArgumentException('The "label" cannot be an empty string.');
221
        }
222
223
        /** @var bool */
224
        $encode = $item['encode'] ?? true;
225
226
        return $encode ? Html::encode($item['label']) : $item['label'];
227
    }
228
229
    private static function link(array $item, string $defaultValue = ''): string
230
    {
231
        return array_key_exists('link', $item) && is_string($item['link']) ? $item['link'] : $defaultValue;
232
    }
233
234
    private static function linkAttributes(array $item): array
235
    {
236
        return array_key_exists('linkAttributes', $item) && is_array($item['linkAttributes'])
237
            ? $item['linkAttributes'] : [];
238
    }
239
240
    private static function toggleAttributes(array $item): array
241
    {
242
        return array_key_exists('toggleAttributes', $item) && is_array($item['toggleAttributes'])
243
            ? $item['toggleAttributes'] : [];
244
    }
245
246
    private static function visible(array $item): bool
247
    {
248
        return array_key_exists('visible', $item) && is_bool($item['visible']) ? $item['visible'] : true;
249
    }
250
}
251