Passed
Pull Request — master (#192)
by Sergei
07:08 queued 03:58
created

Checkbox::uncheckValue()   A

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