Passed
Pull Request — master (#192)
by Alexander
28:18 queued 25:44
created

Checkbox   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 219
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 78
c 2
b 0
f 0
dl 0
loc 219
ccs 84
cts 84
cp 1
rs 10
wmc 29

15 Methods

Rating   Name   Duplication   Size   Complexity  
A shouldHideLabel() 0 3 1
A prepareValue() 0 11 4
A tabIndex() 0 5 1
A uncheckValue() 0 5 1
A disabled() 0 5 1
A inputLabel() 0 5 1
A inputLabelEncode() 0 5 1
A enclosedByLabel() 0 5 1
A prepareContainerTagAttributes() 0 7 2
A autofocus() 0 5 1
A ariaLabel() 0 5 1
A inputValue() 0 5 1
B generateInput() 0 44 11
A ariaDescribedBy() 0 5 1
A inputLabelAttributes() 0 5 1
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->inputTagAttributes['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->inputTagAttributes['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->inputTagAttributes['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->inputTagAttributes['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
     * @return self
102
     *
103
     * @link https://www.w3.org/TR/html52/sec-forms.html#the-label-element
104
     */
105 7
    public function inputLabel(?string $value): self
106
    {
107 7
        $new = clone $this;
108 7
        $new->inputLabel = $value;
109 7
        return $new;
110
    }
111
112
    /**
113
     * HTML attributes for the label tag.
114
     *
115
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
116
     */
117 2
    public function inputLabelAttributes(array $attributes): self
118
    {
119 2
        $new = clone $this;
120 2
        $new->inputLabelAttributes = $attributes;
121 2
        return $new;
122
    }
123
124 2
    public function inputLabelEncode(bool $encode): self
125
    {
126 2
        $new = clone $this;
127 2
        $new->inputLabelEncode = $encode;
128 2
        return $new;
129
    }
130
131 3
    public function inputValue(bool|float|int|string|Stringable|null $value): self
132
    {
133 3
        $new = clone $this;
134 3
        $new->inputValue = $this->prepareValue($value);
135 3
        return $new;
136
    }
137
138
    /**
139
     * The `tabindex` attribute indicates that its element can be focused, and where it participates in sequential
140
     * keyboard navigation (usually with the Tab key, hence the name).
141
     *
142
     * It accepts an integer as a value, with different results depending on the integer's value:
143
     *
144
     * - A negative value (usually `tabindex="-1"`) means that the element is not reachable via sequential keyboard
145
     *   navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
146
     *   with JavaScript.
147
     * - `tabindex="0"` means that the element should be focusable in sequential keyboard navigation, but its order is
148
     *   defined by the document's source order.
149
     * - A positive value means the element should be focusable in sequential keyboard navigation, with its order
150
     *   defined by the value of the number. That is, `tabindex="4"` is focused before `tabindex="5"`, but after
151
     *   `tabindex="3"`.
152
     *
153
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
154
     */
155 2
    public function tabIndex(?int $value): self
156
    {
157 2
        $new = clone $this;
158 2
        $new->inputTagAttributes['tabindex'] = $value;
159 2
        return $new;
160
    }
161
162
    /**
163
     * @param bool|float|int|string|Stringable|null $value Value that corresponds to "unchecked" state of the input.
164
     */
165 13
    public function uncheckValue(bool|float|int|string|Stringable|null $value): self
166
    {
167 13
        $new = clone $this;
168 13
        $new->uncheckValue = $this->prepareValue($value);
169 13
        return $new;
170
    }
171
172 26
    protected function generateInput(): string
173
    {
174 26
        $value = $this->getAttributeValue();
175
176 26
        if (!is_bool($value)
177 26
            && !is_string($value)
178 26
            && !is_numeric($value)
179 26
            && $value !== null
180 26
            && (!is_object($value) || !method_exists($value, '__toString'))
181
        ) {
182 1
            throw new InvalidArgumentException(
183
                'Checkbox widget requires a string, numeric, bool, Stringable or null value.'
184
            );
185
        }
186
187 25
        $value = $this->prepareValue($value);
188
189 25
        $tagAttributes = $this->getInputTagAttributes();
190
191 25
        $inputValue = $this->inputValue;
192 25
        $inputValue = $inputValue ?? $this->prepareValue($tagAttributes['value'] ?? null);
193 25
        unset($tagAttributes['value']);
194 25
        $inputValue = $inputValue ?? '1';
195
196 25
        $checkbox = Html::checkbox($this->getInputName(), $inputValue, $tagAttributes);
197
198 25
        $label = $this->inputLabel ?? $this->getAttributeLabel();
199
200 25
        if ($this->enclosedByLabel) {
201 21
            $checkbox = $checkbox
202 21
                ->label($label, $this->inputLabelAttributes)
203 21
                ->labelEncode($this->inputLabelEncode);
204
        }
205
206 25
        $html = $checkbox
207 25
            ->checked($inputValue === $value)
208 25
            ->uncheckValue($this->uncheckValue)
209 25
            ->render();
210
211 25
        if (!$this->enclosedByLabel && $this->inputLabel !== null) {
212 3
            $html .= ' ' . ($this->inputLabelEncode ? Html::encode($this->inputLabel) : $this->inputLabel);
213
        }
214
215 25
        return $html;
216
    }
217
218 23
    protected function shouldHideLabel(): bool
219
    {
220 23
        return $this->enclosedByLabel;
221
    }
222
223 26
    private function prepareValue(mixed $value): ?string
224
    {
225 26
        if ($value === null) {
226 24
            return null;
227
        }
228
229 25
        if (is_bool($value)) {
230 24
            return $value ? '1' : '0';
231
        }
232
233 6
        return (string) $value;
234
    }
235
236 25
    protected function prepareContainerTagAttributes(array &$attributes): void
237
    {
238 25
        if ($this->hasFormModelAndAttribute()) {
239 25
            $this->addValidationClassToTagAttributes(
240
                $attributes,
241 25
                $this->getFormModel(),
242 25
                $this->getAttributeName(),
243
            );
244
        }
245
    }
246
}
247