Test Failed
Pull Request — master (#160)
by Wilmer
03:21 queued 47s
created

CheckboxList::run()   B

Complexity

Conditions 8
Paths 13

Size

Total Lines 45
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
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 11
cts 11
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
    public function autofocus(): self
46
    {
47
        $new = clone $this;
48
        $new->containerAttributes['autofocus'] = true;
49
        return $new;
50
    }
51 3
52
    /**
53 3
     * The container attributes for generating the list of checkboxes tag using {@see CheckBoxList}.
54 3
     *
55 3
     * @param array $attributes
56
     *
57
     * @return static
58
     *
59
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
60
     */
61
    public function containerAttributes(array $attributes): self
62
    {
63
        $new = clone $this;
64
        $new->containerAttributes = $attributes;
65 5
        return $new;
66
    }
67 5
68 5
    /**
69 5
     * 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
    public function containerTag(?string $tag = null): self
76
    {
77
        $new = clone $this;
78
        $new->containerTag = $tag;
79
        return $new;
80
    }
81
82
    /**
83
     * Set the ID of container the widget.
84
     *
85
     * @param string|null $id
86
     *
87 2
     * @return static
88
     *
89 2
     * @link https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute
90 2
     *
91 2
     * @psalm-suppress MethodSignatureMismatch
92
     */
93
    public function id(?string $id): self
94
    {
95
        $new = clone $this;
96
        $new->containerAttributes['id'] = $id;
97
        return $new;
98
    }
99
100
    /**
101
     * The specified attributes for items.
102
     *
103 3
     * @param array $attributes
104
     *
105 3
     * @return static
106 3
     *
107 3
     * @psalm-param array[] $attributes
108
     */
109
    public function individualItemsAttributes(array $attributes = []): self
110
    {
111
        $new = clone $this;
112
        $new->individualItemsAttributes = $attributes;
113
        return $new;
114
    }
115
116
    /**
117
     * The data used to generate the list of checkboxes.
118
     *
119 25
     * The array keys are the list of checkboxes values, and the array key values are the corresponding labels.
120
     *
121 25
     * @param string[] $items
122 25
     *
123 25
     * @return static
124
     */
125
    public function items(array $items = []): self
126
    {
127
        $new = clone $this;
128
        $new->items = $items;
129
        return $new;
130
    }
131
132
    /**
133
     * The items attribute for generating the list of checkboxes tag using {@see CheckBoxList}.
134
     *
135 4
     * @param array $attributes
136
     *
137 4
     * @return static
138 4
     *
139 4
     * {@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
    public function itemsAttributes(array $attributes = []): self
144
    {
145
        $new = clone $this;
146
        $new->itemsAttributes = $attributes;
147
        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 3
     * ```
159
     *
160 3
     * @param Closure|null $formatter
161 3
     *
162 3
     * @return static
163
     *
164
     * @psalm-param Closure(CheckboxItem):string|null $formatter
165
     */
166
    public function itemsFormatter(?Closure $formatter): self
167
    {
168
        $new = clone $this;
169
        $new->itemsFormatter = $formatter;
170
        return $new;
171
    }
172
173
    /**
174 15
     * The data used to generate the list of checkboxes.
175
     *
176 15
     * The array keys are the list of checkboxes values, and the array values are the corresponding labels.
177 15
     *
178 15
     * @param bool[]|float[]|int[]|string[]|Stringable[] $itemsFromValues
179
     *
180
     * @return static
181
     */
182
    public function itemsFromValues(array $itemsFromValues = []): self
183
    {
184
        $new = clone $this;
185
        $new->itemsFromValues = $itemsFromValues;
186
        return $new;
187
    }
188
189 3
    /**
190
     * The HTML code that separates items.
191 3
     *
192 3
     * @param string $separator
193 3
     *
194
     * @return static
195
     */
196
    public function separator(string $separator): self
197
    {
198
        $new = clone $this;
199
        $new->separator = $separator;
200
        return $new;
201
    }
202
203 3
    /**
204
     * The tabindex global attribute indicates that its element can be focused, and where it participates in sequential
205 3
     * keyboard navigation (usually with the Tab key, hence the name).
206 3
     *
207 3
     * 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 26
     * 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 26
     * defined by the value of the number. That is, tabindex="4" is focused before tabindex="5", but after tabindex="3".
216 26
     *
217
     * @param int $value
218
     *
219 26
     * @return static
220
     *
221 26
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
222 2
     *
223
     * @psalm-suppress MethodSignatureMismatch
224
     */
225 24
    public function tabIndex(int $value): self
226
    {
227
        $new = clone $this;
228 24
        $new->containerAttributes['tabindex'] = $value;
229
        return $new;
230
    }
231 24
232
    /**
233 24
     * @return string the generated checkbox list.
234 22
     */
235 2
    protected function run(): string
236 2
    {
237
        /** @psalm-var array[] */
238
        [$attributes, $containerAttributes] = $this->buildList($this->attributes, $this->containerAttributes);
239
240 24
        /**
241 24
         *  @var iterable<int, scalar|Stringable>|scalar|Stringable|null
242 24
         *
243 24
         *  @link https://html.spec.whatwg.org/multipage/input.html#attr-input-value
244 24
         */
245 24
        $value = $attributes['value'] ?? $this->getAttributeValue();
246 24
        unset($attributes['value']);
247 24
248 24
        if (!is_iterable($value) && null !== $value) {
249
            throw new InvalidArgumentException('CheckboxList widget must be a array or null value.');
250
        }
251
252
        $name = $this->getInputName();
253
254
        /** @var string */
255
        if (!empty($attributes['name']) && is_string($attributes['name'])) {
256
            $name = $attributes['name'];
257
        }
258
259
        $checkboxList = CheckboxListTag::create($name);
260
261
        if ($this->items !== []) {
262
            $checkboxList = $checkboxList->items($this->items, $this->getEncode());
263
        } elseif ($this->itemsFromValues !== []) {
264
            $checkboxList = $checkboxList->itemsFromValues($this->itemsFromValues, $this->getEncode());
265
        }
266
267
        if ($this->itemsAttributes !== []) {
268
            $checkboxList = $checkboxList->replaceCheckboxAttributes($this->itemsAttributes);
269
        }
270
271
        return $checkboxList
272
            ->checkboxAttributes($attributes)
273
            ->containerAttributes($containerAttributes)
274
            ->containerTag($this->containerTag)
275
            ->individualInputAttributes($this->individualItemsAttributes)
276
            ->itemFormatter($this->itemsFormatter)
277
            ->separator($this->separator)
278
            ->values($value ?? [])
279
            ->render();
280
    }
281
}
282