Checkbox::inputLabelAttributes()   A
last analyzed

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
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Field;
6
7
use InvalidArgumentException;
8
use Stringable;
9
use Yiisoft\Form\Field\Base\InputField;
10
use Yiisoft\Form\Field\Base\ValidationClass\ValidationClassInterface;
11
use Yiisoft\Form\Field\Base\ValidationClass\ValidationClassTrait;
12
use Yiisoft\Html\Html;
13
14
use function is_bool;
15
use function is_object;
16
use function is_string;
17
18
/**
19
 * The input element with a type attribute whose value is "checkbox" represents a state or option that can be toggled.
20
 *
21
 * @link https://html.spec.whatwg.org/multipage/input.html#checkbox-state-(type=checkbox)
22
 * @link https://developer.mozilla.org/docs/Web/HTML/Element/input/checkbox
23
 */
24
final class Checkbox extends InputField implements ValidationClassInterface
25
{
26
    use ValidationClassTrait;
27
28
    private ?string $uncheckValue = '0';
29
    private bool $enclosedByLabel = true;
30
    private ?string $inputLabel = null;
31
    private array $inputLabelAttributes = [];
32
    private bool $inputLabelEncode = true;
33
    private ?string $inputValue = null;
34
35
    /**
36
     * Identifies the element (or elements) that describes the object.
37
     *
38
     * @link https://w3c.github.io/aria/#aria-describedby
39
     */
40 2
    public function ariaDescribedBy(?string $value): self
41
    {
42 2
        $new = clone $this;
43 2
        $new->inputAttributes['aria-describedby'] = $value;
44 2
        return $new;
45
    }
46
47
    /**
48
     * Defines a string value that labels the current element.
49
     *
50
     * @link https://w3c.github.io/aria/#aria-label
51
     */
52 2
    public function ariaLabel(?string $value): self
53
    {
54 2
        $new = clone $this;
55 2
        $new->inputAttributes['aria-label'] = $value;
56 2
        return $new;
57
    }
58
59
    /**
60
     * Focus on the control (put cursor into it) when the page loads. Only one form element could be in focus
61
     * at the same time.
62
     *
63
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-fe-autofocus
64
     */
65 2
    public function autofocus(bool $value = true): self
66
    {
67 2
        $new = clone $this;
68 2
        $new->inputAttributes['autofocus'] = $value;
69 2
        return $new;
70
    }
71
72
    /**
73
     * @link https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled
74
     */
75 2
    public function disabled(bool $disabled = true): self
76
    {
77 2
        $new = clone $this;
78 2
        $new->inputAttributes['disabled'] = $disabled;
79 2
        return $new;
80
    }
81
82
    /**
83
     * If the input should be enclosed by label.
84
     *
85
     * @param bool $value If the input should be en closed by label.
86
     */
87 5
    public function enclosedByLabel(bool $value): self
88
    {
89 5
        $new = clone $this;
90 5
        $new->enclosedByLabel = $value;
91 5
        return $new;
92
    }
93
94
    /**
95
     * Label displayed next to the checkbox.
96
     *
97
     * When this option is specified, the checkbox will be enclosed by a label tag.
98
     *
99
     * @param string|null $value
100
     *
101
     * @link https://www.w3.org/TR/html52/sec-forms.html#the-label-element
102
     */
103 7
    public function inputLabel(?string $value): self
104
    {
105 7
        $new = clone $this;
106 7
        $new->inputLabel = $value;
107 7
        return $new;
108
    }
109
110
    /**
111
     * HTML attributes for the label tag.
112
     *
113
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
114
     */
115 2
    public function inputLabelAttributes(array $attributes): self
116
    {
117 2
        $new = clone $this;
118 2
        $new->inputLabelAttributes = $attributes;
119 2
        return $new;
120
    }
121
122 2
    public function addInputLabelAttributes(array $attributes): self
123
    {
124 2
        $new = clone $this;
125 2
        $new->inputLabelAttributes = array_merge($new->inputLabelAttributes, $attributes);
126 2
        return $new;
127
    }
128
129
    /**
130
     * Set enclosed label tag ID.
131
     *
132
     * @param string|null $id Label tag ID.
133
     */
134 3
    public function inputLabelId(?string $id): self
135
    {
136 3
        $new = clone $this;
137 3
        $new->inputLabelAttributes['id'] = $id;
138 3
        return $new;
139
    }
140
141
    /**
142
     * Replace enclosed label tag CSS classes with a new set of classes.
143
     *
144
     * @param string|null ...$class One or many CSS classes.
145
     */
146 7
    public function inputLabelClass(?string ...$class): self
147
    {
148 7
        $new = clone $this;
149 7
        $new->inputLabelAttributes['class'] = array_filter($class, static fn ($c) => $c !== null);
150 7
        return $new;
151
    }
152
153
    /**
154
     * Add one or more CSS classes to the enclosed label tag.
155
     *
156
     * @param string|null ...$class One or many CSS classes.
157
     */
158 9
    public function addInputLabelClass(?string ...$class): self
159
    {
160 9
        $new = clone $this;
161 9
        Html::addCssClass($new->inputLabelAttributes, $class);
162 9
        return $new;
163
    }
164
165 3
    public function inputLabelEncode(bool $encode): self
166
    {
167 3
        $new = clone $this;
168 3
        $new->inputLabelEncode = $encode;
169 3
        return $new;
170
    }
171
172 3
    public function inputValue(bool|float|int|string|Stringable|null $value): self
173
    {
174 3
        $new = clone $this;
175 3
        $new->inputValue = $this->prepareValue($value);
176 3
        return $new;
177
    }
178
179
    /**
180
     * The `tabindex` attribute indicates that its element can be focused, and where it participates in sequential
181
     * keyboard navigation (usually with the Tab key, hence the name).
182
     *
183
     * It accepts an integer as a value, with different results depending on the integer's value:
184
     *
185
     * - A negative value (usually `tabindex="-1"`) means that the element is not reachable via sequential keyboard
186
     *   navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
187
     *   with JavaScript.
188
     * - `tabindex="0"` means that the element should be focusable in sequential keyboard navigation, but its order is
189
     *   defined by the document's source order.
190
     * - A positive value means the element should be focusable in sequential keyboard navigation, with its order
191
     *   defined by the value of the number. That is, `tabindex="4"` is focused before `tabindex="5"`, but after
192
     *   `tabindex="3"`.
193
     *
194
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
195
     */
196 2
    public function tabIndex(?int $value): self
197
    {
198 2
        $new = clone $this;
199 2
        $new->inputAttributes['tabindex'] = $value;
200 2
        return $new;
201
    }
202
203
    /**
204
     * @param bool|float|int|string|Stringable|null $value Value that corresponds to "unchecked" state of the input.
205
     */
206 13
    public function uncheckValue(bool|float|int|string|Stringable|null $value): self
207
    {
208 13
        $new = clone $this;
209 13
        $new->uncheckValue = $this->prepareValue($value);
210 13
        return $new;
211
    }
212
213 50
    protected function generateInput(): string
214
    {
215 50
        $value = $this->getValue();
216
217 50
        if (!is_bool($value)
218 50
            && !is_string($value)
219 50
            && !is_numeric($value)
220 50
            && $value !== null
221 50
            && (!is_object($value) || !method_exists($value, '__toString'))
222
        ) {
223 1
            throw new InvalidArgumentException(
224 1
                'Checkbox widget requires a string, numeric, bool, Stringable or null value.'
225 1
            );
226
        }
227
228 49
        $value = $this->prepareValue($value);
229
230 49
        $inputAttributes = $this->getInputAttributes();
231
232 49
        $inputValue = $this->inputValue;
233 49
        $inputValue ??= $this->prepareValue($inputAttributes['value'] ?? null);
234 49
        unset($inputAttributes['value']);
235 49
        $inputValue ??= '1';
236
237 49
        $checkbox = Html::checkbox($this->getName(), $inputValue, $inputAttributes);
238
239 49
        if ($this->enclosedByLabel) {
240 45
            $label = $this->inputLabel ?? $this->label ?? $this->getInputData()->getLabel();
241 45
            $checkbox = $checkbox
242 45
                ->label($label, $this->inputLabelAttributes)
243 45
                ->labelEncode($this->inputLabelEncode);
244
        }
245
246 49
        $html = $checkbox
247 49
            ->checked($inputValue === $value)
248 49
            ->uncheckValue($this->uncheckValue)
249 49
            ->render();
250
251 49
        if (!$this->enclosedByLabel && $this->inputLabel !== null) {
252 3
            $html .= ' ' . ($this->inputLabelEncode ? Html::encode($this->inputLabel) : $this->inputLabel);
253
        }
254
255 49
        return $html;
256
    }
257
258 46
    protected function shouldHideLabel(): bool
259
    {
260 46
        return $this->enclosedByLabel;
261
    }
262
263 50
    private function prepareValue(mixed $value): ?string
264
    {
265 50
        if ($value === null) {
266 49
            return null;
267
        }
268
269 12
        if (is_bool($value)) {
270 2
            return $value ? '1' : '0';
271
        }
272
273 10
        return (string) $value;
274
    }
275
276 49
    protected function prepareContainerAttributes(array &$attributes): void
277
    {
278 49
        $this->addValidationClassToAttributes(
279 49
            $attributes,
280 49
            $this->getInputData(),
281 49
            $this->hasCustomError() ? true : null,
282 49
        );
283
    }
284
285 49
    protected function prepareInputAttributes(array &$attributes): void
286
    {
287 49
        $this->addInputValidationClassToAttributes(
288 49
            $attributes,
289 49
            $this->getInputData(),
290 49
            $this->hasCustomError() ? true : null,
291 49
        );
292
    }
293
}
294