Upgrade the `LabelFormField` to use the new picker
authorAlexander Ebert <ebert@woltlab.com>
Sat, 7 Oct 2023 16:17:09 +0000 (18:17 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sun, 8 Oct 2023 15:54:51 +0000 (17:54 +0200)
com.woltlab.wcf/templates/__labelFormField.tpl
ts/WoltLabSuite/Core/Form/Builder/Field/Controller/Label.ts [deleted file]
wcfsetup/install/files/acp/templates/__labelFormField.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/Label.js [deleted file]
wcfsetup/install/files/lib/system/form/builder/field/label/LabelFormField.class.php

index fe67833766f9b9ab38acdc8beadf0ce2131da371..3f1037818b9e4db51b62d0266d0e31691c43cd75 100644 (file)
@@ -1,41 +1 @@
-<ul class="labelList jsOnly" data-object-id="{@$field->getLabelGroup()->groupID}">
-       <li class="dropdown labelChooser" data-group-id="{@$field->getLabelGroup()->groupID}">
-               <div class="dropdownToggle" data-toggle="labelGroup{@$field->getLabelGroup()->groupID}">
-                       <span class="badge label">{lang}wcf.label.none{/lang}</span>
-               </div>
-               <div class="dropdownMenu">
-                       <ul class="scrollableDropdownMenu">
-                               {foreach from=$field->getLabelGroup() item=label}
-                                       <li data-label-id="{@$label->labelID}">
-                                               <span>{@$label->render()}</span>
-                                       </li>
-                               {/foreach}
-                       </ul>
-               </div>
-       </li>
-</ul>
-
-<noscript>
-       <select name="{$field->getPrefixedId()}[{@$field->getLabelGroup()->groupID}]">
-               {foreach from=$field->getLabelGroup() item=label}
-                       <option value="{$label->labelID}">{$label->getTitle()}</option>
-               {/foreach}
-       </select>
-</noscript>
-
-<script data-relocate="true">
-       require(['Dom/Util', 'Language', 'WoltLabSuite/Core/Form/Builder/Field/Controller/Label'], function(DomUtil, Language, FormBuilderFieldLabel) {
-               Language.addObject({
-                       'wcf.label.none': '{jslang}wcf.label.none{/jslang}',
-                       'wcf.label.withoutSelection': '{jslang}wcf.label.withoutSelection{/jslang}'
-               });
-               
-               new FormBuilderFieldLabel(
-                       '{@$field->getPrefixedId()|encodeJS}',
-                       {if $field->getValue()}'{$field->getValue()|encodeJS}'{else}null{/if},
-                       {
-                               forceSelection: {if $field->getLabelGroup()->forceSelection}true{else}false{/if}
-                       }
-               );
-       });
-</script>
+{@$field->getLabelPicker()->toHtml()}
diff --git a/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/Label.ts b/ts/WoltLabSuite/Core/Form/Builder/Field/Controller/Label.ts
deleted file mode 100644 (file)
index 17ae26a..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Handles the JavaScript part of the label form field.
- *
- * @author  Alexander Ebert, Matthias Schmidt
- * @copyright 2001-2020 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @since 5.2
- */
-
-import * as Core from "../../../../Core";
-import * as DomUtil from "../../../../Dom/Util";
-import * as Language from "../../../../Language";
-import UiDropdownSimple from "../../../../Ui/Dropdown/Simple";
-import { LabelFormFieldOptions } from "../../Data";
-
-class Label {
-  protected readonly _formFieldContainer: HTMLElement;
-  protected readonly _input: HTMLInputElement;
-  protected readonly _labelChooser: HTMLElement;
-  protected readonly _options: LabelFormFieldOptions;
-
-  constructor(fieldId: string, labelId: string, options: Partial<LabelFormFieldOptions>) {
-    this._formFieldContainer = document.getElementById(fieldId + "Container")!;
-    this._labelChooser = this._formFieldContainer.getElementsByClassName("labelChooser")[0] as HTMLElement;
-    this._options = Core.extend(
-      {
-        forceSelection: false,
-        showWithoutSelection: false,
-      },
-      options,
-    ) as LabelFormFieldOptions;
-
-    this._input = document.createElement("input");
-    this._input.type = "hidden";
-    this._input.id = fieldId;
-    this._input.name = fieldId;
-    this._input.value = labelId;
-    this._formFieldContainer.appendChild(this._input);
-
-    const labelChooserId = DomUtil.identify(this._labelChooser);
-
-    // init dropdown
-    let dropdownMenu = UiDropdownSimple.getDropdownMenu(labelChooserId)!;
-    if (dropdownMenu === null) {
-      UiDropdownSimple.init(this._labelChooser.getElementsByClassName("dropdownToggle")[0] as HTMLElement);
-      dropdownMenu = UiDropdownSimple.getDropdownMenu(labelChooserId)!;
-    }
-
-    let additionalOptionList: HTMLUListElement | null = null;
-    if (this._options.showWithoutSelection || !this._options.forceSelection) {
-      additionalOptionList = document.createElement("ul");
-      dropdownMenu.appendChild(additionalOptionList);
-
-      const dropdownDivider = document.createElement("li");
-      dropdownDivider.classList.add("dropdownDivider");
-      additionalOptionList.appendChild(dropdownDivider);
-    }
-
-    if (this._options.showWithoutSelection) {
-      const listItem = document.createElement("li");
-      listItem.dataset.labelId = "-1";
-      this._blockScroll(listItem);
-      additionalOptionList!.appendChild(listItem);
-
-      const span = document.createElement("span");
-      listItem.appendChild(span);
-
-      const label = document.createElement("span");
-      label.classList.add("badge", "label");
-      label.innerHTML = Language.get("wcf.label.withoutSelection");
-      span.appendChild(label);
-    }
-
-    if (!this._options.forceSelection) {
-      const listItem = document.createElement("li");
-      listItem.dataset.labelId = "0";
-      this._blockScroll(listItem);
-      additionalOptionList!.appendChild(listItem);
-
-      const span = document.createElement("span");
-      listItem.appendChild(span);
-
-      const label = document.createElement("span");
-      label.classList.add("badge", "label");
-      label.innerHTML = Language.get("wcf.label.none");
-      span.appendChild(label);
-    }
-
-    dropdownMenu.querySelectorAll("li:not(.dropdownDivider)").forEach((listItem: HTMLElement) => {
-      listItem.addEventListener("click", (ev) => this._click(ev));
-
-      if (labelId) {
-        if (listItem.dataset.labelId === labelId) {
-          this._selectLabel(listItem);
-        }
-      }
-    });
-  }
-
-  _blockScroll(element: HTMLElement): void {
-    element.addEventListener("wheel", (ev) => ev.preventDefault(), {
-      passive: false,
-    });
-  }
-
-  _click(event: Event): void {
-    event.preventDefault();
-
-    this._selectLabel(event.currentTarget as HTMLElement);
-  }
-
-  _selectLabel(label: HTMLElement): void {
-    // save label
-    let labelId = label.dataset.labelId;
-    if (!labelId) {
-      labelId = "0";
-    }
-
-    // replace button with currently selected label
-    const displayLabel = label.querySelector("span > span")!;
-    const button = this._labelChooser.querySelector(".dropdownToggle > span")!;
-    button.className = displayLabel.className;
-    button.textContent = displayLabel.textContent;
-
-    this._input.value = labelId;
-  }
-}
-
-export = Label;
index fe67833766f9b9ab38acdc8beadf0ce2131da371..3f1037818b9e4db51b62d0266d0e31691c43cd75 100644 (file)
@@ -1,41 +1 @@
-<ul class="labelList jsOnly" data-object-id="{@$field->getLabelGroup()->groupID}">
-       <li class="dropdown labelChooser" data-group-id="{@$field->getLabelGroup()->groupID}">
-               <div class="dropdownToggle" data-toggle="labelGroup{@$field->getLabelGroup()->groupID}">
-                       <span class="badge label">{lang}wcf.label.none{/lang}</span>
-               </div>
-               <div class="dropdownMenu">
-                       <ul class="scrollableDropdownMenu">
-                               {foreach from=$field->getLabelGroup() item=label}
-                                       <li data-label-id="{@$label->labelID}">
-                                               <span>{@$label->render()}</span>
-                                       </li>
-                               {/foreach}
-                       </ul>
-               </div>
-       </li>
-</ul>
-
-<noscript>
-       <select name="{$field->getPrefixedId()}[{@$field->getLabelGroup()->groupID}]">
-               {foreach from=$field->getLabelGroup() item=label}
-                       <option value="{$label->labelID}">{$label->getTitle()}</option>
-               {/foreach}
-       </select>
-</noscript>
-
-<script data-relocate="true">
-       require(['Dom/Util', 'Language', 'WoltLabSuite/Core/Form/Builder/Field/Controller/Label'], function(DomUtil, Language, FormBuilderFieldLabel) {
-               Language.addObject({
-                       'wcf.label.none': '{jslang}wcf.label.none{/jslang}',
-                       'wcf.label.withoutSelection': '{jslang}wcf.label.withoutSelection{/jslang}'
-               });
-               
-               new FormBuilderFieldLabel(
-                       '{@$field->getPrefixedId()|encodeJS}',
-                       {if $field->getValue()}'{$field->getValue()|encodeJS}'{else}null{/if},
-                       {
-                               forceSelection: {if $field->getLabelGroup()->forceSelection}true{else}false{/if}
-                       }
-               );
-       });
-</script>
+{@$field->getLabelPicker()->toHtml()}
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/Label.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Form/Builder/Field/Controller/Label.js
deleted file mode 100644 (file)
index 747cb98..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * Handles the JavaScript part of the label form field.
- *
- * @author  Alexander Ebert, Matthias Schmidt
- * @copyright 2001-2020 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @since 5.2
- */
-define(["require", "exports", "tslib", "../../../../Core", "../../../../Dom/Util", "../../../../Language", "../../../../Ui/Dropdown/Simple"], function (require, exports, tslib_1, Core, DomUtil, Language, Simple_1) {
-    "use strict";
-    Core = tslib_1.__importStar(Core);
-    DomUtil = tslib_1.__importStar(DomUtil);
-    Language = tslib_1.__importStar(Language);
-    Simple_1 = tslib_1.__importDefault(Simple_1);
-    class Label {
-        _formFieldContainer;
-        _input;
-        _labelChooser;
-        _options;
-        constructor(fieldId, labelId, options) {
-            this._formFieldContainer = document.getElementById(fieldId + "Container");
-            this._labelChooser = this._formFieldContainer.getElementsByClassName("labelChooser")[0];
-            this._options = Core.extend({
-                forceSelection: false,
-                showWithoutSelection: false,
-            }, options);
-            this._input = document.createElement("input");
-            this._input.type = "hidden";
-            this._input.id = fieldId;
-            this._input.name = fieldId;
-            this._input.value = labelId;
-            this._formFieldContainer.appendChild(this._input);
-            const labelChooserId = DomUtil.identify(this._labelChooser);
-            // init dropdown
-            let dropdownMenu = Simple_1.default.getDropdownMenu(labelChooserId);
-            if (dropdownMenu === null) {
-                Simple_1.default.init(this._labelChooser.getElementsByClassName("dropdownToggle")[0]);
-                dropdownMenu = Simple_1.default.getDropdownMenu(labelChooserId);
-            }
-            let additionalOptionList = null;
-            if (this._options.showWithoutSelection || !this._options.forceSelection) {
-                additionalOptionList = document.createElement("ul");
-                dropdownMenu.appendChild(additionalOptionList);
-                const dropdownDivider = document.createElement("li");
-                dropdownDivider.classList.add("dropdownDivider");
-                additionalOptionList.appendChild(dropdownDivider);
-            }
-            if (this._options.showWithoutSelection) {
-                const listItem = document.createElement("li");
-                listItem.dataset.labelId = "-1";
-                this._blockScroll(listItem);
-                additionalOptionList.appendChild(listItem);
-                const span = document.createElement("span");
-                listItem.appendChild(span);
-                const label = document.createElement("span");
-                label.classList.add("badge", "label");
-                label.innerHTML = Language.get("wcf.label.withoutSelection");
-                span.appendChild(label);
-            }
-            if (!this._options.forceSelection) {
-                const listItem = document.createElement("li");
-                listItem.dataset.labelId = "0";
-                this._blockScroll(listItem);
-                additionalOptionList.appendChild(listItem);
-                const span = document.createElement("span");
-                listItem.appendChild(span);
-                const label = document.createElement("span");
-                label.classList.add("badge", "label");
-                label.innerHTML = Language.get("wcf.label.none");
-                span.appendChild(label);
-            }
-            dropdownMenu.querySelectorAll("li:not(.dropdownDivider)").forEach((listItem) => {
-                listItem.addEventListener("click", (ev) => this._click(ev));
-                if (labelId) {
-                    if (listItem.dataset.labelId === labelId) {
-                        this._selectLabel(listItem);
-                    }
-                }
-            });
-        }
-        _blockScroll(element) {
-            element.addEventListener("wheel", (ev) => ev.preventDefault(), {
-                passive: false,
-            });
-        }
-        _click(event) {
-            event.preventDefault();
-            this._selectLabel(event.currentTarget);
-        }
-        _selectLabel(label) {
-            // save label
-            let labelId = label.dataset.labelId;
-            if (!labelId) {
-                labelId = "0";
-            }
-            // replace button with currently selected label
-            const displayLabel = label.querySelector("span > span");
-            const button = this._labelChooser.querySelector(".dropdownToggle > span");
-            button.className = displayLabel.className;
-            button.textContent = displayLabel.textContent;
-            this._input.value = labelId;
-        }
-    }
-    return Label;
-});
index 3f4ff07017a0eb5cf4e125c995217d2157f90b6b..b5502a463b59fc880382fb31bf31a61ef23c1bd5 100644 (file)
@@ -12,6 +12,8 @@ use wcf\system\form\builder\IFormDocument;
 use wcf\system\form\builder\IObjectTypeFormNode;
 use wcf\system\form\builder\TObjectTypeFormNode;
 use wcf\system\label\LabelHandler;
+use wcf\system\label\LabelPicker;
+use wcf\system\label\LabelPickerGroup;
 
 /**
  * Implementation of a form field to select labels.
@@ -36,6 +38,8 @@ final class LabelFormField extends AbstractFormField implements IObjectTypeFormN
      */
     protected $labelGroup;
 
+    protected LabelPickerGroup $labelPickerGroup;
+
     /**
      * @inheritDoc
      */
@@ -44,23 +48,43 @@ final class LabelFormField extends AbstractFormField implements IObjectTypeFormN
     /**
      * loaded labels grouped by label object type and object id to avoid loading the same labels
      * over and over again for the same object and different label groups
-     * @var Label[][]
+     * @var Label[][][]
      */
     protected static $loadedLabels = [];
 
     /**
      * Returns the label group whose labels can be selected via this form field.
+     */
+    public function getLabelGroup(): ViewableLabelGroup
+    {
+        return $this->getLabelPicker()->labelGroup;
+    }
+
+    /**
+     * Returns the label picker for this field.
      *
-     * @return  ViewableLabelGroup      label group whose labels can be selected
-     * @throws  \BadMethodCallException     if no label has been set
+     * @since 6.1
      */
-    public function getLabelGroup()
+    public function getLabelPicker(): LabelPicker
     {
-        if ($this->labelGroup === null) {
+        if (!isset($this->labelPickerGroup)) {
             throw new \BadMethodCallException("No label group has been set for field '{$this->getId()}'.");
         }
 
-        return $this->labelGroup;
+        foreach ($this->labelPickerGroup as $labelPicker) {
+            return $labelPicker;
+        }
+
+        throw new \RuntimeException("Unreachable");
+    }
+
+    #[\Override]
+    public function getFieldHtml()
+    {
+        $this->labelPickerGroup->setName($this->getPrefixedId());
+        $this->getLabelPicker()->setElementID($this->getPrefixedId());
+
+        return parent::getFieldHtml();
     }
 
     /**
@@ -91,6 +115,10 @@ final class LabelFormField extends AbstractFormField implements IObjectTypeFormN
     public function labelGroup(ViewableLabelGroup $labelGroup)
     {
         $this->labelGroup = $labelGroup;
+        $this->labelPickerGroup = LabelPickerGroup::fromViewableLabelGroups(
+            [$this->labelGroup],
+            false,
+        );
 
         if ($this->label === null) {
             $this->label($this->getLabelGroup()->getTitle());
@@ -119,12 +147,10 @@ final class LabelFormField extends AbstractFormField implements IObjectTypeFormN
                 static::$loadedLabels[$objectTypeID][$objectID] = $assignedLabels[$objectID] ?? [];
             }
 
-            $labelIDs = $this->getLabelGroup()->getLabelIDs();
-            /** @var Label $label */
-            foreach (static::$loadedLabels[$objectTypeID][$objectID] as $label) {
-                if (\in_array($label->labelID, $labelIDs)) {
-                    $this->value($label->labelID);
-                }
+            $this->labelPickerGroup->setSelectedLabelsFromAssignedLabels(self::$loadedLabels[$objectTypeID][$objectID]);
+            foreach ($this->labelPickerGroup as $labelPicker) {
+                $this->value($labelPicker->getSelectedValue());
+                break;
             }
         }
 
@@ -163,7 +189,8 @@ final class LabelFormField extends AbstractFormField implements IObjectTypeFormN
     public function readValue()
     {
         if ($this->getDocument()->hasRequestData($this->getPrefixedId())) {
-            $this->value = \intval($this->getDocument()->getRequestData($this->getPrefixedId()));
+            $this->labelPickerGroup->setSelectedLabels($this->getDocument()->getRequestData($this->getPrefixedId()));
+            $this->value = $this->getLabelPicker()->getSelectedValue();
         }
 
         return $this;