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

Range   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 70
c 2
b 0
f 0
dl 0
loc 208
ccs 78
cts 78
cp 1
rs 10
wmc 28

15 Methods

Rating   Name   Duplication   Size   Complexity  
A autofocus() 0 5 1
A min() 0 5 1
A list() 0 5 1
A tabIndex() 0 5 1
A step() 0 5 1
B beforeRender() 0 16 8
A ariaDescribedBy() 0 5 1
A max() 0 5 1
A generateInput() 0 17 5
A outputTagName() 0 9 2
A prepareContainerTagAttributes() 0 7 2
A showOutput() 0 5 1
A ariaLabel() 0 5 1
A outputTagAttributes() 0 5 1
A disabled() 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\EnrichmentFromRules\EnrichmentFromRulesInterface;
10
use Yiisoft\Form\Field\Base\EnrichmentFromRules\EnrichmentFromRulesTrait;
11
use Yiisoft\Form\Field\Base\InputField;
12
use Yiisoft\Form\Field\Base\ValidationClass\ValidationClassInterface;
13
use Yiisoft\Form\Field\Base\ValidationClass\ValidationClassTrait;
14
use Yiisoft\Html\Html;
15
use Yiisoft\Validator\Rule\Number as NumberRule;
16
use Yiisoft\Validator\Rule\Required;
17
18
use function is_string;
19
20
/**
21
 * Represents `<input>` element of type "range" are let the user specify a numeric value which must be no less than
22
 * a given value, and no more than another given value.
23
 *
24
 * @link https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range)
25
 * @link https://developer.mozilla.org/docs/Web/HTML/Element/input/range
26
 */
27
final class Range extends InputField implements EnrichmentFromRulesInterface, ValidationClassInterface
28
{
29
    use EnrichmentFromRulesTrait;
30
    use ValidationClassTrait;
31
32
    private bool $showOutput = false;
33
34
    /**
35
     * @psalm-var non-empty-string
36
     */
37
    private string $outputTagName = 'span';
38
    private array $outputTagAttributes = [];
39
40
    /**
41
     * Maximum value.
42
     *
43
     * @link https://html.spec.whatwg.org/multipage/input.html#attr-input-max
44
     */
45 8
    public function max(float|int|string|Stringable|null $value): self
46
    {
47 8
        $new = clone $this;
48 8
        $new->inputTagAttributes['max'] = $value;
49 8
        return $new;
50
    }
51
52
    /**
53
     * Minimum value.
54
     *
55
     * @link https://html.spec.whatwg.org/multipage/input.html#attr-input-min
56
     */
57 8
    public function min(float|int|string|Stringable|null $value): self
58
    {
59 8
        $new = clone $this;
60 8
        $new->inputTagAttributes['min'] = $value;
61 8
        return $new;
62
    }
63
64
    /**
65
     * Granularity to be matched by the form control's value.
66
     *
67
     * @link https://html.spec.whatwg.org/multipage/input.html#attr-input-step
68
     */
69 5
    public function step(float|int|string|Stringable|null $value): self
70
    {
71 5
        $new = clone $this;
72 5
        $new->inputTagAttributes['step'] = $value;
73 5
        return $new;
74
    }
75
76
    /**
77
     * ID of element that lists predefined options suggested to the user.
78
     *
79
     * @link https://html.spec.whatwg.org/multipage/input.html#the-list-attribute
80
     */
81 1
    public function list(?string $id): self
82
    {
83 1
        $new = clone $this;
84 1
        $new->inputTagAttributes['list'] = $id;
85 1
        return $new;
86
    }
87
88
    /**
89
     * @link https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled
90
     */
91 1
    public function disabled(bool $disabled = true): self
92
    {
93 1
        $new = clone $this;
94 1
        $new->inputTagAttributes['disabled'] = $disabled;
95 1
        return $new;
96
    }
97
98
    /**
99
     * Identifies the element (or elements) that describes the object.
100
     *
101
     * @link https://w3c.github.io/aria/#aria-describedby
102
     */
103 1
    public function ariaDescribedBy(?string $value): self
104
    {
105 1
        $new = clone $this;
106 1
        $new->inputTagAttributes['aria-describedby'] = $value;
107 1
        return $new;
108
    }
109
110
    /**
111
     * Defines a string value that labels the current element.
112
     *
113
     * @link https://w3c.github.io/aria/#aria-label
114
     */
115 1
    public function ariaLabel(?string $value): self
116
    {
117 1
        $new = clone $this;
118 1
        $new->inputTagAttributes['aria-label'] = $value;
119 1
        return $new;
120
    }
121
122
    /**
123
     * Focus on the control (put cursor into it) when the page loads. Only one form element could be in focus
124
     * at the same time.
125
     *
126
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-fe-autofocus
127
     */
128 1
    public function autofocus(bool $value = true): self
129
    {
130 1
        $new = clone $this;
131 1
        $new->inputTagAttributes['autofocus'] = $value;
132 1
        return $new;
133
    }
134
135
    /**
136
     * The `tabindex` attribute indicates that its element can be focused, and where it participates in sequential
137
     * keyboard navigation (usually with the Tab key, hence the name).
138
     *
139
     * It accepts an integer as a value, with different results depending on the integer's value:
140
     *
141
     * - A negative value (usually `tabindex="-1"`) means that the element is not reachable via sequential keyboard
142
     *   navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
143
     *   with JavaScript.
144
     * - `tabindex="0"` means that the element should be focusable in sequential keyboard navigation, but its order is
145
     *   defined by the document's source order.
146
     * - A positive value means the element should be focusable in sequential keyboard navigation, with its order
147
     *   defined by the value of the number. That is, `tabindex="4"` is focused before `tabindex="5"`, but after
148
     *   `tabindex="3"`.
149
     *
150
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
151
     */
152 1
    public function tabIndex(?int $value): self
153
    {
154 1
        $new = clone $this;
155 1
        $new->inputTagAttributes['tabindex'] = $value;
156 1
        return $new;
157
    }
158
159 2
    public function showOutput(bool $show = true): self
160
    {
161 2
        $new = clone $this;
162 2
        $new->showOutput = $show;
163 2
        return $new;
164
    }
165
166 2
    public function outputTagName(string $tagName): self
167
    {
168 2
        if ($tagName === '') {
169 1
            throw new InvalidArgumentException('The output tag name it cannot be empty value.');
170
        }
171
172 1
        $new = clone $this;
173 1
        $new->outputTagName = $tagName;
174 1
        return $new;
175
    }
176
177 2
    public function outputTagAttributes(array $attributes): self
178
    {
179 2
        $new = clone $this;
180 2
        $new->outputTagAttributes = $attributes;
181 2
        return $new;
182
    }
183
184
    /**
185
     * @psalm-suppress MixedAssignment,MixedArgument Remove after fix https://github.com/yiisoft/validator/issues/225
186
     */
187 28
    protected function beforeRender(): void
188
    {
189 28
        parent::beforeRender();
190 28
        if ($this->enrichmentFromRules && $this->hasFormModelAndAttribute()) {
191 2
            $rules = $this->getFormModel()->getRules()[$this->getAttributeName()] ?? [];
192 2
            foreach ($rules as $rule) {
193 2
                if ($rule instanceof Required) {
194 1
                    $this->inputTagAttributes['required'] = true;
195
                }
196
197 2
                if ($rule instanceof NumberRule) {
198 1
                    if (null !== $min = $rule->getOptions()['min']) {
199 1
                        $this->inputTagAttributes['min'] = $min;
200
                    }
201 1
                    if (null !== $max = $rule->getOptions()['max']) {
202 1
                        $this->inputTagAttributes['max'] = $max;
203
                    }
204
                }
205
            }
206
        }
207
    }
208
209 28
    protected function generateInput(): string
210
    {
211 28
        $value = $this->getAttributeValue();
212
213 28
        if (!is_string($value) && !is_numeric($value) && $value !== null) {
214 1
            throw new InvalidArgumentException('Range field requires a string, numeric or null value.');
215
        }
216
217 27
        $tag = Html::range($this->getInputName(), $value, $this->getInputTagAttributes());
218 27
        if ($this->showOutput) {
219 2
            $tag = $tag
220 2
                ->showOutput()
221 2
                ->outputTagName($this->outputTagName)
222 2
                ->outputTagAttributes($this->outputTagAttributes);
223
        }
224
225 27
        return $tag->render();
226
    }
227
228 4
    protected function prepareContainerTagAttributes(array &$attributes): void
229
    {
230 4
        if ($this->hasFormModelAndAttribute()) {
231 4
            $this->addValidationClassToTagAttributes(
232
                $attributes,
233 4
                $this->getFormModel(),
234 4
                $this->getAttributeName(),
235
            );
236
        }
237
    }
238
}
239