--- /dev/null
+<dl id="{@$element->getField()->getPrefixedId()}Container"{*
+ *}{if !$element->getField()->getClasses()|empty} class="{implode from=$element->getField()->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{*
+ *}{foreach from=$element->getField()->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{*
+ *}{if !$element->getField()->checkDependencies()} style="display: none;"{/if}{*
+*}>
+ <dt>{if $element->getLabel() !== null}<label for="{@$element->getField()->getPrefixedId()}">{@$element->getLabel()}</label>{/if}</dt>
+ <dd>
+ <div class="inputAddon">
+ {@$element->getField()->getFieldHtml()}
+
+ {if $element->getSuffixField() !== null && $element->getSuffixField()->isAvailable()}
+ {if !$element->suffixHasSelectableOptions()}
+ {if $element->getSuffixLabel() !== ''}
+ <span class="inputSuffix">{@$element->getSuffixLabel()}</span>
+ {/if}
+ {else}
+ <span class="inputSuffix dropdown" id="{@$element->getSuffixField()->getPrefixedId()}_dropdown">
+ <span class="dropdownToggle">{@$element->getSelectedSuffixOption()[label]} <span class="icon icon16 fa-caret-down pointer"></span></span>
+
+ <ul class="dropdownMenu">
+ {foreach from=$element->getSuffixField()->getNestedOptions() item=__fieldNestedOption}
+ <li{if ($element->getSuffixField()->getValue() == $__fieldNestedOption[value] && $__fieldNestedOption[isSelectable]) || !$__fieldNestedOption[isSelectable]} class="{if $element->getSuffixField()->getValue() == $__fieldNestedOption[value] && $__fieldNestedOption[isSelectable]}active{if !$__fieldNestedOption[isSelectable]} disabled{/if}{else}disabled{/if}"{/if} data-value="{$__fieldNestedOption[value]}" data-label="{$__fieldNestedOption[label]}"><span>{@' '|str_repeat:$__fieldNestedOption[depth] * 4}{@$__fieldNestedOption[label]}</span></li>
+ {/foreach}
+ </ul>
+ <input type="hidden" id="{@$element->getSuffixField()->getPrefixedId()}" name="{@$element->getSuffixField()->getPrefixedId()}" value="{if $element->getSuffixField()->getValue() === null}{$element->getSelectedSuffixOption()[value]}{else}{$element->getSuffixField()->getValue()}{/if}" />
+ </span>
+
+ {include file='__formFieldDependencies' field=$element->getSuffixField()}
+ {include file='__formFieldDataHandler' field=$element->getSuffixField()}
+ {/if}
+ {/if}
+ </div>
+
+ {if $element->getDescription() !== null}
+ <small>{@$element->getDescription()}</small>
+ {/if}
+
+ {include file='__formFieldErrors' field=$element->getField()}
+
+ {if $element->getSuffixField() !== null && $element->getSuffixField()->isAvailable()}
+ {foreach from=$element->getSuffixField()->getValidationErrors() item='validationError'}
+ {@$validationError->getHtml()}
+ {/foreach}
+ {/if}
+
+ {include file='__formFieldDependencies' field=$element->getField()}
+ {include file='__formFieldDataHandler' field=$element->getField()}
+ </dd>
+</dl>
+
+{if $element->getSuffixField() !== null && $element->getSuffixField()->isAvailable() && !$element->getSuffixField()->isImmutable() && $element->suffixHasSelectableOptions()}
+<script data-relocate="true">
+ require(['WoltLabSuite/Core/Form/Builder/Container/SuffixFormField'], function(FormBuilderSuffixFormFieldContainer) {
+ new FormBuilderSuffixFormFieldContainer('{@$element->getSuffixField()->getPrefixedId()}');
+ });
+</script>
+{/if}
"__rowFormFieldContainer",
"__singleMediaSelectionFormField",
"__singleSelectionFormField",
+ "__suffixFormFieldContainer",
"__tabFormContainer",
"__tabMenuFormContainer",
"__tabTabMenuFormContainer",
--- /dev/null
+<dl id="{@$element->getField()->getPrefixedId()}Container"{*
+ *}{if !$element->getField()->getClasses()|empty} class="{implode from=$element->getField()->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{*
+ *}{foreach from=$element->getField()->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{*
+ *}{if !$element->getField()->checkDependencies()} style="display: none;"{/if}{*
+*}>
+ <dt>{if $element->getLabel() !== null}<label for="{@$element->getField()->getPrefixedId()}">{@$element->getLabel()}</label>{/if}</dt>
+ <dd>
+ <div class="inputAddon">
+ {@$element->getField()->getFieldHtml()}
+
+ {if $element->getSuffixField() !== null && $element->getSuffixField()->isAvailable()}
+ {if !$element->suffixHasSelectableOptions()}
+ {if $element->getSuffixLabel() !== ''}
+ <span class="inputSuffix">{@$element->getSuffixLabel()}</span>
+ {/if}
+ {else}
+ <span class="inputSuffix dropdown" id="{@$element->getSuffixField()->getPrefixedId()}_dropdown">
+ <span class="dropdownToggle">{@$element->getSelectedSuffixOption()[label]} <span class="icon icon16 fa-caret-down pointer"></span></span>
+
+ <ul class="dropdownMenu">
+ {foreach from=$element->getSuffixField()->getNestedOptions() item=__fieldNestedOption}
+ <li{if ($element->getSuffixField()->getValue() == $__fieldNestedOption[value] && $__fieldNestedOption[isSelectable]) || !$__fieldNestedOption[isSelectable]} class="{if $element->getSuffixField()->getValue() == $__fieldNestedOption[value] && $__fieldNestedOption[isSelectable]}active{if !$__fieldNestedOption[isSelectable]} disabled{/if}{else}disabled{/if}"{/if} data-value="{$__fieldNestedOption[value]}" data-label="{$__fieldNestedOption[label]}"><span>{@' '|str_repeat:$__fieldNestedOption[depth] * 4}{@$__fieldNestedOption[label]}</span></li>
+ {/foreach}
+ </ul>
+ <input type="hidden" id="{@$element->getSuffixField()->getPrefixedId()}" name="{@$element->getSuffixField()->getPrefixedId()}" value="{if $element->getSuffixField()->getValue() === null}{$element->getSelectedSuffixOption()[value]}{else}{$element->getSuffixField()->getValue()}{/if}" />
+ </span>
+
+ {include file='__formFieldDependencies' field=$element->getSuffixField()}
+ {include file='__formFieldDataHandler' field=$element->getSuffixField()}
+ {/if}
+ {/if}
+ </div>
+
+ {if $element->getDescription() !== null}
+ <small>{@$element->getDescription()}</small>
+ {/if}
+
+ {include file='__formFieldErrors' field=$element->getField()}
+
+ {if $element->getSuffixField() !== null && $element->getSuffixField()->isAvailable()}
+ {foreach from=$element->getSuffixField()->getValidationErrors() item='validationError'}
+ {@$validationError->getHtml()}
+ {/foreach}
+ {/if}
+
+ {include file='__formFieldDependencies' field=$element->getField()}
+ {include file='__formFieldDataHandler' field=$element->getField()}
+ </dd>
+</dl>
+
+{if $element->getSuffixField() !== null && $element->getSuffixField()->isAvailable() && !$element->getSuffixField()->isImmutable() && $element->suffixHasSelectableOptions()}
+<script data-relocate="true">
+ require(['WoltLabSuite/Core/Form/Builder/Container/SuffixFormField'], function(FormBuilderSuffixFormFieldContainer) {
+ new FormBuilderSuffixFormFieldContainer('{@$element->getDocument()->getId()}', '{@$element->getSuffixField()->getPrefixedId()}');
+ });
+</script>
+{/if}
--- /dev/null
+/**
+ * Handles the dropdowns of form fields with a suffix.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Form/Builder/Container/SuffixFormField
+ * @since 5.2
+ */
+define(['EventHandler', 'Ui/SimpleDropdown'], function(EventHandler, UiSimpleDropdown) {
+ "use strict";
+
+ /**
+ * @constructor
+ */
+ function PrefixSuffixFormFieldContainer(formId, suffixFieldId) {
+ this._formId = formId;
+
+ this._suffixField = elById(suffixFieldId);
+ this._suffixDropdownMenu = UiSimpleDropdown.getDropdownMenu(suffixFieldId + '_dropdown');
+ this._suffixDropdownToggle = elByClass('dropdownToggle', UiSimpleDropdown.getDropdown(suffixFieldId + '_dropdown'))[0];
+
+ var listItems = this._suffixDropdownMenu.children;
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ listItems[i].addEventListener('click', this._changeSuffixSelection.bind(this));
+ }
+
+ EventHandler.add('WoltLabSuite/Core/Form/Builder/Manager', 'afterUnregisterForm', this._destroyDropdown.bind(this));
+ };
+ PrefixSuffixFormFieldContainer.prototype = {
+ /**
+ * Handles changing the suffix selection.
+ *
+ * @param {Event} event
+ */
+ _changeSuffixSelection: function(event) {
+ if (event.currentTarget.classList.contains('disabled')) {
+ return;
+ }
+
+ var listItems = this._suffixDropdownMenu.children;
+ for (var i = 0, length = listItems.length; i < length; i++) {
+ if (listItems[i] === event.currentTarget) {
+ listItems[i].classList.add('active');
+ }
+ else {
+ listItems[i].classList.remove('active');
+ }
+ }
+
+ this._suffixField.value = elData(event.currentTarget, 'value');
+ this._suffixDropdownToggle.innerHTML = elData(event.currentTarget, 'label') + ' <span class="icon icon16 fa-caret-down pointer"></span>';
+ },
+
+ /**
+ * Destorys the suffix dropdown if the parent form is unregistered.
+ *
+ * @param {object} data event data
+ */
+ _destroyDropdown: function(data) {
+ if (data.formId === this._formId) {
+ UiSimpleDropdown.destroy(this._suffixDropdownMenu.id);
+ }
+ }
+ };
+
+ return PrefixSuffixFormFieldContainer;
+});
--- /dev/null
+<?php
+namespace wcf\system\form\builder\container;
+use wcf\system\form\builder\field\IFormField;
+use wcf\system\form\builder\field\IImmutableFormField;
+use wcf\system\form\builder\field\ISelectionFormField;
+use wcf\system\WCF;
+
+/**
+ * Represents a form field container for one main field with (optional) support for a suffix selection
+ * form field.
+ *
+ * Child elements explicitly added to this container are not shown.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Form\Builder\Container
+ * @since 5.2
+ */
+class SuffixFormFieldContainer extends FormContainer {
+ /**
+ * form field to which the suffix selection is added
+ * @var IFormField
+ */
+ protected $field;
+
+ /**
+ * selection form field containing the suffix options
+ * @var ISelectionFormField
+ */
+ protected $suffixField;
+
+ /**
+ * Sets the form field to which the suffix selection is added and returns this field.
+ *
+ * @param IFormField $formField
+ * @return $this
+ */
+ public function field(IFormField $formField) {
+ if ($this->field !== null) {
+ throw new \BadMethodCallException('Field has already been set.');
+ }
+
+ $this->field = $formField;
+ $this->appendChild($formField);
+
+ return $this;
+ }
+
+ /**
+ * Returns the form field to which the suffix selection is added.
+ *
+ * @return IFormField
+ */
+ public function getField() {
+ if ($this->field === null) {
+ throw new \BadMethodCallException('Field has not been set yet.');
+ }
+
+ return $this->field;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getHtml() {
+ return WCF::getTPL()->fetch('__suffixFormFieldContainer', 'wcf', [
+ 'element' => $this
+ ]);
+ }
+
+ /**
+ * Returns the intial option of the suffix selection dropdown.
+ *
+ * @return string
+ * @throws \BadMethodCallException if no suffix field is set or has no options
+ */
+ public function getSelectedSuffixOption() {
+ if ($this->getSuffixField() === null) {
+ throw new \BadMethodCallException('There is no suffix field for which a label could be determined.');
+ }
+ if (empty($this->getSuffixField()->getOptions())) {
+ throw new \BadMethodCallException('The suffix field has no options.');
+ }
+
+ foreach ($this->getSuffixField()->getNestedOptions() as $option) {
+ if ($this->getSuffixField()->getValue() === null) {
+ if ($option['isSelectable']) {
+ return $option;
+ }
+ }
+ else if ($option['value'] == $this->getSuffixField()->getValue()) {
+ return $option;
+ }
+ }
+
+ throw new \RuntimeException('Cannot determine selected suffix option.');
+ }
+
+ /**
+ * Returns the selection form field containing the suffix options.
+ *
+ * @return ISelectionFormField
+ */
+ public function getSuffixField() {
+ return $this->suffixField;
+ }
+
+ /**
+ * Returns the label used for the suffix selection if the field has no selectable options
+ * or is immutable.
+ *
+ * @return string
+ */
+ public function getSuffixLabel() {
+ if ($this->getSuffixField() === null) {
+ throw new \BadMethodCallException('There is no suffix field for which a label could be determined.');
+ }
+
+ if (empty($this->getSuffixField()->getOptions())) {
+ return '';
+ }
+
+ if (isset($this->getSuffixField()->getOptions()[$this->getSuffixField()->getValue()])) {
+ return $this->getSuffixField()->getOptions()[$this->getSuffixField()->getValue()];
+ }
+
+ return '';
+ }
+
+ /**
+ * Sets the selection form field containing the suffix options.
+ *
+ * @param ISelectionFormField $formField
+ * @return $this
+ * @throws \BadMethodCallException if no suffix field is set
+ */
+ public function suffixField(ISelectionFormField $formField) {
+ if ($this->suffixField !== null) {
+ throw new \BadMethodCallException('Suffix field has already been set.');
+ }
+
+ $this->suffixField = $formField;
+ $this->appendChild($formField);
+
+ return $this;
+ }
+
+ /**
+ * Returns `true` if the suffix selection has any selectable options.
+ *
+ * @return bool
+ */
+ public function suffixHasSelectableOptions() {
+ if ($this->getSuffixField() === null) {
+ return false;
+ }
+
+ if ($this->getSuffixField() instanceof IImmutableFormField && $this->getSuffixField()->isImmutable()) {
+ return false;
+ }
+
+ foreach ($this->getSuffixField()->getNestedOptions() as $option) {
+ if ($option['isSelectable']) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
}
}
+ &.disabled {
+ color: $wcfContentDimmedText;
+
+ > span {
+ cursor: not-allowed !important;
+ }
+ }
+
> a,
> span {
clear: both;