Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / htmlform / fields / HTMLSelectOrOtherField.php
bloba95e8acba1db0d2777f131474a004da71100986d
1 <?php
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;
10 /**
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.
16 * @stable to extend
18 class HTMLSelectOrOtherField extends HTMLTextField {
19 private const FIELD_CLASS = 'mw-htmlform-select-or-other';
21 /**
22 * @stable to call
23 * @inheritDoc
25 public function __construct( $params ) {
26 parent::__construct( $params );
27 $this->getOptions();
28 if ( !in_array( 'other', $this->mOptions, true ) ) {
29 $msg =
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 ) {
37 $valInSelect = false;
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 );
75 $wrapperAttribs = [
76 'id' => $this->mID,
77 'class' => $this->getFieldClasses()
79 if ( $this->mClass !== '' ) {
80 $wrapperAttribs['class'][] = $this->mClass;
82 return Html::rawElement(
83 'div',
84 $wrapperAttribs,
85 "$select<br />\n$textbox"
89 protected function shouldInfuseOOUI() {
90 return true;
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
108 # DropdownInput
109 $dropdownAttribs = [
110 'name' => $this->mName,
111 'options' => $this->getOptionsOOUI(),
112 'value' => $valInSelect ? $value : 'other',
115 $allowedParams = [
116 'disabled',
117 'tabindex',
120 $dropdownAttribs += \OOUI\Element::configFromHtmlAttributes(
121 $this->getAttributes( $allowedParams )
124 # TextInput
125 $textAttribs = [
126 'name' => $this->mName . '-other',
127 'size' => $this->getSize(),
128 'value' => $valInSelect ? '' : $value,
131 $allowedParams = [
132 'required',
133 'autofocus',
134 'multiple',
135 'disabled',
136 'tabindex',
137 'maxlength',
138 'minlength',
141 $textAttribs += \OOUI\Element::configFromHtmlAttributes(
142 $this->getAttributes( $allowedParams )
145 if ( $this->mPlaceholder !== '' ) {
146 $textAttribs['placeholder'] = $this->mPlaceholder;
149 $disabled = false;
150 if ( isset( $this->mParams[ 'disabled' ] ) && $this->mParams[ 'disabled' ] ) {
151 $disabled = true;
154 $inputClasses = $this->getFieldClasses();
155 if ( $this->mClass !== '' ) {
156 $inputClasses = array_merge( $inputClasses, explode( ' ', $this->mClass ) );
158 return $this->getInputWidget( [
159 'id' => $this->mID,
160 'classes' => $inputClasses,
161 'disabled' => $disabled,
162 'textinput' => $textAttribs,
163 'dropdowninput' => $dropdownAttribs,
164 'required' => $this->mParams[ 'required' ] ?? false,
165 'or' => true,
166 ] );
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,
216 $hasErrors,
217 'text',
218 $this->mName . '-other',
219 $textInputAttribs
222 // Set up the wrapper element and return the entire component.
223 $wrapperAttribs = [
224 'id' => $this->mID,
225 'class' => $this->getFieldClasses()
227 if ( $this->mClass !== '' ) {
228 $wrapperAttribs['class'][] = $this->mClass;
230 return Html::rawElement(
231 'div',
232 $wrapperAttribs,
233 "$select\n$textInput"
238 * @param WebRequest $request
240 * @return string
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' );
250 return $val;
251 } else {
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
262 * @return string[]
264 protected function getFieldClasses(): array {
265 return [ self::FIELD_CLASS ];
269 /** @deprecated class alias since 1.42 */
270 class_alias( HTMLSelectOrOtherField::class, 'HTMLSelectOrOtherField' );