Passed
Pull Request — master (#160)
by Wilmer
03:13
created

CheckboxList::run()   B

Complexity

Conditions 8
Paths 13

Size

Total Lines 45
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 8

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 8
eloc 24
c 2
b 0
f 0
nc 13
nop 0
dl 0
loc 45
ccs 24
cts 24
cp 1
crap 8
rs 8.4444
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Widget;
6
7
use Closure;
8
use InvalidArgumentException;
9
use Stringable;
10
use Yiisoft\Form\Widget\Attribute\ChoiceAttributes;
11
use Yiisoft\Html\Widget\CheckboxList\CheckboxItem;
12
use Yiisoft\Html\Widget\CheckboxList\CheckboxList as CheckboxListTag;
13
14
/*
15
 * Generates a list of checkboxes.
16
 *
17
 * A checkbox list allows multiple selection.
18
 */
19
final class CheckboxList extends ChoiceAttributes
20
{
21
    private array $containerAttributes = [];
22
    private ?string $containerTag = 'div';
23
    /** @psalm-var array[] $value */
24
    private array $individualItemsAttributes = [];
25
    /** @var string[] */
26
    private array $items = [];
27
    /** @var bool[]|float[]|int[]|string[]|Stringable[] */
28
    private array $itemsAttributes = [];
29
    /** @psalm-var Closure(CheckboxItem):string|null */
30
    private ?Closure $itemsFormatter = null;
31
    /** @var bool[]|float[]|int[]|string[]|Stringable[] */
32
    private array $itemsFromValues = [];
33
    private string $separator = "\n";
34
35
    /**
36
     * Focus on the control (put cursor into it) when the page loads.
37
     * Only one form element could be in focus at the same time.
38
     *
39
     * @return static
40
     *
41
     * @link https://www.w3.org/TR/html52/sec-forms.html#autofocusing-a-form-control-the-autofocus-attribute
42
     *
43
     * @psalm-suppress MethodSignatureMismatch
44
     */
45 1
    public function autofocus(): self
46
    {
47 1
        $new = clone $this;
48 1
        $new->containerAttributes['autofocus'] = true;
49 1
        return $new;
50
    }
51
52
    /**
53
     * The container attributes for generating the list of checkboxes tag using {@see CheckBoxList}.
54
     *
55
     * @param array $attributes
56
     *
57
     * @return static
58
     *
59
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
60
     */
61 3
    public function containerAttributes(array $attributes): self
62
    {
63 3
        $new = clone $this;
64 3
        $new->containerAttributes = $attributes;
65 3
        return $new;
66
    }
67
68
    /**
69
     * The tag name for the container element.
70
     *
71
     * @param string|null $tag tag name. if `null` disabled rendering.
72
     *
73
     * @return static
74
     */
75 5
    public function containerTag(?string $tag = null): self
76
    {
77 5
        $new = clone $this;
78 5
        $new->containerTag = $tag;
79 5
        return $new;
80
    }
81
82
    /**
83
     * Set the ID of container the widget.
84
     *
85
     * @param string|null $id
86
     *
87
     * @return static
88
     *
89
     * @link https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute
90
     *
91
     * @psalm-suppress MethodSignatureMismatch
92
     */
93 2
    public function id(?string $id): self
94
    {
95 2
        $new = clone $this;
96 2
        $new->containerAttributes['id'] = $id;
97 2
        return $new;
98
    }
99
100
    /**
101
     * The specified attributes for items.
102
     *
103
     * @param array $attributes
104
     *
105
     * @return static
106
     *
107
     * @psalm-param array[] $attributes
108
     */
109 3
    public function individualItemsAttributes(array $attributes = []): self
110
    {
111 3
        $new = clone $this;
112 3
        $new->individualItemsAttributes = $attributes;
113 3
        return $new;
114
    }
115
116
    /**
117
     * The data used to generate the list of checkboxes.
118
     *
119
     * The array keys are the list of checkboxes values, and the array key values are the corresponding labels.
120
     *
121
     * @param string[] $items
122
     *
123
     * @return static
124
     */
125 35
    public function items(array $items = []): self
126
    {
127 35
        $new = clone $this;
128 35
        $new->items = $items;
129 35
        return $new;
130
    }
131
132
    /**
133
     * The items attribute for generating the list of checkboxes tag using {@see CheckBoxList}.
134
     *
135
     * @param array $attributes
136
     *
137
     * @return static
138
     *
139
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
140
     *
141
     *  @psalm-param bool[]|float[]|int[]|string[]|Stringable[] $attributes
142
     */
143 4
    public function itemsAttributes(array $attributes = []): self
144
    {
145 4
        $new = clone $this;
146 4
        $new->itemsAttributes = $attributes;
147 4
        return $new;
148
    }
149
150
    /**
151
     * Callable, a callback that can be used to customize the generation of the HTML code corresponding to a single
152
     * item in $items.
153
     *
154
     * The signature of this callback must be:
155
     *
156
     * ```php
157
     * function ($index, $label, $name, $checked, $value)
158
     * ```
159
     *
160
     * @param Closure|null $formatter
161
     *
162
     * @return static
163
     *
164
     * @psalm-param Closure(CheckboxItem):string|null $formatter
165
     */
166 3
    public function itemsFormatter(?Closure $formatter): self
167
    {
168 3
        $new = clone $this;
169 3
        $new->itemsFormatter = $formatter;
170 3
        return $new;
171
    }
172
173
    /**
174
     * The data used to generate the list of checkboxes.
175
     *
176
     * The array keys are the list of checkboxes values, and the array values are the corresponding labels.
177
     *
178
     * @param bool[]|float[]|int[]|string[]|Stringable[] $itemsFromValues
179
     *
180
     * @return static
181
     */
182 3
    public function itemsFromValues(array $itemsFromValues = []): self
183
    {
184 3
        $new = clone $this;
185 3
        $new->itemsFromValues = $itemsFromValues;
186 3
        return $new;
187
    }
188
189
    /**
190
     * The HTML code that separates items.
191
     *
192
     * @param string $separator
193
     *
194
     * @return static
195
     */
196 3
    public function separator(string $separator): self
197
    {
198 3
        $new = clone $this;
199 3
        $new->separator = $separator;
200 3
        return $new;
201
    }
202
203
    /**
204
     * The tabindex global attribute indicates that its element can be focused, and where it participates in sequential
205
     * keyboard navigation (usually with the Tab key, hence the name).
206
     *
207
     * It accepts an integer as a value, with different results depending on the integer's value:
208
     *
209
     * - A negative value (usually tabindex="-1") means that the element is not reachable via sequential keyboard
210
     * navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
211
     * with JavaScript.
212
     * - tabindex="0" means that the element should be focusable in sequential keyboard navigation, but its order is
213
     * defined by the document's source order.
214
     * - A positive value means the element should be focusable in sequential keyboard navigation, with its order
215
     * defined by the value of the number. That is, tabindex="4" is focused before tabindex="5", but after tabindex="3".
216
     *
217
     * @param int $value
218
     *
219
     * @return static
220
     *
221
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
222
     *
223
     * @psalm-suppress MethodSignatureMismatch
224
     */
225 1
    public function tabIndex(int $value): self
226
    {
227 1
        $new = clone $this;
228 1
        $new->containerAttributes['tabindex'] = $value;
229 1
        return $new;
230
    }
231
232
    /**
233
     * @return string the generated checkbox list.
234
     */
235 38
    protected function run(): string
236
    {
237
        /** @psalm-var array[] */
238 38
        [$attributes, $containerAttributes] = $this->buildList($this->attributes, $this->containerAttributes);
239
240
        /**
241
         *  @var iterable<int, scalar|Stringable>|scalar|Stringable|null
242
         *
243
         *  @link https://html.spec.whatwg.org/multipage/input.html#attr-input-value
244
         */
245 38
        $value = $attributes['value'] ?? $this->getAttributeValue();
246 38
        unset($attributes['value']);
247
248 38
        if (!is_iterable($value) && null !== $value) {
249 2
            throw new InvalidArgumentException('CheckboxList widget must be a array or null value.');
250
        }
251
252 36
        $name = $this->getInputName();
253
254
        /** @var string */
255 36
        if (!empty($attributes['name']) && is_string($attributes['name'])) {
256 2
            $name = $attributes['name'];
257
        }
258
259 36
        $checkboxList = CheckboxListTag::create($name);
260
261 36
        if ($this->items !== []) {
262 34
            $checkboxList = $checkboxList->items($this->items, $this->getEncode());
263 2
        } elseif ($this->itemsFromValues !== []) {
264 2
            $checkboxList = $checkboxList->itemsFromValues($this->itemsFromValues, $this->getEncode());
265
        }
266
267 36
        if ($this->itemsAttributes !== []) {
268 3
            $checkboxList = $checkboxList->replaceCheckboxAttributes($this->itemsAttributes);
269
        }
270
271
        return $checkboxList
272 36
            ->checkboxAttributes($attributes)
273 36
            ->containerAttributes($containerAttributes)
274 36
            ->containerTag($this->containerTag)
275 36
            ->individualInputAttributes($this->individualItemsAttributes)
276 36
            ->itemFormatter($this->itemsFormatter)
277 36
            ->separator($this->separator)
278 36
            ->values($value ?? [])
279 36
            ->render();
280
    }
281
}
282