‪TYPO3CMS  ‪main
NumberElement.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
24 
31 {
37  protected ‪$defaultFieldInformation = [
38  'tcaDescription' => [
39  'renderType' => 'tcaDescription',
40  ],
41  ];
42 
48  protected ‪$defaultFieldWizard = [
49  'localizationStateSelector' => [
50  'renderType' => 'localizationStateSelector',
51  ],
52  'otherLanguageContent' => [
53  'renderType' => 'otherLanguageContent',
54  'after' => [
55  'localizationStateSelector',
56  ],
57  ],
58  'defaultLanguageDifferences' => [
59  'renderType' => 'defaultLanguageDifferences',
60  'after' => [
61  'otherLanguageContent',
62  ],
63  ],
64  ];
65 
71  public function ‪render(): array
72  {
73  $table = $this->data['tableName'];
74  $fieldName = $this->data['fieldName'];
75  $parameterArray = $this->data['parameterArray'];
76  $resultArray = $this->‪initializeResultArray();
77  $config = $parameterArray['fieldConf']['config'];
78 
79  $format = $config['format'] ?? 'integer';
80  if ($format !== 'integer' && $format !== 'decimal') {
81  throw new \UnexpectedValueException(
82  'Format "' . $format . '" for field "' . $fieldName . '" in table "' . $table . '" is '
83  . 'not valid. Must be either empty or set to one of: "integer", "decimal".',
84  1649124682
85  );
86  }
87 
88  // @todo This should be configurable (e.g. [config][precision])
89  $precision = 2;
90 
91  $itemValue = $parameterArray['itemFormElValue'];
92  $width = $this->‪formMaxWidth(
93  ‪MathUtility::forceIntegerInRange($config['size'] ?? $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth)
94  );
95  $fieldId = ‪StringUtility::getUniqueId('formengine-input-');
96  $renderedLabel = $this->‪renderLabel($fieldId);
97 
98  $fieldInformationResult = $this->‪renderFieldInformation();
99  $fieldInformationHtml = $fieldInformationResult['html'];
100  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
101 
102  if ($config['readOnly'] ?? false) {
103  $html = [];
104  $html[] = $renderedLabel;
105  $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
106  $html[] = $fieldInformationHtml;
107  $html[] = '<div class="form-wizards-wrap">';
108  $html[] = '<div class="form-wizards-element">';
109  $html[] = '<div class="form-control-wrap" style="max-width: ' . $width . 'px">';
110  $html[] = '<input class="form-control" id="' . htmlspecialchars($fieldId) . '" value="' . htmlspecialchars((string)$itemValue) . '" type="text" disabled>';
111  $html[] = '</div>';
112  $html[] = '</div>';
113  $html[] = '</div>';
114  $html[] = '</div>';
115  $resultArray['html'] = implode(LF, $html);
116  return $resultArray;
117  }
118 
119  $languageService = $this->‪getLanguageService();
120  $itemName = (string)$parameterArray['itemFormElName'];
121 
122  // Always add the format.
123  $evalList = [$format];
124  if ($config['nullable'] ?? false) {
125  $evalList[] = 'null';
126  }
127 
128  $attributes = [
129  'value' => '',
130  'id' => $fieldId,
131  'class' => implode(' ', [
132  'form-control',
133  'form-control-clearable',
134  't3js-clearable',
135  'hasDefaultValue',
136  ]),
137  'data-formengine-validation-rules' => $this->‪getValidationDataAsJsonString($config),
138  'data-formengine-input-params' => (string)json_encode([
139  'field' => $itemName,
140  'evalList' => implode(',', $evalList),
141  ], JSON_THROW_ON_ERROR),
142  'data-formengine-input-name' => $itemName,
143  ];
144 
145  if (!empty($config['placeholder'])) {
146  $attributes['placeholder'] = trim($config['placeholder']);
147  }
148  if (isset($config['autocomplete'])) {
149  $attributes['autocomplete'] = empty($config['autocomplete']) ? 'new-' . $fieldName : 'on';
150  }
151 
152  $valuePickerHtml = [];
153  if (is_array($config['valuePicker']['items'] ?? false)) {
154  $valuePickerConfiguration = [
155  'mode' => $config['valuePicker']['mode'] ?? 'replace',
156  'linked-field' => '[data-formengine-input-name="' . $itemName . '"]',
157  ];
158  $valuePickerAttributes = array_merge(
159  [
160  'class' => 'form-select form-control-adapt',
161  ],
162  $this->getOnFieldChangeAttrs('change', $parameterArray['fieldChangeFunc'] ?? [])
163  );
164 
165  $valuePickerHtml[] = '<typo3-formengine-valuepicker ' . GeneralUtility::implodeAttributes($valuePickerConfiguration, true) . '>';
166  $valuePickerHtml[] = '<select ' . GeneralUtility::implodeAttributes($valuePickerAttributes, true) . '>';
167  $valuePickerHtml[] = '<option></option>';
168  foreach ($config['valuePicker']['items'] as $item) {
169  $valuePickerHtml[] = '<option value="' . htmlspecialchars((string)$item[1]) . '">' . htmlspecialchars($languageService->sL($item[0])) . '</option>';
170  }
171  $valuePickerHtml[] = '</select>';
172  $valuePickerHtml[] = '</typo3-formengine-valuepicker>';
173 
174  $resultArray['javaScriptModules'][] = ‪JavaScriptModuleInstruction::create('@typo3/backend/form-engine/field-wizard/value-picker.js');
175  }
176 
177  $valueSliderHtml = [];
178  if (is_array($config['slider'] ?? false)) {
179  if ($format === 'decimal') {
180  $itemValue = (float)$itemValue;
181  } else {
182  $itemValue = (int)$itemValue;
183  }
184  $valueSliderConfiguration = [
185  'precision' => (string)$precision,
186  'format' => $format,
187  'linked-field' => '[data-formengine-input-name="' . $itemName . '"]',
188  ];
189  $rangeAttributes = [
190  'type' => 'range',
191  'class' => 'slider',
192  'min' => (string)(int)($config['range']['lower'] ?? 0),
193  'max' => (string)(int)($config['range']['upper'] ?? 10000),
194  'step' => (string)($config['slider']['step'] ?? 1),
195  'style' => 'width: ' . (int)($config['slider']['width'] ?? 400) . 'px',
196  'title' => (string)$itemValue,
197  'value' => (string)$itemValue,
198  ];
199 
200  $valueSliderHtml[] = '<typo3-formengine-valueslider ' . GeneralUtility::implodeAttributes($valueSliderConfiguration, true) . '>';
201  $valueSliderHtml[] = '<div class="slider-wrapper">';
202  $valueSliderHtml[] = '<input ' . GeneralUtility::implodeAttributes($rangeAttributes, true) . '>';
203  $valueSliderHtml[] = '</div>';
204  $valueSliderHtml[] = '</typo3-formengine-valuepicker>';
205 
206  $resultArray['javaScriptModules'][] = ‪JavaScriptModuleInstruction::create('@typo3/backend/form-engine/field-wizard/value-slider.js');
207  }
208 
209  $fieldControlResult = $this->‪renderFieldControl();
210  $fieldControlHtml = $fieldControlResult['html'];
211  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
212 
213  $fieldWizardResult = $this->‪renderFieldWizard();
214  $fieldWizardHtml = $fieldWizardResult['html'];
215  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
216 
217  if (isset($config['range']['lower'])) {
218  $attributes['min'] = (string)(int)$config['range']['lower'];
219  }
220  if (isset($config['range']['upper'])) {
221  $attributes['max'] = (string)(int)$config['range']['upper'];
222  }
223 
224  if ($format === 'decimal') {
225  $attributes['step'] = '0.' . str_repeat('0', $precision - 1) . '1';
226  }
227 
228  $mainFieldHtml = [];
229  $mainFieldHtml[] = '<div class="form-control-wrap" style="max-width: ' . $width . 'px">';
230  $mainFieldHtml[] = '<div class="form-wizards-wrap">';
231  $mainFieldHtml[] = '<div class="form-wizards-element">';
232  $mainFieldHtml[] = '<input type="number" ' . GeneralUtility::implodeAttributes($attributes, true) . ' />';
233  $mainFieldHtml[] = '<input type="hidden" name="' . $itemName . '" value="' . htmlspecialchars((string)$itemValue) . '" />';
234  $mainFieldHtml[] = '</div>';
235  if (!empty($valuePickerHtml) || !empty($valueSliderHtml) || !empty($fieldControlHtml)) {
236  $mainFieldHtml[] = '<div class="form-wizards-items-aside form-wizards-items-aside--field-control">';
237  $mainFieldHtml[] = '<div class="btn-group">';
238  $mainFieldHtml[] = implode(LF, $valuePickerHtml);
239  $mainFieldHtml[] = implode(LF, $valueSliderHtml);
240  $mainFieldHtml[] = $fieldControlHtml;
241  $mainFieldHtml[] = '</div>';
242  $mainFieldHtml[] = '</div>';
243  }
244  if (!empty($fieldWizardHtml)) {
245  $mainFieldHtml[] = '<div class="form-wizards-items-bottom">';
246  $mainFieldHtml[] = $fieldWizardHtml;
247  $mainFieldHtml[] = '</div>';
248  }
249  $mainFieldHtml[] = '</div>';
250  $mainFieldHtml[] = '</div>';
251  $mainFieldHtml = implode(LF, $mainFieldHtml);
252 
253  $nullControlNameEscaped = htmlspecialchars('control[active][' . $table . '][' . $this->data['databaseRow']['uid'] . '][' . $fieldName . ']');
254 
255  $fullElement = $mainFieldHtml;
256  if ($this->‪hasNullCheckboxButNoPlaceholder()) {
257  $checked = $itemValue !== null ? ' checked="checked"' : '';
258  $fullElement = [];
259  $fullElement[] = '<div class="t3-form-field-disable"></div>';
260  $fullElement[] = '<div class="form-check t3-form-field-eval-null-checkbox">';
261  $fullElement[] = '<input type="hidden" name="' . $nullControlNameEscaped . '" value="0" />';
262  $fullElement[] = '<input type="checkbox" class="form-check-input" name="' . $nullControlNameEscaped . '" id="' . $nullControlNameEscaped . '" value="1"' . $checked . ' />';
263  $fullElement[] = '<label class="form-check-label" for="' . $nullControlNameEscaped . '">';
264  $fullElement[] = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.nullCheckbox');
265  $fullElement[] = '</label>';
266  $fullElement[] = '</div>';
267  $fullElement[] = $mainFieldHtml;
268  $fullElement = implode(LF, $fullElement);
269  } elseif ($this->‪hasNullCheckboxWithPlaceholder()) {
270  $checked = $itemValue !== null ? ' checked="checked"' : '';
271  $placeholder = $shortenedPlaceholder = trim((string)($config['placeholder'] ?? ''));
272  if ($placeholder !== '') {
273  $shortenedPlaceholder = ‪GeneralUtility::fixed_lgd_cs($placeholder, 20);
274  if ($placeholder !== $shortenedPlaceholder) {
275  $overrideLabel = sprintf(
276  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override'),
277  '<span title="' . htmlspecialchars($placeholder) . '">' . htmlspecialchars($shortenedPlaceholder) . '</span>'
278  );
279  } else {
280  $overrideLabel = sprintf(
281  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override'),
282  htmlspecialchars($placeholder)
283  );
284  }
285  } else {
286  $overrideLabel = $languageService->sL(
287  'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override_not_available'
288  );
289  }
290  $fullElement = [];
291  $fullElement[] = '<div class="form-check t3js-form-field-eval-null-placeholder-checkbox">';
292  $fullElement[] = '<input type="hidden" name="' . $nullControlNameEscaped . '" value="0" />';
293  $fullElement[] = '<input type="checkbox" class="form-check-input" name="' . $nullControlNameEscaped . '" id="' . $nullControlNameEscaped . '" value="1"' . $checked . ' />';
294  $fullElement[] = '<label class="form-check-label" for="' . $nullControlNameEscaped . '">';
295  $fullElement[] = $overrideLabel;
296  $fullElement[] = '</label>';
297  $fullElement[] = '</div>';
298  $fullElement[] = '<div class="t3js-formengine-placeholder-placeholder">';
299  $fullElement[] = '<div class="form-control-wrap" style="max-width:' . $width . 'px">';
300  $fullElement[] = '<input type="text" class="form-control" disabled="disabled" value="' . htmlspecialchars($shortenedPlaceholder) . '" />';
301  $fullElement[] = '</div>';
302  $fullElement[] = '</div>';
303  $fullElement[] = '<div class="t3js-formengine-placeholder-formfield">';
304  $fullElement[] = $mainFieldHtml;
305  $fullElement[] = '</div>';
306  $fullElement = implode(LF, $fullElement);
307  }
308 
309  $resultArray['html'] = $renderedLabel . '
310  <div class="formengine-field-item t3js-formengine-field-item">
311  ' . $fieldInformationHtml . $fullElement . '
312  </div>';
313 
314  return $resultArray;
315  }
316 }
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldInformation
‪array renderFieldInformation()
Definition: AbstractFormElement.php:73
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixed_lgd_cs
‪static string fixed_lgd_cs(string $string, int $chars, string $appendString='...')
Definition: GeneralUtility.php:92
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:104
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\create
‪static create(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:47
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement
Definition: AbstractFormElement.php:37
‪TYPO3\CMS\Backend\Form\Element\NumberElement\render
‪array render()
Definition: NumberElement.php:69
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Backend\Form\Element
Definition: AbstractFormElement.php:16
‪TYPO3\CMS\Backend\Form\Element\NumberElement\$defaultFieldInformation
‪array $defaultFieldInformation
Definition: NumberElement.php:36
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldControl
‪array renderFieldControl()
Definition: AbstractFormElement.php:89
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\getLanguageService
‪getLanguageService()
Definition: AbstractFormElement.php:456
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\formMaxWidth
‪int formMaxWidth($size=48)
Definition: AbstractFormElement.php:332
‪TYPO3\CMS\Backend\Form\Element\NumberElement
Definition: NumberElement.php:31
‪TYPO3\CMS\Backend\Form\AbstractNode\getValidationDataAsJsonString
‪getValidationDataAsJsonString(array $config)
Definition: AbstractNode.php:133
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderLabel
‪renderLabel(string $for)
Definition: AbstractFormElement.php:119
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Backend\Form\Element\NumberElement\$defaultFieldWizard
‪array $defaultFieldWizard
Definition: NumberElement.php:46
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldWizard
‪array renderFieldWizard()
Definition: AbstractFormElement.php:105
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\hasNullCheckboxWithPlaceholder
‪hasNullCheckboxWithPlaceholder()
Definition: AbstractFormElement.php:195
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\hasNullCheckboxButNoPlaceholder
‪hasNullCheckboxButNoPlaceholder()
Definition: AbstractFormElement.php:163
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪initializeResultArray()
Definition: AbstractNode.php:77