3 namespace dokuwiki\Form
;
6 * Class DropdownElement
8 * Represents a HTML select. Please not that prefilling with input data only works for single values.
10 * @package dokuwiki\Form
12 class DropdownElement
extends InputElement
14 /** @var array OptGroup[] */
15 protected $optGroups = [];
17 /** @var string[] the currently set values */
18 protected $values = [];
21 * @param string $name The name of this form element
22 * @param array $options The available options
23 * @param string $label The label text for this element (will be autoescaped)
25 public function __construct($name, $options, $label = '')
27 parent
::__construct('dropdown', $name, $label);
28 $this->rmattr('type');
29 $this->optGroups
[''] = new OptGroup(null, $options);
34 * Add an `<optgroup>` and respective options
36 * @param string $label
37 * @param array $options
38 * @return OptGroup a reference to the added optgroup
39 * @throws \InvalidArgumentException
41 public function addOptGroup($label, $options)
44 throw new \
InvalidArgumentException(hsc('<optgroup> must have a label!'));
46 $this->optGroups
[$label] = new OptGroup($label, $options);
47 return end($this->optGroups
);
51 * Set or get the optgroups of an Dropdown-Element.
53 * optgroups have to be given as associative array
54 * * the key being the label of the group
55 * * the value being an array of options as defined in @param null|array $optGroups
56 * @return OptGroup[]|DropdownElement
57 * @see OptGroup::options()
60 public function optGroups($optGroups = null)
62 if ($optGroups === null) {
63 return $this->optGroups
;
65 if (!is_array($optGroups)) {
66 throw new \
InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!'));
68 $this->optGroups
= [];
69 foreach ($optGroups as $label => $options) {
70 $this->addOptGroup($label, $options);
76 * Get or set the options of the Dropdown
78 * Options can be given as associative array (value => label) or as an
79 * indexd array (label = value) or as an array of arrays. In the latter
80 * case an element has to look as follows:
81 * option-value => array (
82 * 'label' => option-label,
84 * attr-key => attr-value, ...
88 * @param null|array $options
91 public function options($options = null)
93 if ($options === null) {
94 return $this->optGroups
['']->options();
96 $this->optGroups
[''] = new OptGroup(null, $options);
101 * Get or set the current value
103 * When setting a value that is not defined in the options, the value is ignored
104 * and the first option's value is selected instead
106 * @param null|string|string[] $value The value to set
107 * @return $this|string|string[]
109 public function val($value = null)
112 if ($value === null) {
113 if (isset($this->attributes
['multiple'])) {
114 return $this->values
;
116 return $this->values
[0];
121 $this->values
= $this->setValuesInOptGroups((array) $value);
122 if (!$this->values
) {
123 // unknown value set, select first option instead
124 $this->values
= $this->setValuesInOptGroups((array) $this->getFirstOptionKey());
131 * Returns the first option's key
135 protected function getFirstOptionKey()
137 $options = $this->options();
138 if (!empty($options)) {
139 $keys = array_keys($options);
140 return (string)array_shift($keys);
142 foreach ($this->optGroups
as $optGroup) {
143 $options = $optGroup->options();
144 if (!empty($options)) {
145 $keys = array_keys($options);
146 return (string)array_shift($keys);
150 return ''; // should not happen
154 * Set the value in the OptGroups, including the optgroup for the options without optgroup.
156 * @param string[] $values The values to be set
157 * @return string[] The values actually set
159 protected function setValuesInOptGroups($values)
163 /** @var OptGroup $optGroup */
164 foreach ($this->optGroups
as $optGroup) {
165 $found = $optGroup->storeValues($values);
166 $values = array_diff($values, $found);
167 $valueset = array_merge($valueset, $found);
174 * Create the HTML for the select it self
178 protected function mainElementHTML()
180 $attr = $this->attrs();
181 if (isset($attr['multiple'])) {
182 // use array notation when multiple values are allowed
183 $attr['name'] .= '[]';
184 } elseif ($this->useInput
) {
185 // prefilling is only supported for non-multi fields
186 $this->prefillInput();
189 $html = '<select ' . buildAttributes($attr) . '>';
190 $html = array_reduce(
192 static fn($html, OptGroup
$optGroup) => $html . $optGroup->toHTML(),
195 $html .= '</select>';