Passed
Pull Request — master (#192)
by Alexander
05:43 queued 02:47
created

Checkbox::shouldHideLabel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
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
 */
23
final class Checkbox extends InputField implements ValidationClassInterface
24
{
25
    use ValidationClassTrait;
26
27
    private ?string $uncheckValue = '0';
28
    private bool $enclosedByLabel = true;
29
    private ?string $inputLabel = null;
30
    private array $inputLabelAttributes = [];
31
    private bool $inputLabelEncode = true;
32
    private ?string $inputValue = null;
33
34
    /**
35
     * @param bool|float|int|string|Stringable|null $value Value that corresponds to "unchecked" state of the input.
36
     */
37 13
    public function uncheckValue(bool|float|int|string|Stringable|null $value): self
38
    {
39 13
        $new = clone $this;
40 13
        $new->uncheckValue = $this->prepareValue($value);
41 13
        return $new;
42
    }
43
44
    /**
45
     * If the input should be enclosed by label.
46
     *
47
     * @param bool $value If the input should be en closed by label.
48
     */
49 5
    public function enclosedByLabel(bool $value): self
50
    {
51 5
        $new = clone $this;
52 5
        $new->enclosedByLabel = $value;
53 5
        return $new;
54
    }
55
56
    /**
57
     * Label displayed next to the checkbox.
58
     *
59
     * When this option is specified, the checkbox will be enclosed by a label tag.
60
     *
61
     * @param string|null $value
62
     *
63
     * @return self
64
     *
65
     * @link https://www.w3.org/TR/html52/sec-forms.html#the-label-element
66
     */
67 7
    public function inputLabel(?string $value): self
68
    {
69 7
        $new = clone $this;
70 7
        $new->inputLabel = $value;
71 7
        return $new;
72
    }
73
74
    /**
75
     * HTML attributes for the label tag.
76
     *
77
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
78
     */
79 2
    public function inputLabelAttributes(array $attributes): self
80
    {
81 2
        $new = clone $this;
82 2
        $new->inputLabelAttributes = $attributes;
83 2
        return $new;
84
    }
85
86 2
    public function inputLabelEncode(bool $encode): self
87
    {
88 2
        $new = clone $this;
89 2
        $new->inputLabelEncode = $encode;
90 2
        return $new;
91
    }
92
93 3
    public function inputValue(bool|float|int|string|Stringable|null $value): self
94
    {
95 3
        $new = clone $this;
96 3
        $new->inputValue = $this->prepareValue($value);
97 3
        return $new;
98
    }
99
100
    /**
101
     * @link https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled
102
     */
103 2
    public function disabled(bool $disabled = true): self
104
    {
105 2
        $new = clone $this;
106 2
        $new->inputTagAttributes['disabled'] = $disabled;
107 2
        return $new;
108
    }
109
110
    /**
111
     * Identifies the element (or elements) that describes the object.
112
     *
113
     * @link https://w3c.github.io/aria/#aria-describedby
114
     */
115 2
    public function ariaDescribedBy(?string $value): self
116
    {
117 2
        $new = clone $this;
118 2
        $new->inputTagAttributes['aria-describedby'] = $value;
119 2
        return $new;
120
    }
121
122
    /**
123
     * Defines a string value that labels the current element.
124
     *
125
     * @link https://w3c.github.io/aria/#aria-label
126
     */
127 2
    public function ariaLabel(?string $value): self
128
    {
129 2
        $new = clone $this;
130 2
        $new->inputTagAttributes['aria-label'] = $value;
131 2
        return $new;
132
    }
133
134
    /**
135
     * Focus on the control (put cursor into it) when the page loads. Only one form element could be in focus
136
     * at the same time.
137
     *
138
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-fe-autofocus
139
     */
140 2
    public function autofocus(bool $value = true): self
141
    {
142 2
        $new = clone $this;
143 2
        $new->inputTagAttributes['autofocus'] = $value;
144 2
        return $new;
145
    }
146
147
    /**
148
     * The `tabindex` attribute indicates that its element can be focused, and where it participates in sequential
149
     * keyboard navigation (usually with the Tab key, hence the name).
150
     *
151
     * It accepts an integer as a value, with different results depending on the integer's value:
152
     *
153
     * - A negative value (usually `tabindex="-1"`) means that the element is not reachable via sequential keyboard
154
     *   navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
155
     *   with JavaScript.
156
     * - `tabindex="0"` means that the element should be focusable in sequential keyboard navigation, but its order is
157
     *   defined by the document's source order.
158
     * - A positive value means the element should be focusable in sequential keyboard navigation, with its order
159
     *   defined by the value of the number. That is, `tabindex="4"` is focused before `tabindex="5"`, but after
160
     *   `tabindex="3"`.
161
     *
162
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
163
     */
164 2
    public function tabIndex(?int $value): self
165
    {
166 2
        $new = clone $this;
167 2
        $new->inputTagAttributes['tabindex'] = $value;
168 2
        return $new;
169
    }
170
171 26
    protected function generateInput(): string
172
    {
173 26
        $value = $this->getAttributeValue();
174
175 26
        if (!is_bool($value)
176 26
            && !is_string($value)
177 26
            && !is_numeric($value)
178 26
            && $value !== null
179 26
            && (!is_object($value) || !method_exists($value, '__toString'))
180
        ) {
181 1
            throw new InvalidArgumentException(
182
                'Checkbox widget requires a string, numeric, bool, Stringable or null value.'
183
            );
184
        }
185
186 25
        $value = $this->prepareValue($value);
187
188 25
        $tagAttributes = $this->getInputTagAttributes();
189
190 25
        $inputValue = $this->inputValue;
191 25
        $inputValue = $inputValue ?? $this->prepareValue($tagAttributes['value'] ?? null);
192 25
        unset($tagAttributes['value']);
193 25
        $inputValue = $inputValue ?? '1';
194
195 25
        $checkbox = Html::checkbox($this->getInputName(), $inputValue, $tagAttributes);
196
197 25
        $label = $this->inputLabel ?? $this->getAttributeLabel();
198
199 25
        if ($this->enclosedByLabel) {
200 21
            $checkbox = $checkbox
201 21
                ->label($label, $this->inputLabelAttributes)
202 21
                ->labelEncode($this->inputLabelEncode);
203
        }
204
205 25
        $html = $checkbox
206 25
            ->checked($inputValue === $value)
207 25
            ->uncheckValue($this->uncheckValue)
208 25
            ->render();
209
210 25
        if (!$this->enclosedByLabel && $this->inputLabel !== null) {
211 3
            $html .= ' ' . ($this->inputLabelEncode ? Html::encode($this->inputLabel) : $this->inputLabel);
212
        }
213
214 25
        return $html;
215
    }
216
217 23
    protected function shouldHideLabel(): bool
218
    {
219 23
        return $this->enclosedByLabel;
220
    }
221
222 26
    private function prepareValue(mixed $value): ?string
223
    {
224 26
        if ($value === null) {
225 24
            return null;
226
        }
227
228 25
        if (is_bool($value)) {
229 24
            return $value ? '1' : '0';
230
        }
231
232 6
        return (string) $value;
233
    }
234
235 25
    protected function prepareContainerTagAttributes(array &$attributes): void
236
    {
237 25
        if ($this->hasFormModelAndAttribute()) {
238 25
            $this->addValidationClassToTagAttributes(
239
                $attributes,
240 25
                $this->getFormModel(),
241 25
                $this->getAttributeName(),
242
            );
243
        }
244
    }
245
}
246