summaryrefslogtreecommitdiffstats
path: root/vendor/ipl/html/src/FormElement/RadioElement.php
blob: 831671c85a7d0fba52d4f39929487ddab68f9446 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
<?php

namespace ipl\Html\FormElement;

use InvalidArgumentException;
use ipl\Html\Attributes;
use ipl\Html\HtmlDocument;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\I18n\Translation;
use ipl\Validator\DeferredInArrayValidator;
use ipl\Validator\ValidatorChain;

class RadioElement extends BaseFormElement
{
    use Translation;

    /** @var string The element type */
    protected $type = 'radio';

    /** @var RadioOption[] Radio options */
    protected $options = [];

    /** @var array Disabled radio options */
    protected $disabledOptions = [];

    /**
     * Set the options
     *
     * @param array $options
     *
     * @return $this
     */
    public function setOptions(array $options): self
    {
        $this->options = [];
        foreach ($options as $value => $label) {
            $option = (new RadioOption($value, $label))
                ->setDisabled(
                    in_array($value, $this->disabledOptions, ! is_int($value))
                    || ($value === '' && in_array(null, $this->disabledOptions, true))
                );

            $this->options[$value] = $option;
        }

        $this->disabledOptions = [];

        return $this;
    }

    /**
     * Get the option with specified value
     *
     * @param string|int $value
     *
     * @return RadioOption
     *
     * @throws InvalidArgumentException If no option with the specified value exists
     */
    public function getOption($value): RadioOption
    {
        if (! isset($this->options[$value])) {
            throw new InvalidArgumentException(sprintf('There is no such option "%s"', $value));
        }

        return $this->options[$value];
    }

    /**
     * Set the specified options as disable
     *
     * @param array $disabledOptions
     *
     * @return $this
     */
    public function setDisabledOptions(array $disabledOptions): self
    {
        if (! empty($this->options)) {
            foreach ($this->options as $value => $option) {
                $option->setDisabled(
                    in_array($value, $disabledOptions, ! is_int($value))
                    || ($value === '' && in_array(null, $disabledOptions, true))
                );
            }

            $this->disabledOptions = [];
        } else {
            $this->disabledOptions = $disabledOptions;
        }

        return $this;
    }

    public function renderUnwrapped()
    {
        // Parent::renderUnwrapped() requires $tag and the content should be empty. However, since we are wrapping
        // each button in a label, the call to parent cannot work here and must be overridden.
        return HtmlDocument::renderUnwrapped();
    }

    protected function assemble()
    {
        foreach ($this->options as $option) {
            $radio = (new InputElement($this->getValueOfNameAttribute()))
                ->setType($this->type)
                ->setValue($option->getValue());

            // Only add the non-callback attributes to all options
            foreach ($this->getAttributes() as $attribute) {
                $radio->getAttributes()->addAttribute(clone $attribute);
            }

            $radio->getAttributes()
                ->merge($option->getAttributes())
                ->registerAttributeCallback(
                    'checked',
                    function () use ($option) {
                        $optionValue = $option->getValue();

                        return ! is_int($optionValue)
                            ? $this->getValue() === $optionValue
                            : $this->getValue() == $optionValue;
                    }
                )
                ->registerAttributeCallback(
                    'disabled',
                    function () use ($option) {
                        return $this->getAttributes()->get('disabled')->getValue() || $option->isDisabled();
                    }
                );

            $label = new HtmlElement(
                'label',
                new Attributes(['class' => $option->getLabelCssClass()]),
                $radio,
                Text::create($option->getLabel())
            );

            $this->addHtml($label);
        }
    }

    protected function addDefaultValidators(ValidatorChain $chain): void
    {
        $chain->add(new DeferredInArrayValidator(function (): array {
            $possibleValues = [];

            foreach ($this->options as $option) {
                if ($option->isDisabled()) {
                    continue;
                }

                $possibleValues[] = $option->getValue();
            }

            return $possibleValues;
        }));
    }

    protected function registerAttributeCallbacks(Attributes $attributes)
    {
        parent::registerAttributeCallbacks($attributes);

        $this->getAttributes()->registerAttributeCallback(
            'options',
            null,
            [$this, 'setOptions']
        );

        $this->getAttributes()->registerAttributeCallback(
            'disabledOptions',
            null,
            [$this, 'setDisabledOptions']
        );
    }
}