diff --git a/com.woltlab.wcf/templates/shared_booleanFormField.tpl b/com.woltlab.wcf/templates/shared_booleanFormField.tpl index fb7e2ed4589..07c11ce7d68 100644 --- a/com.woltlab.wcf/templates/shared_booleanFormField.tpl +++ b/com.woltlab.wcf/templates/shared_booleanFormField.tpl @@ -1,29 +1,11 @@ -
    -
  1. - getFieldClasses()|empty} class="{implode from=$field->getFieldClasses() item='class' glue=' '}{$class}{/implode}"{/if}{* - *}{if $field->isAutofocused()} autofocus{/if}{* - *}{if $field->isRequired()} required{/if}{* - *}{if $field->isImmutable()} disabled{/if}{* - *}{if $field->getValue()} checked{/if}{* - *}{foreach from=$field->getFieldAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{* - *}> - -
  2. -
  3. - getFieldClasses()|empty} class="{implode from=$field->getFieldClasses() item='class' glue=' '}{$class}{/implode}"{/if}{* - *}{if $field->isImmutable()} disabled{/if}{* - *}{if !$field->getValue()} checked{/if}{* - *}{foreach from=$field->getFieldAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{* - *}> - -
  4. -
+getFieldClasses()|empty} class="{implode from=$field->getFieldClasses() item='class' glue=' '}{$class}{/implode}"{/if}{* + *}{if $field->isAutofocused()} autofocus{/if}{* + *}{if $field->isRequired()} required{/if}{* + *}{if $field->isImmutable()} disabled{/if}{* + *}{if $field->getValue()} checked{/if}{* + *}{foreach from=$field->getFieldAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{* +*}> diff --git a/com.woltlab.wcf/templates/shared_checkboxFormField.tpl b/com.woltlab.wcf/templates/shared_checkboxFormField.tpl index 5e92fc55b29..0a1832eb078 100644 --- a/com.woltlab.wcf/templates/shared_checkboxFormField.tpl +++ b/com.woltlab.wcf/templates/shared_checkboxFormField.tpl @@ -1,23 +1,10 @@ -
getClasses()|empty} class="{implode from=$field->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{foreach from=$field->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{if !$field->checkDependencies()} style="display: none;"{/if}> -
-
- - - {include file='shared_formFieldDescription'} - {include file='shared_formFieldErrors'} - {include file='shared_formFieldDependencies'} - {include file='shared_formFieldDataHandler'} -
-
+getFieldClasses()|empty} class="{implode from=$field->getFieldClasses() item='class' glue=' '}{$class}{/implode}"{/if}{* + *}{if $field->isRequired()} required{/if}{* + *}{if $field->isImmutable()} disabled{/if}{* + *}{if $field->getValue()} checked{/if}{* + *}{foreach from=$field->getFieldAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{* +*}> diff --git a/com.woltlab.wcf/templates/shared_inlineLabelFormField.tpl b/com.woltlab.wcf/templates/shared_inlineLabelFormField.tpl new file mode 100644 index 00000000000..c4f7b10cce4 --- /dev/null +++ b/com.woltlab.wcf/templates/shared_inlineLabelFormField.tpl @@ -0,0 +1,18 @@ +
getClasses()|empty} class="{implode from=$field->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{* + *}{foreach from=$field->getAttributes() key='attributeName' item='attributeValue'} {$attributeName}="{$attributeValue}"{/foreach}{* + *}{if !$field->checkDependencies()} style="display: none;"{/if}{* +*}> +
+
+ + + {include file='shared_formFieldErrors'} + {include file='shared_formFieldDescription'} + {include file='shared_formFieldDependencies'} + {include file='shared_formFieldDataHandler'} +
+
diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager.ts index d064eb31ac3..5af259c58c9 100644 --- a/ts/WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager.ts +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/Dependency/Manager.ts @@ -92,7 +92,8 @@ function _show(node: HTMLElement): void { }); } - node.querySelectorAll("input, select, textarea").forEach((validatedField: HTMLInputElement | HTMLSelectElement) => { + const validatedFields = "input, select, textarea, woltlab-core-toggle-button"; + node.querySelectorAll(validatedFields).forEach((validatedField: HTMLInputElement | HTMLSelectElement) => { // if a container is shown, ignore all fields that // have a hidden parent element within the container let parentNode = validatedField.parentNode! as HTMLElement; @@ -149,11 +150,13 @@ export function addDependency(dependency: FormBuilderFormFieldDependency): void if (!_fields.has(id)) { _fields.set(id, field); + // Toggle buttons fire `change` on every state change, including programmatic ones. if ( - field.tagName === "INPUT" && - ((field as HTMLInputElement).type === "checkbox" || - (field as HTMLInputElement).type === "radio" || - (field as HTMLInputElement).type === "hidden") + field.tagName === "WOLTLAB-CORE-TOGGLE-BUTTON" || + (field.tagName === "INPUT" && + ((field as HTMLInputElement).type === "checkbox" || + (field as HTMLInputElement).type === "radio" || + (field as HTMLInputElement).type === "hidden")) ) { field.addEventListener("change", () => checkDependencies()); } else { diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/Dependency/Value.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/Dependency/Value.ts index 096f594bf9a..eb418dd8302 100644 --- a/ts/WoltLabSuite/Core/Form/Builder/Field/Dependency/Value.ts +++ b/ts/WoltLabSuite/Core/Form/Builder/Field/Dependency/Value.ts @@ -26,7 +26,13 @@ class Value extends Abstract { return false; } - values.push((this._field as HTMLInputElement).value); + if (this._field.tagName === "WOLTLAB-CORE-TOGGLE-BUTTON") { + // The value of a toggle button is always set, regardless of its state. + const toggleButton = this._field as WoltlabCoreToggleButtonElement; + values.push(toggleButton.checked ? toggleButton.value : "0"); + } else { + values.push((this._field as HTMLInputElement).value); + } } else { let hasCheckedField = true; this._fields.forEach((field: HTMLInputElement) => { diff --git a/ts/WoltLabSuite/WebComponent/woltlab-core-toggle-button.ts b/ts/WoltLabSuite/WebComponent/woltlab-core-toggle-button.ts index 30716755344..7af56662c0c 100644 --- a/ts/WoltLabSuite/WebComponent/woltlab-core-toggle-button.ts +++ b/ts/WoltLabSuite/WebComponent/woltlab-core-toggle-button.ts @@ -2,6 +2,9 @@ * `` creates a toggle button. * Usage: `` * + * The element is form-associated and submits its `value` (defaults to `1`) + * when checked, behaving like a regular checkbox. + * * @author Marcel Werk * @copyright 2001-2024 WoltLab GmbH * @license GNU Lesser General Public License @@ -10,13 +13,20 @@ { class WoltlabCoreToggleButtonElement extends HTMLElement { + static formAssociated = true; + static get observedAttributes() { - return ["checked"]; + return ["checked", "required"]; } + readonly #internals: ElementInternals; + #defaultChecked: boolean | undefined = undefined; + constructor() { super(); + this.#internals = this.attachInternals(); + const shadow = this.attachShadow({ mode: "open" }); const style = document.createElement("style"); style.textContent = ` @@ -24,7 +34,11 @@ display: inline-flex; vertical-align: middle; } - + + :host(:disabled) { + opacity: 0.54; + } + [part="track"] { position: relative; border-radius: 14px; @@ -35,6 +49,10 @@ transition: 0.4s; } + :host(:disabled) [part="track"] { + cursor: default; + } + :host([checked]) [part="track"] { background-color: var(--wcfToggleButtonBackgroundActive); } @@ -74,46 +92,49 @@ const iconSlot = document.createElement("slot"); iconSlot.name = "icon"; slider.append(iconSlot); - const checkboxSlot = document.createElement("slot"); - checkboxSlot.name = "checkbox"; - shadow.append(style, track, checkboxSlot); + shadow.append(style, track); + // Clicks on an associated `