Commit a5e131c0 authored by Andreas Fernandez's avatar Andreas Fernandez
Browse files

[TASK] Implement ValuePicker as custom web component

TCA's value picker uses a lot inline JavaScript for its
picker handling, therefore, the handling has been moved
into a dedicated web component.

As a side-effect a bug in TextElement and InputTextElement
is fixed. Both elements previously used exactly the opposite
insert mode.

Resolves: #94112
Releases: master
Change-Id: Ib3a1f9697477cab4b9a00c606fcd0bd6524450b5
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/69108

Tested-by: Benjamin Franzke's avatarBenjamin Franzke <bfr@qbus.de>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Benjamin Franzke's avatarBenjamin Franzke <bfr@qbus.de>
Reviewed-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
parent b5eab03e
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
enum InsertModes {
append = 'append',
replace = 'replace',
prepend = 'prepend'
}
/**
* Module TYPO3/CMS/Backend/FormEngine/FieldWizard/ValuePicker
*
* @example
* <typo3-formengine-valuepicker mode="prepend" linked-field="css-selector">
* <select>
* </typo3-formengine-valuepicker>
*
* This is based on W3C custom elements ("web components") specification, see
* https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
*/
export class ValuePicker extends HTMLElement {
private valuePicker: HTMLSelectElement;
public connectedCallback(): void {
this.valuePicker = this.querySelector('select') as HTMLSelectElement;
if (this.valuePicker !== null) {
this.valuePicker.addEventListener('change', this.onChange);
}
}
public disconnectedCallback(): void {
if (this.valuePicker !== null) {
this.valuePicker.removeEventListener('change', this.onChange);
this.valuePicker = null;
}
}
private onChange = () => {
this.setValue();
this.valuePicker.selectedIndex = 0;
this.valuePicker.blur();
}
private setValue (): void {
const selectedValue = this.valuePicker.options[this.valuePicker.selectedIndex].value;
const linkedField = document.querySelector(this.getAttribute('linked-field')) as HTMLInputElement|HTMLTextAreaElement;
const mode = this.getAttribute('mode') as InsertModes ?? InsertModes.replace;
if (mode === InsertModes.append) {
linkedField.value += selectedValue;
} else if (mode === InsertModes.prepend) {
linkedField.value = selectedValue + linkedField.value;
} else {
linkedField.value = selectedValue;
}
linkedField.dispatchEvent(new Event('change', {bubbles: true, cancelable: true}));
}
}
window.customElements.define('typo3-formengine-valuepicker', ValuePicker);
......@@ -160,7 +160,7 @@ class InputColorPickerElement extends AbstractFormElement
$valuePickerHtml = [];
if (isset($config['valuePicker']['items']) && is_array($config['valuePicker']['items'])) {
$valuePickerHtml[] = '<select class="t3js-colorpicker-value-trigger form-select">';
$valuePickerHtml[] = '<select class="t3js-colorpicker-value-trigger form-select form-control-adapt">';
$valuePickerHtml[] = '<option></option>';
foreach ($config['valuePicker']['items'] as $item) {
$valuePickerHtml[] = '<option value="' . htmlspecialchars($item[1]) . '">' . htmlspecialchars($languageService->sL($item[0])) . '</option>';
......
......@@ -181,28 +181,25 @@ class InputLinkElement extends AbstractFormElement
$valuePickerHtml = [];
if (isset($config['valuePicker']['items']) && is_array($config['valuePicker']['items'])) {
$mode = $config['valuePicker']['mode'] ?? '';
$itemName = $parameterArray['itemFormElName'];
$fieldChangeFunc = $parameterArray['fieldChangeFunc'];
if ($mode === 'append') {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value=\'\'+this.options[this.selectedIndex].value+document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value';
} elseif ($mode === 'prepend') {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value+=\'\'+this.options[this.selectedIndex].value';
} else {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value=this.options[this.selectedIndex].value';
}
$valuePickerHtml[] = '<select';
$valuePickerHtml[] = ' class="form-select"';
$valuePickerHtml[] = ' onchange="' . htmlspecialchars($assignValue . ';this.blur();this.selectedIndex=0;' . implode('', $fieldChangeFunc)) . '"';
$valuePickerHtml[] = '>';
$valuePickerConfiguration = [
'mode' => $config['valuePicker']['mode'] ?? 'replace',
'linked-field' => '[data-formengine-input-name="' . $parameterArray['itemFormElName'] . '"]'
];
$valuePickerAttributes = [
'class' => 'form-select form-control-adapt',
'onchange' => implode('', $parameterArray['fieldChangeFunc']),
];
$valuePickerHtml[] = '<typo3-formengine-valuepicker ' . GeneralUtility::implodeAttributes($valuePickerConfiguration, true) . '>';
$valuePickerHtml[] = '<select ' . GeneralUtility::implodeAttributes($valuePickerAttributes, true) . '>';
$valuePickerHtml[] = '<option></option>';
foreach ($config['valuePicker']['items'] as $item) {
$valuePickerHtml[] = '<option value="' . htmlspecialchars($item[1]) . '">' . htmlspecialchars($languageService->sL($item[0])) . '</option>';
}
$valuePickerHtml[] = '</select>';
$valuePickerHtml[] = '</typo3-formengine-valuepicker>';
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/FieldWizard/ValuePicker' => null];
}
$fieldWizardResult = $this->renderFieldWizard();
......
......@@ -171,28 +171,25 @@ class InputTextElement extends AbstractFormElement
$valuePickerHtml = [];
if (isset($config['valuePicker']['items']) && is_array($config['valuePicker']['items'])) {
$mode = $config['valuePicker']['mode'] ?? '';
$itemName = $parameterArray['itemFormElName'];
$fieldChangeFunc = $parameterArray['fieldChangeFunc'];
if ($mode === 'append') {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value+=\'\'+this.options[this.selectedIndex].value';
} elseif ($mode === 'prepend') {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value=\'\'+this.options[this.selectedIndex].value+document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value';
} else {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value=this.options[this.selectedIndex].value';
}
$valuePickerHtml[] = '<select';
$valuePickerHtml[] = ' class="form-select form-control-adapt"';
$valuePickerHtml[] = ' onchange="' . htmlspecialchars($assignValue . ';this.blur();this.selectedIndex=0;' . implode('', $fieldChangeFunc)) . '"';
$valuePickerHtml[] = '>';
$valuePickerConfiguration = [
'mode' => $config['valuePicker']['mode'] ?? 'replace',
'linked-field' => '[data-formengine-input-name="' . $parameterArray['itemFormElName'] . '"]'
];
$valuePickerAttributes = [
'class' => 'form-select form-control-adapt',
'onchange' => implode('', $parameterArray['fieldChangeFunc']),
];
$valuePickerHtml[] = '<typo3-formengine-valuepicker ' . GeneralUtility::implodeAttributes($valuePickerConfiguration, true) . '>';
$valuePickerHtml[] = '<select ' . GeneralUtility::implodeAttributes($valuePickerAttributes, true) . '>';
$valuePickerHtml[] = '<option></option>';
foreach ($config['valuePicker']['items'] as $item) {
$valuePickerHtml[] = '<option value="' . htmlspecialchars($item[1]) . '">' . htmlspecialchars($languageService->sL($item[0])) . '</option>';
}
$valuePickerHtml[] = '</select>';
$valuePickerHtml[] = '</typo3-formengine-valuepicker>';
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/FieldWizard/ValuePicker' => null];
}
$valueSliderHtml = [];
......
......@@ -186,28 +186,25 @@ class TextElement extends AbstractFormElement
$valuePickerHtml = [];
if (isset($config['valuePicker']['items']) && is_array($config['valuePicker']['items'])) {
$mode = $config['valuePicker']['mode'] ?? '';
$itemName = $parameterArray['itemFormElName'];
$fieldChangeFunc = $parameterArray['fieldChangeFunc'];
if ($mode === 'append') {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value=\'\'+this.options[this.selectedIndex].value+document.editform[' . GeneralUtility::quoteJSvalue($itemName) . '].value';
} elseif ($mode === 'prepend') {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value+=\'\'+this.options[this.selectedIndex].value';
} else {
$assignValue = 'document.querySelectorAll(' . GeneralUtility::quoteJSvalue('[data-formengine-input-name="' . $itemName . '"]') . ')[0]'
. '.value=this.options[this.selectedIndex].value';
}
$valuePickerHtml[] = '<select';
$valuePickerHtml[] = ' class="form-select form-control-adapt"';
$valuePickerHtml[] = ' onchange="' . htmlspecialchars($assignValue . ';this.blur();this.selectedIndex=0;' . implode('', $fieldChangeFunc)) . '"';
$valuePickerHtml[] = '>';
$valuePickerConfiguration = [
'mode' => $config['valuePicker']['mode'] ?? 'replace',
'linked-field' => '[data-formengine-input-name="' . $parameterArray['itemFormElName'] . '"]'
];
$valuePickerAttributes = [
'class' => 'form-select form-control-adapt',
'onchange' => implode('', $parameterArray['fieldChangeFunc']),
];
$valuePickerHtml[] = '<typo3-formengine-valuepicker ' . GeneralUtility::implodeAttributes($valuePickerConfiguration, true) . '>';
$valuePickerHtml[] = '<select ' . GeneralUtility::implodeAttributes($valuePickerAttributes, true) . '>';
$valuePickerHtml[] = '<option></option>';
foreach ($config['valuePicker']['items'] as $item) {
$valuePickerHtml[] = '<option value="' . htmlspecialchars($item[1]) . '">' . htmlspecialchars($languageService->sL($item[0])) . '</option>';
}
$valuePickerHtml[] = '</select>';
$valuePickerHtml[] = '</typo3-formengine-valuepicker>';
$resultArray['requireJsModules'][] = ['TYPO3/CMS/Backend/FormEngine/FieldWizard/ValuePicker' => null];
}
$fieldControlResult = $this->renderFieldControl();
......
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
define(["require","exports"],(function(e,t){"use strict";var l;Object.defineProperty(t,"__esModule",{value:!0}),t.ValuePicker=void 0,function(e){e.append="append",e.replace="replace",e.prepend="prepend"}(l||(l={}));class n extends HTMLElement{constructor(){super(...arguments),this.onChange=()=>{this.setValue(),this.valuePicker.selectedIndex=0,this.valuePicker.blur()}}connectedCallback(){this.valuePicker=this.querySelector("select"),null!==this.valuePicker&&this.valuePicker.addEventListener("change",this.onChange)}disconnectedCallback(){null!==this.valuePicker&&(this.valuePicker.removeEventListener("change",this.onChange),this.valuePicker=null)}setValue(){var e;const t=this.valuePicker.options[this.valuePicker.selectedIndex].value,n=document.querySelector(this.getAttribute("linked-field")),i=null!==(e=this.getAttribute("mode"))&&void 0!==e?e:l.replace;i===l.append?n.value+=t:i===l.prepend?n.value=t+n.value:n.value=t,n.dispatchEvent(new Event("change",{bubbles:!0,cancelable:!0}))}}t.ValuePicker=n,window.customElements.define("typo3-formengine-valuepicker",n)}));
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment