3 namespace MediaWiki\HTMLForm\Field
;
5 use MediaWiki\Html\Html
;
6 use MediaWiki\HTMLForm\HTMLFormField
;
7 use MediaWiki\Request\WebRequest
;
8 use MediaWiki\Xml\XmlSelect
;
11 * Select dropdown field, with an additional "other" textbox.
13 * HTMLComboboxField implements the same functionality using a single form field
14 * and should be used instead.
18 class HTMLSelectOrOtherField
extends HTMLTextField
{
19 private const FIELD_CLASS
= 'mw-htmlform-select-or-other';
25 public function __construct( $params ) {
26 parent
::__construct( $params );
28 if ( !in_array( 'other', $this->mOptions
, true ) ) {
30 $params['other'] ??
wfMessage( 'htmlform-selectorother-other' )->text();
31 // Have 'other' always as first element
32 $this->mOptions
= [ $msg => 'other' ] +
$this->mOptions
;
36 public function getInputHTML( $value ) {
39 if ( $value !== false ) {
40 $value = strval( $value );
41 $valInSelect = in_array(
42 $value, HTMLFormField
::flattenOptions( $this->getOptions() ), true
46 $selected = $valInSelect ?
$value : 'other';
48 $select = new XmlSelect( $this->mName
, false, $selected );
49 $select->addOptions( $this->getOptions() );
51 $tbAttribs = [ 'size' => $this->getSize() ];
53 if ( !empty( $this->mParams
['disabled'] ) ) {
54 $select->setAttribute( 'disabled', 'disabled' );
55 $tbAttribs['disabled'] = 'disabled';
58 if ( isset( $this->mParams
['tabindex'] ) ) {
59 $select->setAttribute( 'tabindex', $this->mParams
['tabindex'] );
60 $tbAttribs['tabindex'] = $this->mParams
['tabindex'];
63 $select = $select->getHTML();
65 if ( isset( $this->mParams
['maxlength'] ) ) {
66 $tbAttribs['maxlength'] = $this->mParams
['maxlength'];
69 if ( isset( $this->mParams
['minlength'] ) ) {
70 $tbAttribs['minlength'] = $this->mParams
['minlength'];
73 $textbox = Html
::input( $this->mName
. '-other', $valInSelect ?
'' : $value, 'text', $tbAttribs );
77 'class' => $this->getFieldClasses()
79 if ( $this->mClass
!== '' ) {
80 $wrapperAttribs['class'][] = $this->mClass
;
82 return Html
::rawElement(
85 "$select<br />\n$textbox"
89 protected function shouldInfuseOOUI() {
93 protected function getOOUIModules() {
94 return [ 'mediawiki.widgets.SelectWithInputWidget' ];
97 public function getInputOOUI( $value ) {
98 $this->mParent
->getOutput()->addModuleStyles( 'mediawiki.widgets.SelectWithInputWidget.styles' );
100 $valInSelect = false;
101 if ( $value !== false ) {
102 $value = strval( $value );
103 $valInSelect = in_array(
104 $value, HTMLFormField
::flattenOptions( $this->getOptions() ), true
110 'name' => $this->mName
,
111 'options' => $this->getOptionsOOUI(),
112 'value' => $valInSelect ?
$value : 'other',
120 $dropdownAttribs +
= \OOUI\Element
::configFromHtmlAttributes(
121 $this->getAttributes( $allowedParams )
126 'name' => $this->mName
. '-other',
127 'size' => $this->getSize(),
128 'value' => $valInSelect ?
'' : $value,
141 $textAttribs +
= \OOUI\Element
::configFromHtmlAttributes(
142 $this->getAttributes( $allowedParams )
145 if ( $this->mPlaceholder
!== '' ) {
146 $textAttribs['placeholder'] = $this->mPlaceholder
;
150 if ( isset( $this->mParams
[ 'disabled' ] ) && $this->mParams
[ 'disabled' ] ) {
154 $inputClasses = $this->getFieldClasses();
155 if ( $this->mClass
!== '' ) {
156 $inputClasses = array_merge( $inputClasses, explode( ' ', $this->mClass
) );
158 return $this->getInputWidget( [
160 'classes' => $inputClasses,
161 'disabled' => $disabled,
162 'textinput' => $textAttribs,
163 'dropdowninput' => $dropdownAttribs,
164 'required' => $this->mParams
[ 'required' ] ??
false,
169 public function getInputWidget( $params ) {
170 return new \MediaWiki\Widget\
SelectWithInputWidget( $params );
173 public function getInputCodex( $value, $hasErrors ) {
174 // Figure out the value of the select.
175 $valInSelect = false;
176 if ( $value !== false ) {
177 $value = strval( $value );
178 $valInSelect = in_array(
179 $value, HTMLFormField
::flattenOptions( $this->getOptions() ), true
182 $selected = $valInSelect ?
$value : 'other';
184 // Create the <select> element.
185 $select = new XmlSelect( $this->mName
, false, $selected );
186 // TODO: Add support for error class once it's implemented in the Codex CSS-only Select.
187 $select->setAttribute( 'class', 'cdx-select' );
188 $select->addOptions( $this->getOptions() );
190 // Set up attributes for the select and the text input.
191 $textInputAttribs = [ 'size' => $this->getSize() ];
192 $textInputAttribs['name'] = $this->mName
. '-other';
194 if ( !empty( $this->mParams
['disabled'] ) ) {
195 $select->setAttribute( 'disabled', 'disabled' );
196 $textInputAttribs['disabled'] = 'disabled';
199 if ( isset( $this->mParams
['tabindex'] ) ) {
200 $select->setAttribute( 'tabindex', $this->mParams
['tabindex'] );
201 $textInputAttribs['tabindex'] = $this->mParams
['tabindex'];
204 if ( isset( $this->mParams
['maxlength'] ) ) {
205 $textInputAttribs['maxlength'] = $this->mParams
['maxlength'];
208 if ( isset( $this->mParams
['minlength'] ) ) {
209 $textInputAttribs['minlength'] = $this->mParams
['minlength'];
212 // Get HTML of the select and text input.
213 $select = $select->getHTML();
214 $textInput = parent
::buildCodexComponent(
215 $valInSelect ?
'' : $value,
218 $this->mName
. '-other',
222 // Set up the wrapper element and return the entire component.
225 'class' => $this->getFieldClasses()
227 if ( $this->mClass
!== '' ) {
228 $wrapperAttribs['class'][] = $this->mClass
;
230 return Html
::rawElement(
233 "$select\n$textInput"
238 * @param WebRequest $request
242 public function loadDataFromRequest( $request ) {
243 if ( $request->getCheck( $this->mName
) ) {
244 $val = $request->getText( $this->mName
);
246 if ( $val === 'other' ) {
247 $val = $request->getText( $this->mName
. '-other' );
252 return $this->getDefault();
257 * Returns a list of classes that should be applied to the widget itself. Unfortunately, we can't use
258 * $this->mClass or the 'cssclass' config option, because they're also added to the outer field wrapper
259 * (which includes the label). This method exists a temporary workaround until HTMLFormField will have
260 * a stable way for subclasses to specify additional classes for the widget itself.
261 * @internal Should only be used in HTMLTimezoneField
264 protected function getFieldClasses(): array {
265 return [ self
::FIELD_CLASS
];
269 /** @deprecated class alias since 1.42 */
270 class_alias( HTMLSelectOrOtherField
::class, 'HTMLSelectOrOtherField' );