Passed
Push — master ( 73f50c...734467 )
by Dmitrijs
02:29
created

Collection   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 224
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
wmc 39
eloc 84
c 3
b 0
f 1
dl 0
loc 224
rs 9.28

7 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 8 2
A renderItems() 0 14 3
A renderSecondary() 0 17 6
C renderItem() 0 47 13
A hasHeader() 0 9 4
A run() 0 5 1
B renderAvatar() 0 29 10
1
<?php
2
namespace dmgpage\yii2materialize\widgets;
3
4
use dmgpage\yii2materialize\helpers\Html;
5
use yii\helpers\ArrayHelper;
6
use yii\base\InvalidConfigException;
7
8
/**
9
 * Collections allow you to group list objects together.
10
 *
11
 * ```php
12
 * echo Collection::widget([
13
 *     'items' => [
14
 *         [
15
 *             'label' => '<h5>Best Panda</h5>',
16
 *             'encode' => false,
17
 *             'header' => true
18
 *         ],
19
 *         [
20
 *             'label' => 'Kung Fu Panda',
21
 *             'secondary' => [
22
 *                 'icon' => [
23
 *                     'name' => 'favorite',
24
 *                     'options' =>  ['class' => 'red-text'],
25
 *                 ],
26
 *                 'url' => '#',
27
 *                 'options' => ['target' => '_blank']
28
 *             ]
29
 *         ],
30
 *         [
31
 *             'label' => 'Kung Fu Panda',
32
 *             'secondary' => ['icon' => 'favorite']
33
 *         ],
34
 *         [
35
 *             'label' => 'Kung Fu Panda',
36
 *             'secondary' => ['icon' => 'favorite']
37
 *         ],
38
 *         [
39
 *             'label' => 'Kung Fu Panda',
40
 *             'secondary' => ['icon' => 'favorite']
41
 *         ]
42
 *     ]
43
 * ]);
44
 * ```
45
 * @see https://materializecss.com/collections.html
46
 * @package widgets
47
 */
48
class Collection extends Widget
49
{
50
    /**
51
     * @var array list of items in the collection widget. Each array element represents a single
52
     * row with the following structure:
53
     *
54
     * - avatar: array, optional, options for creating avatar content. Available options are:
55
     *   - icon: string|array, optional, the options for the icon. See [[Html::icon()]]
56
     *   - image: string|array, optional, the options for the avatar image. See [[Html::img()]]
57
     *   - title: string, title for the item avatar. Value will be HTML-encoded.
58
     *   - titleOptions: array the HTML attributes for the title tag.
59
     * - label: string, required, the item label.
60
     * - header: boolean, optional, whether this label should be formatted as header.
61
     * - encode: boolean, optional, whether this label should be HTML-encoded. This param will override
62
     *   global `$this->encodeLabels` param.
63
     * - options: array, optional, the HTML attributes of the item container (LI).
64
     * - url: array|string, optional, the URL for the hyperlink tag. Defaults to "#".
65
     * - linkOptions: array, optional, the HTML attributes of the item's link.
66
     * - active: boolean, optional, whether this item should be active.
67
     * - visible: boolean, optional, whether the item tab header and pane should be visible or not. Defaults to true.
68
     * - secondary: array, optional, options for creating secondary content. Available options are:
69
     *   - icon: string|array, required, the options for the icon. See [[Html::icon()]]
70
     *   - options: array, optional, the HTML attributes of the icon link.
71
     *     for more description.
72
     *   - url: array|string, optional, the URL for the icon. Defaults to "#".
73
     */
74
    public $items = [];
75
76
    /**
77
     * @var boolean whether the labels for items should be HTML-encoded.
78
     */
79
    public $encodeLabels = true;
80
81
    /**
82
     * @var boolean weather format each item as a link
83
     */
84
    public $asLinks = false;
85
86
    /**
87
     * Initializes the widget.
88
     */
89
    public function init()
90
    {
91
        parent::init();
92
93
        Html::addCssClass($this->options, ['widget' => 'collection']);
94
95
        if ($this->hasHeader()) {
96
            Html::addCssClass($this->options, ['header' => 'with-header']);
97
        }
98
    }
99
100
    /**
101
     * Renders the widget.
102
     */
103
    public function run()
104
    {
105
        $this->initializePlugin = true;
106
        $this->registerPlugin('collection');
107
        return $this->renderItems();
108
    }
109
110
    /**
111
     * Renders tab items as specified on [[items]].
112
     *
113
     * @return string the rendering result.
114
     * @throws InvalidConfigException.
115
     */
116
    protected function renderItems()
117
    {
118
        $rows = [];
119
120
        foreach ($this->items as $item) {
121
            $rows[] = $this->renderItem($item);
122
        }
123
124
        $containerTag = $this->asLinks ? 'div' : 'ul';
125
        $html = Html::beginTag($containerTag, $this->options);
126
        $html .= implode("\n", $rows);
127
        $html .= Html::endTag($containerTag);
128
129
        return $html;
130
    }
131
132
    /**
133
     * Renders single collection item as specified on [[items]].
134
     *
135
     * @param array $item single collection element
136
     * @return string the rendering result
137
     * @throws InvalidConfigException.
138
     */
139
    protected function renderItem($item)
140
    {
141
        if (!array_key_exists('label', $item)) {
142
            throw new InvalidConfigException("The 'label' option is required.");
143
        } elseif (ArrayHelper::remove($item, 'visible', true)) {
144
            $itemContent = null;
145
            $encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
146
            $label = $encodeLabel ? Html::encode($item['label']) : $item['label'];
147
            $isHeader = ArrayHelper::getValue($item, 'header', false);
148
            $avatar = ArrayHelper::remove($item, 'avatar', []);
149
            $defaultUrl = $this->asLinks ? '#' : null;
150
            $url = ArrayHelper::getValue($item, 'url', $defaultUrl);
151
            $linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
152
            $options = ArrayHelper::getValue($item, 'options', []);
153
            $isActive = ArrayHelper::getValue($item, 'active', false);
154
            $containerClass = $isHeader ? 'collection-header' : 'collection-item';
155
            $secondaryItem = ArrayHelper::getValue($item, 'secondary', []);
156
            Html::addCssClass($options, ['item' => $containerClass]);
157
158
            if ($isActive) {
159
                Html::addCssClass($options, ['active' => 'active']);
160
            }
161
162
            // Avatar
163
            if (!empty($avatar) && !$isHeader) {
164
                Html::addCssClass($options, ['avatar' => 'avatar']);
165
                $itemContent .= $this->renderAvatar($avatar);
166
            }
167
168
            // Main content
169
            if (!$this->asLinks && !empty($url)) {
170
                $itemContent .= Html::a($label, $url, $linkOptions);
171
            } else {
172
                $itemContent .= $label;
173
            }
174
175
            // Secondary content
176
            $itemContent .= $this->renderSecondary($secondaryItem, $isHeader);
177
178
            // Item container
179
            if ($this->asLinks) {
180
                $html = Html::a($itemContent, $url, $options);
181
            } else {
182
                $html = Html::tag('li', $itemContent, $options);
183
            }
184
185
            return $html;
186
        }
187
    }
188
189
    /**
190
     * Renders single avatar image or icon as specified on [[avatar]].
191
     *
192
     * @param array $avatar single avatar item
193
     * @param bool $isHeader whether this label should be formatted as header
194
     * @return string the rendering result
195
     *
196
     * @throws InvalidConfigException.
197
     * @see https://materializecss.com/collections.html#secondary
198
     */
199
    protected function renderAvatar($avatar)
200
    {
201
        $content = null;
202
        $image = ArrayHelper::getValue($avatar, 'image', []);
203
        $icon = ArrayHelper::getValue($avatar, 'icon', []);
204
        $title = ArrayHelper::getValue($avatar, 'title', null);
205
        $titleOptions = ArrayHelper::getValue($avatar, 'titleOptions', []);
206
207
        // Avatar
208
        if (!empty($image)) {
209
            $url = is_string($image) ? $image : ArrayHelper::remove($image, 'url', '#');
210
            $options = is_array($image) ? ArrayHelper::getValue($image, 'options', []) : [];
211
            $options['class'] = isset($options['class']) ? $options['class'] : 'circle';
212
            $content .= Html::img($url, $options);
213
        } elseif (!empty($icon)) {
214
            $name = is_string($icon) ? $icon : ArrayHelper::remove($icon, 'name', null);
215
            $options = is_array($icon) ? ArrayHelper::getValue($icon, 'options', []) : [];
216
            $options['class'] = isset($options['class']) ? $options['class'] : 'circle';
217
            $content .= Html::icon($name, $options);
218
        }
219
220
        // Title
221
        if (!empty($title)) {
222
            $title = Html::encode($title);
223
            Html::addCssClass($titleOptions, ['title' => 'title']);
224
            $content .= Html::tag('span', $title, $titleOptions);
225
        }
226
227
        return $content;
228
    }
229
230
    /**
231
     * Renders single secondary content item as specified on [[secondary]].
232
     *
233
     * @param array $item single secondary content element
234
     * @param bool $isHeader whether this label should be formatted as header
235
     * @return string the rendering result
236
     *
237
     * @throws InvalidConfigException.
238
     * @see https://materializecss.com/collections.html#secondary
239
     */
240
    protected function renderSecondary($item, $isHeader)
241
    {
242
        $content = null;
243
244
        if (!empty($item) && !$isHeader && !array_key_exists('icon', $item)) {
245
            throw new InvalidConfigException("The 'icon' option is required for secondary content.");
246
        } else if (!empty($item) && !$isHeader) {
247
            $url = ArrayHelper::getValue($item, 'url', '#');
248
            $options = ArrayHelper::getValue($item, 'options', []);
249
250
            Html::addCssClass($options, ['secondary' => 'secondary-content']);
251
252
            $icon = $this->renderIcon($item['icon']);
253
            $content = Html::a($icon, $url, $options);
254
        }
255
256
        return $content;
257
    }
258
259
    /**
260
     * Searches for header value in [[items]]
261
     * @return boolean if there's header defined
262
     */
263
    protected function hasHeader()
264
    {
265
        foreach ($this->items as $item) {
266
            if (isset($item['header']) && $item['header'] === true) {
267
                return true;
268
            }
269
        }
270
271
        return false;
272
    }
273
}
274