-
-
Notifications
You must be signed in to change notification settings - Fork 840
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for optgroup-tags for select-fields #1778
Changes from all commits
693978b
238a072
b0eca9c
068abb2
2bd1d2c
45082b9
9c3fca6
d1bbf58
017eef9
4eed441
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,18 +10,59 @@ | |
*/ | ||
class DropdownElement extends InputElement { | ||
|
||
protected $options = array(); | ||
|
||
protected $value = ''; | ||
/** @var array OptGroup[] */ | ||
protected $optGroups = array(); | ||
|
||
/** | ||
* @param string $name The name of this form element | ||
* @param string $options The available options | ||
* @param array $options The available options | ||
* @param string $label The label text for this element (will be autoescaped) | ||
*/ | ||
public function __construct($name, $options, $label = '') { | ||
parent::__construct('dropdown', $name, $label); | ||
$this->options($options); | ||
$this->rmattr('type'); | ||
$this->optGroups[''] = new OptGroup(null, $options); | ||
$this->val(''); | ||
} | ||
|
||
/** | ||
* Add an `<optgroup>` and respective options | ||
* | ||
* @param string $label | ||
* @param array $options | ||
* @return OptGroup a reference to the added optgroup | ||
* @throws \Exception | ||
*/ | ||
public function addOptGroup($label, $options) { | ||
if (empty($label)) { | ||
throw new \InvalidArgumentException(hsc('<optgroup> must have a label!')); | ||
} | ||
$this->optGroups[$label] = new OptGroup($label, $options); | ||
return end($this->optGroups); | ||
} | ||
|
||
/** | ||
* Set or get the optgroups of an Dropdown-Element. | ||
* | ||
* optgroups have to be given as associative array | ||
* * the key being the label of the group | ||
* * the value being an array of options as defined in @see OptGroup::options() | ||
* | ||
* @param null|array $optGroups | ||
* @return OptGroup[]|DropdownElement | ||
*/ | ||
public function optGroups($optGroups = null) { | ||
if($optGroups === null) { | ||
return $this->optGroups; | ||
} | ||
if (!is_array($optGroups)) { | ||
throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!')); | ||
} | ||
$this->optGroups = array(); | ||
foreach ($optGroups as $label => $options) { | ||
$this->addOptGroup($label, $options); | ||
} | ||
return $this; | ||
} | ||
|
||
/** | ||
|
@@ -41,21 +82,10 @@ public function __construct($name, $options, $label = '') { | |
* @return $this|array | ||
*/ | ||
public function options($options = null) { | ||
if($options === null) return $this->options; | ||
if(!is_array($options)) throw new \InvalidArgumentException('Options have to be an array'); | ||
$this->options = array(); | ||
|
||
foreach($options as $key => $val) { | ||
if(is_int($key)) { | ||
$this->options[$val] = array('label' => (string) $val); | ||
} elseif (!is_array($val)) { | ||
$this->options[$key] = array('label' => (string) $val); | ||
} else { | ||
if (!key_exists('label', $val)) throw new \InvalidArgumentException('If option is given as array, it has to have a "label"-key!'); | ||
$this->options[$key] = $val; | ||
} | ||
if ($options === null) { | ||
return $this->optGroups['']->options(); | ||
} | ||
$this->val(''); // set default value (empty or first) | ||
$this->optGroups[''] = new OptGroup(null, $options); | ||
return $this; | ||
} | ||
|
||
|
@@ -91,17 +121,55 @@ public function attr($name, $value = null) { | |
public function val($value = null) { | ||
if($value === null) return $this->value; | ||
|
||
if(isset($this->options[$value])) { | ||
$value_exists = $this->setValueInOptGroups($value); | ||
|
||
if($value_exists) { | ||
$this->value = $value; | ||
} else { | ||
// unknown value set, select first option instead | ||
$keys = array_keys($this->options); | ||
$this->value = (string) array_shift($keys); | ||
$this->value = $this->getFirstOption(); | ||
$this->setValueInOptGroups($this->value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how are we handling multi selects? it seems this only works with a single value? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are not, we are throwing an exception when setting the |
||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Returns the first options as it will be rendered in HTML | ||
* | ||
* @return string | ||
*/ | ||
protected function getFirstOption() { | ||
$options = $this->options(); | ||
if (!empty($options)) { | ||
return (string) array_shift(array_keys($options)); | ||
} | ||
foreach ($this->optGroups as $optGroup) { | ||
$options = $optGroup->options(); | ||
if (!empty($options)) { | ||
return (string) array_shift(array_keys($options)); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Set the value in the OptGroups, including the optgroup for the options without optgroup. | ||
* | ||
* @param string $value | ||
* @return bool | ||
*/ | ||
protected function setValueInOptGroups($value) { | ||
$value_exists = false; | ||
/** @var OptGroup $optGroup */ | ||
foreach ($this->optGroups as $optGroup) { | ||
$value_exists = $optGroup->storeValue($value) || $value_exists; | ||
if ($value_exists) { | ||
$value = null; | ||
} | ||
} | ||
return $value_exists; | ||
} | ||
|
||
/** | ||
* Create the HTML for the select it self | ||
* | ||
|
@@ -111,15 +179,7 @@ protected function mainElementHTML() { | |
if($this->useInput) $this->prefillInput(); | ||
|
||
$html = '<select ' . buildAttributes($this->attrs()) . '>'; | ||
foreach($this->options as $key => $val) { | ||
$selected = ($key == $this->value) ? ' selected="selected"' : ''; | ||
$attrs = ''; | ||
if (is_array($val['attrs'])) { | ||
array_walk($val['attrs'],function (&$aval, $akey){$aval = hsc($akey).'="'.hsc($aval).'"';}); | ||
$attrs = join(' ', $val['attrs']); | ||
} | ||
$html .= '<option' . $selected . ' value="' . hsc($key) . '" '.$attrs.'>' . hsc($val['label']) . '</option>'; | ||
} | ||
$html = array_reduce($this->optGroups, function($html, OptGroup $optGroup) {return $html . $optGroup->toHTML();}, $html); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's a bit weird that not all options are handled through the optgroups in |
||
$html .= '</select>'; | ||
|
||
return $html; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
<?php | ||
|
||
namespace dokuwiki\Form; | ||
|
||
|
||
class OptGroup extends Element { | ||
protected $options = array(); | ||
protected $value; | ||
|
||
/** | ||
* @param string $label The label text for this element (will be autoescaped) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wrong doc block |
||
* @param array $options The available options | ||
*/ | ||
public function __construct($label, $options) { | ||
parent::__construct('optGroup', array('label' => $label)); | ||
$this->options($options); | ||
} | ||
|
||
/** | ||
* Store the given value so it can be used during rendering | ||
* | ||
* This is intended to be only called from within @see DropdownElement::val() | ||
* | ||
* @param string $value | ||
* @return bool true if an option with the given value exists, false otherwise | ||
*/ | ||
public function storeValue($value) { | ||
$this->value = $value; | ||
return isset($this->options[$value]); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit unclear what this function does. It seems to store the currently selected value? What if this optgroup is part of a multiselect? What exactly is the return value? |
||
|
||
/** | ||
* Get or set the options of the optgroup | ||
* | ||
* Options can be given as associative array (value => label) or as an | ||
* indexd array (label = value) or as an array of arrays. In the latter | ||
* case an element has to look as follows: | ||
* option-value => array ( | ||
* 'label' => option-label, | ||
* 'attrs' => array ( | ||
* attr-key => attr-value, ... | ||
* ) | ||
* ) | ||
* | ||
* @param null|array $options | ||
* @return $this|array | ||
*/ | ||
public function options($options = null) { | ||
if($options === null) return $this->options; | ||
if(!is_array($options)) throw new \InvalidArgumentException('Options have to be an array'); | ||
$this->options = array(); | ||
foreach($options as $key => $val) { | ||
if(is_int($key)) { | ||
$this->options[$val] = array('label' => (string) $val); | ||
} elseif (!is_array($val)) { | ||
$this->options[$key] = array('label' => (string) $val); | ||
} else { | ||
if (!key_exists('label', $val)) throw new \InvalidArgumentException('If option is given as array, it has to have a "label"-key!'); | ||
$this->options[$key] = $val; | ||
} | ||
} | ||
return $this; | ||
} | ||
|
||
|
||
/** | ||
* The HTML representation of this element | ||
* | ||
* @return string | ||
*/ | ||
public function toHTML() { | ||
if ($this->attributes['label'] === null) { | ||
return $this->renderOptions(); | ||
} | ||
$html = '<optgroup '. buildAttributes($this->attrs()) . '>'; | ||
$html .= $this->renderOptions(); | ||
$html .= '</optgroup>'; | ||
return $html; | ||
} | ||
|
||
|
||
/** | ||
* @return string | ||
*/ | ||
protected function renderOptions() { | ||
$html = ''; | ||
foreach($this->options as $key => $val) { | ||
$selected = ($key == $this->value) ? ' selected="selected"' : ''; | ||
$attrs = ''; | ||
if (!empty($val['attrs']) && is_array($val['attrs'])) { | ||
$attrs = buildAttributes($val['attrs']); | ||
} | ||
$html .= '<option' . $selected . ' value="' . hsc($key) . '" '.$attrs.'>' . hsc($val['label']) . '</option>'; | ||
} | ||
return $html; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should it return
$this
here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2bd1d2c