From: Matthias Schmidt Date: Sat, 17 Apr 2021 15:08:01 +0000 (+0200) Subject: Support enabling/disabling `Ui/ItemList/LineBreakSeparatedText` X-Git-Tag: 5.4.0_Alpha_1~80 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=1f32134abb0154db2ff41672e4226fc50ef7b809;p=GitHub%2FWoltLab%2FWCF.git Support enabling/disabling `Ui/ItemList/LineBreakSeparatedText` … to support enabling/disabling user group options. --- diff --git a/ts/WoltLabSuite/Core/Ui/ItemList/LineBreakSeparatedText.ts b/ts/WoltLabSuite/Core/Ui/ItemList/LineBreakSeparatedText.ts index 1f00d74fbc..0c829c1ec8 100644 --- a/ts/WoltLabSuite/Core/Ui/ItemList/LineBreakSeparatedText.ts +++ b/ts/WoltLabSuite/Core/Ui/ItemList/LineBreakSeparatedText.ts @@ -18,12 +18,15 @@ export interface LineBreakSeparatedTextOptions { } export class UiItemListLineBreakSeparatedText { + protected addButton?: HTMLAnchorElement = undefined; protected clearButton?: HTMLAnchorElement = undefined; protected itemInput?: HTMLInputElement = undefined; protected readonly itemList: HTMLUListElement; protected readonly items = new Set(); + protected readonly mutationObserver: MutationObserver; protected readonly options: LineBreakSeparatedTextOptions; protected readonly submitField?: HTMLInputElement = undefined; + protected uiDisabled = false; constructor(itemList: HTMLUListElement, options: LineBreakSeparatedTextOptions = {}) { this.itemList = itemList; @@ -40,6 +43,22 @@ export class UiItemListLineBreakSeparatedText { this.itemList.closest("form")!.addEventListener("submit", () => this.submit()); + // The UI can be used for user group option types which can be enabled/disabled by changing the + // `readonly` attribute, which has to be observed to enable/disable the UI. + this.mutationObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.attributeName === "readonly") { + const input = mutation.target as HTMLInputElement; + + if (input.readOnly) { + this.disableUi(); + } else { + this.enableUi(); + } + } + }); + }); + this.initValues(); this.buildUi(); } @@ -50,6 +69,10 @@ export class UiItemListLineBreakSeparatedText { protected addItem(event: Event): void { event.preventDefault(); + if (this.uiDisabled) { + return; + } + const itemInput = this.itemInput!; const item = itemInput.value.trim(); @@ -93,14 +116,17 @@ export class UiItemListLineBreakSeparatedText { this.itemInput.addEventListener("keydown", (ev) => this.keydown(ev)); this.itemInput.addEventListener("paste", (ev) => this.paste(ev)); inputAddon.appendChild(this.itemInput); + this.mutationObserver.observe(this.itemInput, { + attributes: true, + }); - const addButton = document.createElement("a"); - addButton.href = "#"; - addButton.classList.add("button", "inputSuffix", "jsTooltip"); - addButton.title = Language.get("wcf.global.button.add"); - addButton.innerHTML = ''; - addButton.addEventListener("click", (ev) => this.addItem(ev)); - inputAddon.appendChild(addButton); + this.addButton = document.createElement("a"); + this.addButton.href = "#"; + this.addButton.classList.add("button", "inputSuffix", "jsTooltip"); + this.addButton.title = Language.get("wcf.global.button.add"); + this.addButton.innerHTML = ''; + this.addButton.addEventListener("click", (ev) => this.addItem(ev)); + inputAddon.appendChild(this.addButton); this.clearButton = document.createElement("a"); this.clearButton.href = "#"; @@ -117,8 +143,12 @@ export class UiItemListLineBreakSeparatedText { /** * Clears the item list after clicking on the clear button. */ - protected clearList(ev: Event): void { - ev.preventDefault(); + protected clearList(event: Event): void { + event.preventDefault(); + + if (this.uiDisabled) { + return; + } UiConfirmation.show({ confirm: () => { @@ -136,6 +166,10 @@ export class UiItemListLineBreakSeparatedText { * Deletes an item from the list after clicking on its delete icon. */ protected deleteItem(event: Event): void { + if (this.uiDisabled) { + return; + } + const button = event.currentTarget as HTMLElement; const item = button.closest("li")!.dataset.value!; @@ -156,6 +190,30 @@ export class UiItemListLineBreakSeparatedText { }); } + /** + * Disables the user interface after the input field has been set readonly. + */ + protected disableUi(): void { + this.addButton!.classList.add("disabled"); + this.clearButton!.classList.add("disabled"); + + this.itemList.querySelectorAll(".jsDeleteItem").forEach((button) => button.classList.add("disabled")); + + this.uiDisabled = true; + } + + /** + * Enables the user interface after the input field is no longer readonly. + */ + protected enableUi(): void { + this.addButton!.classList.remove("disabled"); + this.clearButton!.classList.remove("disabled"); + + this.itemList.querySelectorAll(".jsDeleteItem").forEach((button) => button.classList.remove("disabled")); + + this.uiDisabled = false; + } + /** * Hides the item list and clear button. */ @@ -221,6 +279,10 @@ export class UiItemListLineBreakSeparatedText { * field. */ protected paste(event: ClipboardEvent): void { + if (this.uiDisabled) { + return; + } + const items = event.clipboardData!.getData("text/plain").split("\n"); if (items.length > 1) { event.preventDefault(); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/LineBreakSeparatedText.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/LineBreakSeparatedText.js index b8ef7ca454..e3df337d62 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/LineBreakSeparatedText.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/ItemList/LineBreakSeparatedText.js @@ -17,10 +17,12 @@ define(["require", "exports", "tslib", "../Confirmation", "../../Language", "../ Util_1 = tslib_1.__importDefault(Util_1); class UiItemListLineBreakSeparatedText { constructor(itemList, options = {}) { + this.addButton = undefined; this.clearButton = undefined; this.itemInput = undefined; this.items = new Set(); this.submitField = undefined; + this.uiDisabled = false; this.itemList = itemList; this.options = options; if (!this.options.submitFieldName) { @@ -33,6 +35,21 @@ define(["require", "exports", "tslib", "../Confirmation", "../../Language", "../ } } this.itemList.closest("form").addEventListener("submit", () => this.submit()); + // The UI can be used for user group option types which can be enabled/disabled by changing the + // `readonly` attribute, which has to be observed to enable/disable the UI. + this.mutationObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.attributeName === "readonly") { + const input = mutation.target; + if (input.readOnly) { + this.disableUi(); + } + else { + this.enableUi(); + } + } + }); + }); this.initValues(); this.buildUi(); } @@ -41,6 +58,9 @@ define(["require", "exports", "tslib", "../Confirmation", "../../Language", "../ */ addItem(event) { event.preventDefault(); + if (this.uiDisabled) { + return; + } const itemInput = this.itemInput; const item = itemInput.value.trim(); if (item === "") { @@ -75,13 +95,16 @@ define(["require", "exports", "tslib", "../Confirmation", "../../Language", "../ this.itemInput.addEventListener("keydown", (ev) => this.keydown(ev)); this.itemInput.addEventListener("paste", (ev) => this.paste(ev)); inputAddon.appendChild(this.itemInput); - const addButton = document.createElement("a"); - addButton.href = "#"; - addButton.classList.add("button", "inputSuffix", "jsTooltip"); - addButton.title = Language.get("wcf.global.button.add"); - addButton.innerHTML = ''; - addButton.addEventListener("click", (ev) => this.addItem(ev)); - inputAddon.appendChild(addButton); + this.mutationObserver.observe(this.itemInput, { + attributes: true, + }); + this.addButton = document.createElement("a"); + this.addButton.href = "#"; + this.addButton.classList.add("button", "inputSuffix", "jsTooltip"); + this.addButton.title = Language.get("wcf.global.button.add"); + this.addButton.innerHTML = ''; + this.addButton.addEventListener("click", (ev) => this.addItem(ev)); + inputAddon.appendChild(this.addButton); this.clearButton = document.createElement("a"); this.clearButton.href = "#"; this.clearButton.classList.add("button", "inputSuffix", "jsTooltip"); @@ -96,8 +119,11 @@ define(["require", "exports", "tslib", "../Confirmation", "../../Language", "../ /** * Clears the item list after clicking on the clear button. */ - clearList(ev) { - ev.preventDefault(); + clearList(event) { + event.preventDefault(); + if (this.uiDisabled) { + return; + } UiConfirmation.show({ confirm: () => { this.itemList.innerHTML = ""; @@ -112,6 +138,9 @@ define(["require", "exports", "tslib", "../Confirmation", "../../Language", "../ * Deletes an item from the list after clicking on its delete icon. */ deleteItem(event) { + if (this.uiDisabled) { + return; + } const button = event.currentTarget; const item = button.closest("li").dataset.value; UiConfirmation.show({ @@ -128,6 +157,24 @@ define(["require", "exports", "tslib", "../Confirmation", "../../Language", "../ messageIsHtml: true, }); } + /** + * Disables the user interface after the input field has been set readonly. + */ + disableUi() { + this.addButton.classList.add("disabled"); + this.clearButton.classList.add("disabled"); + this.itemList.querySelectorAll(".jsDeleteItem").forEach((button) => button.classList.add("disabled")); + this.uiDisabled = true; + } + /** + * Enables the user interface after the input field is no longer readonly. + */ + enableUi() { + this.addButton.classList.remove("disabled"); + this.clearButton.classList.remove("disabled"); + this.itemList.querySelectorAll(".jsDeleteItem").forEach((button) => button.classList.remove("disabled")); + this.uiDisabled = false; + } /** * Hides the item list and clear button. */ @@ -182,6 +229,9 @@ define(["require", "exports", "tslib", "../Confirmation", "../../Language", "../ * field. */ paste(event) { + if (this.uiDisabled) { + return; + } const items = event.clipboardData.getData("text/plain").split("\n"); if (items.length > 1) { event.preventDefault();