Test Failed
Pull Request — master (#192)
by Sergei
02:42
created

Checkbox::shouldHideLabel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
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
    public function ariaDescribedBy(?string $value): self
41
    {
42
        $new = clone $this;
43
        $new->inputAttributes['aria-describedby'] = $value;
44
        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
    public function ariaLabel(?string $value): self
53
    {
54
        $new = clone $this;
55
        $new->inputAttributes['aria-label'] = $value;
56
        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
    public function autofocus(bool $value = true): self
66
    {
67
        $new = clone $this;
68
        $new->inputAttributes['autofocus'] = $value;
69
        return $new;
70
    }
71
72
    /**
73
     * @link https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled
74
     */
75
    public function disabled(bool $disabled = true): self
76
    {
77
        $new = clone $this;
78
        $new->inputAttributes['disabled'] = $disabled;
79
        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
    public function enclosedByLabel(bool $value): self
88
    {
89
        $new = clone $this;
90
        $new->enclosedByLabel = $value;
91
        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
    public function inputLabel(?string $value): self
106
    {
107
        $new = clone $this;
108
        $new->inputLabel = $value;
109
        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
    public function inputLabelAttributes(array $attributes): self
118
    {
119
        $new = clone $this;
120
        $new->inputLabelAttributes = array_merge($new->inputLabelAttributes, $attributes);
121
        return $new;
122
    }
123
124
    public function replaceInputLabelAttributes(array $attributes): self
125
    {
126
        $new = clone $this;
127
        $new->inputLabelAttributes = $attributes;
128
        return $new;
129
    }
130
131
    public function inputLabelEncode(bool $encode): self
132
    {
133
        $new = clone $this;
134
        $new->inputLabelEncode = $encode;
135
        return $new;
136
    }
137
138
    public function inputValue(bool|float|int|string|Stringable|null $value): self
139
    {
140
        $new = clone $this;
141
        $new->inputValue = $this->prepareValue($value);
142
        return $new;
143
    }
144
145
    /**
146
     * The `tabindex` attribute indicates that its element can be focused, and where it participates in sequential
147
     * keyboard navigation (usually with the Tab key, hence the name).
148
     *
149
     * It accepts an integer as a value, with different results depending on the integer's value:
150
     *
151
     * - A negative value (usually `tabindex="-1"`) means that the element is not reachable via sequential keyboard
152
     *   navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
153
     *   with JavaScript.
154
     * - `tabindex="0"` means that the element should be focusable in sequential keyboard navigation, but its order is
155
     *   defined by the document's source order.
156
     * - A positive value means the element should be focusable in sequential keyboard navigation, with its order
157
     *   defined by the value of the number. That is, `tabindex="4"` is focused before `tabindex="5"`, but after
158
     *   `tabindex="3"`.
159
     *
160
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
161
     */
162
    public function tabIndex(?int $value): self
163
    {
164
        $new = clone $this;
165
        $new->inputAttributes['tabindex'] = $value;
166
        return $new;
167
    }
168
169
    /**
170
     * @param bool|float|int|string|Stringable|null $value Value that corresponds to "unchecked" state of the input.
171
     */
172
    public function uncheckValue(bool|float|int|string|Stringable|null $value): self
173
    {
174
        $new = clone $this;
175
        $new->uncheckValue = $this->prepareValue($value);
176
        return $new;
177
    }
178
179
    protected function generateInput(): string
180
    {
181
        $value = $this->getFormAttributeValue();
182
183
        if (!is_bool($value)
184
            && !is_string($value)
185
            && !is_numeric($value)
186
            && $value !== null
187
            && (!is_object($value) || !method_exists($value, '__toString'))
188
        ) {
189
            throw new InvalidArgumentException(
190
                'Checkbox widget requires a string, numeric, bool, Stringable or null value.'
191
            );
192
        }
193
194
        $value = $this->prepareValue($value);
195
196
        $inputAttributes = $this->getInputAttributes();
197
198
        $inputValue = $this->inputValue;
199
        $inputValue = $inputValue ?? $this->prepareValue($inputAttributes['value'] ?? null);
200
        unset($inputAttributes['value']);
201
        $inputValue = $inputValue ?? '1';
202
203
        $checkbox = Html::checkbox($this->getInputName(), $inputValue, $inputAttributes);
204
205
        $label = $this->inputLabel ?? $this->getFormAttributeLabel();
206
207
        if ($this->enclosedByLabel) {
208
            $checkbox = $checkbox
209
                ->label($label, $this->inputLabelAttributes)
210
                ->labelEncode($this->inputLabelEncode);
211
        }
212
213
        $html = $checkbox
214
            ->checked($inputValue === $value)
215
            ->uncheckValue($this->uncheckValue)
216
            ->render();
217
218
        if (!$this->enclosedByLabel && $this->inputLabel !== null) {
219
            $html .= ' ' . ($this->inputLabelEncode ? Html::encode($this->inputLabel) : $this->inputLabel);
220
        }
221
222
        return $html;
223
    }
224
225
    protected function shouldHideLabel(): bool
226
    {
227
        return $this->enclosedByLabel;
228
    }
229
230
    private function prepareValue(mixed $value): ?string
231
    {
232
        if ($value === null) {
233
            return null;
234
        }
235
236
        if (is_bool($value)) {
237
            return $value ? '1' : '0';
238
        }
239
240
        return (string) $value;
241
    }
242
243
    protected function prepareContainerAttributes(array &$attributes): void
244
    {
245
        if ($this->hasFormModelAndAttribute()) {
246
            $this->addValidationClassToAttributes(
247
                $attributes,
248
                $this->getFormModel(),
249
                $this->getFormAttributeName(),
250
            );
251
        }
252
    }
253
}
254