From 8305449dc3f76813ec3f506a62c24aed25dc4ede Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Mon, 27 Jan 2025 11:34:32 +0100 Subject: [PATCH 01/12] Add new inline editor --- .../Core/Component/InlineEditor.ts | 34 +++++++++++++++++++ .../Core/Component/InlineEditor.js | 31 +++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 ts/WoltLabSuite/Core/Component/InlineEditor.ts create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js diff --git a/ts/WoltLabSuite/Core/Component/InlineEditor.ts b/ts/WoltLabSuite/Core/Component/InlineEditor.ts new file mode 100644 index 00000000000..79c2c5d3f33 --- /dev/null +++ b/ts/WoltLabSuite/Core/Component/InlineEditor.ts @@ -0,0 +1,34 @@ +/** + * Provides an inline editor for objects with a dropdown menu. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ + +import { DropdownBuilderItemData, create as createDropDownMenu } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; +import { stringToBool } from "WoltLabSuite/Core/Core"; + +export abstract class InlineEditor { + protected readonly element: HTMLElement; + protected readonly dropdownToggle: HTMLElement; + protected readonly dropdownMenu: HTMLUListElement | null = null; + + protected constructor(element: HTMLElement, dropdownToggleSelector: string) { + this.element = element; + this.dropdownToggle = this.element.querySelector(dropdownToggleSelector) as HTMLElement; + this.dropdownMenu = createDropDownMenu(this.getDropdownOptions()); + this.dropdownToggle.parentElement!.appendChild(this.dropdownMenu); + } + + abstract getDropdownOptions(): DropdownBuilderItemData[]; + + public getPermission(permission: string): boolean { + if (!Object.prototype.hasOwnProperty.call(this.element.dataset, permission)) { + return false; + } + + return stringToBool(this.element.dataset[permission]!); + } +} diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js new file mode 100644 index 00000000000..c22c470cafd --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js @@ -0,0 +1,31 @@ +/** + * Provides an inline editor for objects with a dropdown menu. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ +define(["require", "exports", "WoltLabSuite/Core/Ui/Dropdown/Builder", "WoltLabSuite/Core/Core"], function (require, exports, Builder_1, Core_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.InlineEditor = void 0; + class InlineEditor { + element; + dropdownToggle; + dropdownMenu = null; + constructor(element, dropdownToggleSelector) { + this.element = element; + this.dropdownToggle = this.element.querySelector(dropdownToggleSelector); + this.dropdownMenu = (0, Builder_1.create)(this.getDropdownOptions()); + this.dropdownToggle.parentElement.appendChild(this.dropdownMenu); + } + getPermission(permission) { + if (!Object.prototype.hasOwnProperty.call(this.element.dataset, permission)) { + return false; + } + return (0, Core_1.stringToBool)(this.element.dataset[permission]); + } + } + exports.InlineEditor = InlineEditor; +}); From 9e89babb58b30e0864f22dba832cfc28ebfd56f5 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Mon, 27 Jan 2025 13:23:15 +0100 Subject: [PATCH 02/12] Add functions to set the menu entries and get the permissions --- .../Core/Component/InlineEditor.ts | 110 ++++++++++++++++-- .../Core/Component/InlineEditor.js | 80 +++++++++++-- 2 files changed, 171 insertions(+), 19 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/InlineEditor.ts b/ts/WoltLabSuite/Core/Component/InlineEditor.ts index 79c2c5d3f33..40df5d105fb 100644 --- a/ts/WoltLabSuite/Core/Component/InlineEditor.ts +++ b/ts/WoltLabSuite/Core/Component/InlineEditor.ts @@ -1,5 +1,5 @@ /** - * Provides an inline editor for objects with a dropdown menu. + * Provides an inline editor for objects using a drop-down menu. * * @author Olaf Braun * @copyright 2001-2025 WoltLab GmbH @@ -7,28 +7,114 @@ * @since 6.2 */ -import { DropdownBuilderItemData, create as createDropDownMenu } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; +import { + DropdownBuilderItemData, + create as createDropdownMenu, + attach as attachDropdownMenu, + setItems as setDropdownItems, +} from "WoltLabSuite/Core/Ui/Dropdown/Builder"; import { stringToBool } from "WoltLabSuite/Core/Core"; -export abstract class InlineEditor { +export interface DropdownMenuItem { + visible?: () => boolean; + item: DropdownBuilderItemData; +} + +const inlineEditors = new Map(); + +export class InlineEditor { protected readonly element: HTMLElement; protected readonly dropdownToggle: HTMLElement; - protected readonly dropdownMenu: HTMLUListElement | null = null; + protected permissions: Record = {}; + protected readonly dropdownMenu: HTMLUListElement; + protected readonly menuItems: DropdownMenuItem[] = []; - protected constructor(element: HTMLElement, dropdownToggleSelector: string) { + public constructor(element: HTMLElement, dropdownToggleSelector: string) { this.element = element; this.dropdownToggle = this.element.querySelector(dropdownToggleSelector) as HTMLElement; - this.dropdownMenu = createDropDownMenu(this.getDropdownOptions()); - this.dropdownToggle.parentElement!.appendChild(this.dropdownMenu); - } + this.dropdownMenu = createDropdownMenu([]); + attachDropdownMenu(this.dropdownMenu, this.dropdownToggle); - abstract getDropdownOptions(): DropdownBuilderItemData[]; + inlineEditors.set(this.element, this); + } - public getPermission(permission: string): boolean { - if (!Object.prototype.hasOwnProperty.call(this.element.dataset, permission)) { + /** + * Gets the state of a property from the element's dataset as a boolean. + */ + public getState(propertyName: string): boolean { + if (!Object.prototype.hasOwnProperty.call(this.element.dataset, propertyName)) { return false; } - return stringToBool(this.element.dataset[permission]!); + return stringToBool(this.element.dataset[propertyName]!); + } + + /** + * Updates the state of the element's dataset with the provided data. + */ + public updateState(data: Record): void { + Object.entries(data).forEach(([key, value]) => { + this.element.dataset[key] = value ? "1" : "0"; + }); + + this.rebuildDropdownMenu(); + } + + /** + * Sets the permissions for the inline editor. + */ + public setPermissions(permissions: Record): void { + this.permissions = permissions; + + this.rebuildDropdownMenu(); } + + /** + * Gets the permissions for the inline editor. + */ + public getPermissions(): Record { + return this.permissions; + } + + /** + * Adds a menu item to the dropdown menu and rebuilds the menu. + */ + public addMenuItem(menuItem: DropdownMenuItem): void { + this.menuItems.push(menuItem); + + this.rebuildDropdownMenu(); + } + + /** + * Adds multiple menu items to the dropdown menu and rebuilds the menu. + */ + public addMenuItems(menuItems: DropdownMenuItem[]): void { + this.menuItems.push(...menuItems); + + this.rebuildDropdownMenu(); + } + + /** + * Rebuilds the dropdown menu based on the current menu items and their visibility. + */ + public rebuildDropdownMenu(): void { + const dropdownMenuItems = this.menuItems + .filter((item) => { + return item.visible === undefined || item.visible(); + }) + .map((item) => item.item); + + if (dropdownMenuItems.length === 0) { + this.dropdownMenu.innerHTML = ""; + } else { + setDropdownItems(this.dropdownMenu, dropdownMenuItems); + } + } +} + +/** + * Gets the inline editor instance associated with the given element. + */ +export function getInlineEditor(element: HTMLElement): InlineEditor | undefined { + return inlineEditors.get(element); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js index c22c470cafd..d995559012b 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js @@ -1,5 +1,5 @@ /** - * Provides an inline editor for objects with a dropdown menu. + * Provides an inline editor for objects using a drop-down menu. * * @author Olaf Braun * @copyright 2001-2025 WoltLab GmbH @@ -10,22 +10,88 @@ define(["require", "exports", "WoltLabSuite/Core/Ui/Dropdown/Builder", "WoltLabS "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InlineEditor = void 0; + exports.getInlineEditor = getInlineEditor; + const inlineEditors = new Map(); class InlineEditor { element; dropdownToggle; - dropdownMenu = null; + permissions = {}; + dropdownMenu; + menuItems = []; constructor(element, dropdownToggleSelector) { this.element = element; this.dropdownToggle = this.element.querySelector(dropdownToggleSelector); - this.dropdownMenu = (0, Builder_1.create)(this.getDropdownOptions()); - this.dropdownToggle.parentElement.appendChild(this.dropdownMenu); + this.dropdownMenu = (0, Builder_1.create)([]); + (0, Builder_1.attach)(this.dropdownMenu, this.dropdownToggle); + inlineEditors.set(this.element, this); } - getPermission(permission) { - if (!Object.prototype.hasOwnProperty.call(this.element.dataset, permission)) { + /** + * Gets the state of a property from the element's dataset as a boolean. + */ + getState(propertyName) { + if (!Object.prototype.hasOwnProperty.call(this.element.dataset, propertyName)) { return false; } - return (0, Core_1.stringToBool)(this.element.dataset[permission]); + return (0, Core_1.stringToBool)(this.element.dataset[propertyName]); + } + /** + * Updates the state of the element's dataset with the provided data. + */ + updateState(data) { + Object.entries(data).forEach(([key, value]) => { + this.element.dataset[key] = value ? "1" : "0"; + }); + this.rebuildDropdownMenu(); + } + /** + * Sets the permissions for the inline editor. + */ + setPermissions(permissions) { + this.permissions = permissions; + this.rebuildDropdownMenu(); + } + /** + * Gets the permissions for the inline editor. + */ + getPermissions() { + return this.permissions; + } + /** + * Adds a menu item to the dropdown menu and rebuilds the menu. + */ + addMenuItem(menuItem) { + this.menuItems.push(menuItem); + this.rebuildDropdownMenu(); + } + /** + * Adds multiple menu items to the dropdown menu and rebuilds the menu. + */ + addMenuItems(menuItems) { + this.menuItems.push(...menuItems); + this.rebuildDropdownMenu(); + } + /** + * Rebuilds the dropdown menu based on the current menu items and their visibility. + */ + rebuildDropdownMenu() { + const dropdownMenuItems = this.menuItems + .filter((item) => { + return item.visible === undefined || item.visible(); + }) + .map((item) => item.item); + if (dropdownMenuItems.length === 0) { + this.dropdownMenu.innerHTML = ""; + } + else { + (0, Builder_1.setItems)(this.dropdownMenu, dropdownMenuItems); + } } } exports.InlineEditor = InlineEditor; + /** + * Gets the inline editor instance associated with the given element. + */ + function getInlineEditor(element) { + return inlineEditors.get(element); + } }); From 552f6a6dd9161e248092d4b8915ef91a68c40bec Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Mon, 27 Jan 2025 14:20:16 +0100 Subject: [PATCH 03/12] Show notification after changing states of an element --- .../Core/Component/InlineEditor.ts | 41 ++++++++++++++----- .../Core/Component/InlineEditor.js | 31 ++++++++++---- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/InlineEditor.ts b/ts/WoltLabSuite/Core/Component/InlineEditor.ts index 40df5d105fb..679cce48f14 100644 --- a/ts/WoltLabSuite/Core/Component/InlineEditor.ts +++ b/ts/WoltLabSuite/Core/Component/InlineEditor.ts @@ -10,10 +10,11 @@ import { DropdownBuilderItemData, create as createDropdownMenu, - attach as attachDropdownMenu, setItems as setDropdownItems, } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; +import { show as showNotification } from "WoltLabSuite/Core/Ui/Notification"; import { stringToBool } from "WoltLabSuite/Core/Core"; +import UiDropdownSimple from "WoltLabSuite/Core/Ui/Dropdown/Simple"; export interface DropdownMenuItem { visible?: () => boolean; @@ -33,7 +34,20 @@ export class InlineEditor { this.element = element; this.dropdownToggle = this.element.querySelector(dropdownToggleSelector) as HTMLElement; this.dropdownMenu = createDropdownMenu([]); - attachDropdownMenu(this.dropdownMenu, this.dropdownToggle); + + // @see WoltLabSuite/Core/Ui/Dropdown/Builder::attach() + UiDropdownSimple.initFragment(this.dropdownToggle, this.dropdownMenu); + + this.dropdownToggle.addEventListener("click", (event) => { + event.preventDefault(); + event.stopPropagation(); + + // Rebuild the menu to ensure the menu items are displayed correctly, + // as states may change externally and cannot be detected automatically + this.#rebuildDropdownMenu(); + + UiDropdownSimple.toggleDropdown(this.dropdownToggle.id); + }); inlineEditors.set(this.element, this); } @@ -53,11 +67,24 @@ export class InlineEditor { * Updates the state of the element's dataset with the provided data. */ public updateState(data: Record): void { + showNotification(); + Object.entries(data).forEach(([key, value]) => { this.element.dataset[key] = value ? "1" : "0"; }); + } + + /** + * Parses the database response and converts it to a record of boolean values. + */ + protected parseDboResponse(data: Record): Record { + const result: Record = {}; + + Object.entries(data).forEach(([key, value]) => { + result[key] = stringToBool(value.toString()); + }); - this.rebuildDropdownMenu(); + return result; } /** @@ -65,8 +92,6 @@ export class InlineEditor { */ public setPermissions(permissions: Record): void { this.permissions = permissions; - - this.rebuildDropdownMenu(); } /** @@ -81,8 +106,6 @@ export class InlineEditor { */ public addMenuItem(menuItem: DropdownMenuItem): void { this.menuItems.push(menuItem); - - this.rebuildDropdownMenu(); } /** @@ -90,14 +113,12 @@ export class InlineEditor { */ public addMenuItems(menuItems: DropdownMenuItem[]): void { this.menuItems.push(...menuItems); - - this.rebuildDropdownMenu(); } /** * Rebuilds the dropdown menu based on the current menu items and their visibility. */ - public rebuildDropdownMenu(): void { + #rebuildDropdownMenu(): void { const dropdownMenuItems = this.menuItems .filter((item) => { return item.visible === undefined || item.visible(); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js index d995559012b..274efbc17dd 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js @@ -6,11 +6,12 @@ * @license GNU Lesser General Public License * @since 6.2 */ -define(["require", "exports", "WoltLabSuite/Core/Ui/Dropdown/Builder", "WoltLabSuite/Core/Core"], function (require, exports, Builder_1, Core_1) { +define(["require", "exports", "tslib", "WoltLabSuite/Core/Ui/Dropdown/Builder", "WoltLabSuite/Core/Ui/Notification", "WoltLabSuite/Core/Core", "WoltLabSuite/Core/Ui/Dropdown/Simple"], function (require, exports, tslib_1, Builder_1, Notification_1, Core_1, Simple_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InlineEditor = void 0; exports.getInlineEditor = getInlineEditor; + Simple_1 = tslib_1.__importDefault(Simple_1); const inlineEditors = new Map(); class InlineEditor { element; @@ -22,7 +23,16 @@ define(["require", "exports", "WoltLabSuite/Core/Ui/Dropdown/Builder", "WoltLabS this.element = element; this.dropdownToggle = this.element.querySelector(dropdownToggleSelector); this.dropdownMenu = (0, Builder_1.create)([]); - (0, Builder_1.attach)(this.dropdownMenu, this.dropdownToggle); + // @see WoltLabSuite/Core/Ui/Dropdown/Builder::attach() + Simple_1.default.initFragment(this.dropdownToggle, this.dropdownMenu); + this.dropdownToggle.addEventListener("click", (event) => { + event.preventDefault(); + event.stopPropagation(); + // Rebuild the menu to ensure the menu items are displayed correctly, + // as states may change externally and cannot be detected automatically + this.#rebuildDropdownMenu(); + Simple_1.default.toggleDropdown(this.dropdownToggle.id); + }); inlineEditors.set(this.element, this); } /** @@ -38,17 +48,26 @@ define(["require", "exports", "WoltLabSuite/Core/Ui/Dropdown/Builder", "WoltLabS * Updates the state of the element's dataset with the provided data. */ updateState(data) { + (0, Notification_1.show)(); Object.entries(data).forEach(([key, value]) => { this.element.dataset[key] = value ? "1" : "0"; }); - this.rebuildDropdownMenu(); + } + /** + * Parses the database response and converts it to a record of boolean values. + */ + parseDboResponse(data) { + const result = {}; + Object.entries(data).forEach(([key, value]) => { + result[key] = (0, Core_1.stringToBool)(value.toString()); + }); + return result; } /** * Sets the permissions for the inline editor. */ setPermissions(permissions) { this.permissions = permissions; - this.rebuildDropdownMenu(); } /** * Gets the permissions for the inline editor. @@ -61,19 +80,17 @@ define(["require", "exports", "WoltLabSuite/Core/Ui/Dropdown/Builder", "WoltLabS */ addMenuItem(menuItem) { this.menuItems.push(menuItem); - this.rebuildDropdownMenu(); } /** * Adds multiple menu items to the dropdown menu and rebuilds the menu. */ addMenuItems(menuItems) { this.menuItems.push(...menuItems); - this.rebuildDropdownMenu(); } /** * Rebuilds the dropdown menu based on the current menu items and their visibility. */ - rebuildDropdownMenu() { + #rebuildDropdownMenu() { const dropdownMenuItems = this.menuItems .filter((item) => { return item.visible === undefined || item.visible(); From e7f9f48b24989bf28442f09ce96a89faad0004f8 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Mon, 27 Jan 2025 14:37:26 +0100 Subject: [PATCH 04/12] Remove `parseDboResponse`. RPC API should be used --- ts/WoltLabSuite/Core/Component/InlineEditor.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/InlineEditor.ts b/ts/WoltLabSuite/Core/Component/InlineEditor.ts index 679cce48f14..ece25581997 100644 --- a/ts/WoltLabSuite/Core/Component/InlineEditor.ts +++ b/ts/WoltLabSuite/Core/Component/InlineEditor.ts @@ -74,19 +74,6 @@ export class InlineEditor { }); } - /** - * Parses the database response and converts it to a record of boolean values. - */ - protected parseDboResponse(data: Record): Record { - const result: Record = {}; - - Object.entries(data).forEach(([key, value]) => { - result[key] = stringToBool(value.toString()); - }); - - return result; - } - /** * Sets the permissions for the inline editor. */ From 7ef715cf6351d72bda27519638edef29e729c6f9 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Mon, 27 Jan 2025 15:13:34 +0100 Subject: [PATCH 05/12] Run `tsc` --- .../js/WoltLabSuite/Core/Component/InlineEditor.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js index 274efbc17dd..bc42b114942 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js @@ -53,16 +53,6 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Ui/Dropdown/Builder", this.element.dataset[key] = value ? "1" : "0"; }); } - /** - * Parses the database response and converts it to a record of boolean values. - */ - parseDboResponse(data) { - const result = {}; - Object.entries(data).forEach(([key, value]) => { - result[key] = (0, Core_1.stringToBool)(value.toString()); - }); - return result; - } /** * Sets the permissions for the inline editor. */ From ed0e9f649e7a0bdc3ae651057d27374a2d7c8eaf Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Tue, 28 Jan 2025 09:51:25 +0100 Subject: [PATCH 06/12] Rename function `visible` to `isVisible` --- ts/WoltLabSuite/Core/Component/InlineEditor.ts | 4 ++-- .../files/js/WoltLabSuite/Core/Component/InlineEditor.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/InlineEditor.ts b/ts/WoltLabSuite/Core/Component/InlineEditor.ts index ece25581997..6395eb0e4ec 100644 --- a/ts/WoltLabSuite/Core/Component/InlineEditor.ts +++ b/ts/WoltLabSuite/Core/Component/InlineEditor.ts @@ -17,7 +17,7 @@ import { stringToBool } from "WoltLabSuite/Core/Core"; import UiDropdownSimple from "WoltLabSuite/Core/Ui/Dropdown/Simple"; export interface DropdownMenuItem { - visible?: () => boolean; + isVisible?: () => boolean; item: DropdownBuilderItemData; } @@ -108,7 +108,7 @@ export class InlineEditor { #rebuildDropdownMenu(): void { const dropdownMenuItems = this.menuItems .filter((item) => { - return item.visible === undefined || item.visible(); + return item.isVisible === undefined || item.isVisible(); }) .map((item) => item.item); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js index bc42b114942..20e2953cf52 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js @@ -83,7 +83,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Ui/Dropdown/Builder", #rebuildDropdownMenu() { const dropdownMenuItems = this.menuItems .filter((item) => { - return item.visible === undefined || item.visible(); + return item.isVisible === undefined || item.isVisible(); }) .map((item) => item.item); if (dropdownMenuItems.length === 0) { From 2e94af4d4e5e351a471456f7210a0f2736cd8a58 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Tue, 28 Jan 2025 11:05:28 +0100 Subject: [PATCH 07/12] Use Actions to control the individual functions in the InlineEditor --- .../Core/Component/Inline/Actions/Disable.ts | 56 +++++++++++++++++++ .../Core/Component/Inline/Actions/Enable.ts | 38 +++++++++++++ .../{InlineEditor.ts => Inline/Editor.ts} | 45 +++++++-------- .../Core/Component/Inline/Actions/Disable.js | 50 +++++++++++++++++ .../Core/Component/Inline/Actions/Enable.js | 35 ++++++++++++ .../{InlineEditor.js => Inline/Editor.js} | 40 ++++++------- 6 files changed, 222 insertions(+), 42 deletions(-) create mode 100644 ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts create mode 100644 ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts rename ts/WoltLabSuite/Core/Component/{InlineEditor.ts => Inline/Editor.ts} (68%) create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js rename wcfsetup/install/files/js/WoltLabSuite/Core/Component/{InlineEditor.js => Inline/Editor.js} (73%) diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts new file mode 100644 index 00000000000..2b2e5cfbef9 --- /dev/null +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts @@ -0,0 +1,56 @@ +/** + * Inline editor action to disable an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ + +import { Action, InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor"; +import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; +import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; +import { apiResultFromError, apiResultFromValue, ApiResult } from "WoltLabSuite/Core/Api/Result"; + +export class Disable implements Action { + protected readonly inlineEditor: InlineEditor; + protected readonly endpoint: string | URL; + + constructor(inlineEditor: InlineEditor, endpoint: string | URL) { + this.inlineEditor = inlineEditor; + this.endpoint = endpoint; + } + + get item(): DropdownBuilderItemData { + return { + label: "wcf.global.button.disable", + callback: async () => { + const response = await disable(this.endpoint); + + if (response.ok) { + this.inlineEditor.updateState({ + isDisabled: true, + }); + } + }, + }; + } + + isVisible(): boolean { + return ( + this.inlineEditor.getPermissions()["canEnable"] && + !this.inlineEditor.getState("isDeleted") && + !this.inlineEditor.getState("isDisabled") + ); + } +} + +export async function disable(endpoint: string | URL): Promise> { + try { + await prepareRequest(endpoint).post().fetchAsJson(); + } catch (e) { + return apiResultFromError(e); + } + + return apiResultFromValue([]); +} diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts new file mode 100644 index 00000000000..bf0b3b767d2 --- /dev/null +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts @@ -0,0 +1,38 @@ +/** + * Inline editor action to enable an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ + +import { Action, InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor"; +import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; + +export class Enable implements Action { + protected readonly inlineEditor: InlineEditor; + protected readonly endpoint: string | URL; + + constructor(inlineEditor: InlineEditor, endpoint: string | URL) { + this.inlineEditor = inlineEditor; + this.endpoint = endpoint; + } + + get item(): DropdownBuilderItemData { + return { + label: "wcf.global.button.enable", + callback: async () => { + // TODO + }, + }; + } + + isVisible(): boolean { + return ( + this.inlineEditor.getPermissions()["canEnable"] && + !this.inlineEditor.getState("isDeleted") && + this.inlineEditor.getState("isDisabled") + ); + } +} diff --git a/ts/WoltLabSuite/Core/Component/InlineEditor.ts b/ts/WoltLabSuite/Core/Component/Inline/Editor.ts similarity index 68% rename from ts/WoltLabSuite/Core/Component/InlineEditor.ts rename to ts/WoltLabSuite/Core/Component/Inline/Editor.ts index 6395eb0e4ec..5780c134411 100644 --- a/ts/WoltLabSuite/Core/Component/InlineEditor.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Editor.ts @@ -16,29 +16,30 @@ import { show as showNotification } from "WoltLabSuite/Core/Ui/Notification"; import { stringToBool } from "WoltLabSuite/Core/Core"; import UiDropdownSimple from "WoltLabSuite/Core/Ui/Dropdown/Simple"; -export interface DropdownMenuItem { +export interface Action { isVisible?: () => boolean; - item: DropdownBuilderItemData; + + get item(): DropdownBuilderItemData; } const inlineEditors = new Map(); export class InlineEditor { protected readonly element: HTMLElement; - protected readonly dropdownToggle: HTMLElement; + readonly #dropdownToggle: HTMLElement; protected permissions: Record = {}; - protected readonly dropdownMenu: HTMLUListElement; - protected readonly menuItems: DropdownMenuItem[] = []; + readonly #dropdownMenu: HTMLUListElement; + readonly #actions: Action[] = []; public constructor(element: HTMLElement, dropdownToggleSelector: string) { this.element = element; - this.dropdownToggle = this.element.querySelector(dropdownToggleSelector) as HTMLElement; - this.dropdownMenu = createDropdownMenu([]); + this.#dropdownToggle = this.element.querySelector(dropdownToggleSelector) as HTMLElement; + this.#dropdownMenu = createDropdownMenu([]); // @see WoltLabSuite/Core/Ui/Dropdown/Builder::attach() - UiDropdownSimple.initFragment(this.dropdownToggle, this.dropdownMenu); + UiDropdownSimple.initFragment(this.#dropdownToggle, this.#dropdownMenu); - this.dropdownToggle.addEventListener("click", (event) => { + this.#dropdownToggle.addEventListener("click", (event) => { event.preventDefault(); event.stopPropagation(); @@ -46,7 +47,7 @@ export class InlineEditor { // as states may change externally and cannot be detected automatically this.#rebuildDropdownMenu(); - UiDropdownSimple.toggleDropdown(this.dropdownToggle.id); + UiDropdownSimple.toggleDropdown(this.#dropdownToggle.id); }); inlineEditors.set(this.element, this); @@ -75,10 +76,10 @@ export class InlineEditor { } /** - * Sets the permissions for the inline editor. + * Merge the current permissions with the provided permissions. */ - public setPermissions(permissions: Record): void { - this.permissions = permissions; + public addPermissions(permissions: Record): void { + this.permissions = { ...this.permissions, ...permissions }; } /** @@ -89,33 +90,33 @@ export class InlineEditor { } /** - * Adds a menu item to the dropdown menu and rebuilds the menu. + * Adds an action to the inline editor. */ - public addMenuItem(menuItem: DropdownMenuItem): void { - this.menuItems.push(menuItem); + public addAction(action: Action): void { + this.#actions.push(action); } /** - * Adds multiple menu items to the dropdown menu and rebuilds the menu. + * Adds multiple actions to the inline editor. */ - public addMenuItems(menuItems: DropdownMenuItem[]): void { - this.menuItems.push(...menuItems); + public addActions(actions: Action[]): void { + this.#actions.push(...actions); } /** * Rebuilds the dropdown menu based on the current menu items and their visibility. */ #rebuildDropdownMenu(): void { - const dropdownMenuItems = this.menuItems + const dropdownMenuItems = this.#actions .filter((item) => { return item.isVisible === undefined || item.isVisible(); }) .map((item) => item.item); if (dropdownMenuItems.length === 0) { - this.dropdownMenu.innerHTML = ""; + this.#dropdownMenu.innerHTML = ""; } else { - setDropdownItems(this.dropdownMenu, dropdownMenuItems); + setDropdownItems(this.#dropdownMenu, dropdownMenuItems); } } } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js new file mode 100644 index 00000000000..5d2a5ae8860 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js @@ -0,0 +1,50 @@ +/** + * Inline editor action to disable an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ +define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Api/Result"], function (require, exports, Backend_1, Result_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Disable = void 0; + exports.disable = disable; + class Disable { + inlineEditor; + endpoint; + constructor(inlineEditor, endpoint) { + this.inlineEditor = inlineEditor; + this.endpoint = endpoint; + } + get item() { + return { + label: "wcf.global.button.disable", + callback: async () => { + const response = await disable(this.endpoint); + if (response.ok) { + this.inlineEditor.updateState({ + isDisabled: true, + }); + } + }, + }; + } + isVisible() { + return (this.inlineEditor.getPermissions()["canEnable"] && + !this.inlineEditor.getState("isDeleted") && + !this.inlineEditor.getState("isDisabled")); + } + } + exports.Disable = Disable; + async function disable(endpoint) { + try { + await (0, Backend_1.prepareRequest)(endpoint).post().fetchAsJson(); + } + catch (e) { + return (0, Result_1.apiResultFromError)(e); + } + return (0, Result_1.apiResultFromValue)([]); + } +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js new file mode 100644 index 00000000000..9fe3bf981b9 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js @@ -0,0 +1,35 @@ +/** + * Inline editor action to enable an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ +define(["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Enable = void 0; + class Enable { + inlineEditor; + endpoint; + constructor(inlineEditor, endpoint) { + this.inlineEditor = inlineEditor; + this.endpoint = endpoint; + } + get item() { + return { + label: "wcf.global.button.enable", + callback: async () => { + // TODO + }, + }; + } + isVisible() { + return (this.inlineEditor.getPermissions()["canEnable"] && + !this.inlineEditor.getState("isDeleted") && + this.inlineEditor.getState("isDisabled")); + } + } + exports.Enable = Enable; +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js similarity index 73% rename from wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js rename to wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js index 20e2953cf52..3070bee78c4 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/InlineEditor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js @@ -15,23 +15,23 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Ui/Dropdown/Builder", const inlineEditors = new Map(); class InlineEditor { element; - dropdownToggle; + #dropdownToggle; permissions = {}; - dropdownMenu; - menuItems = []; + #dropdownMenu; + #actions = []; constructor(element, dropdownToggleSelector) { this.element = element; - this.dropdownToggle = this.element.querySelector(dropdownToggleSelector); - this.dropdownMenu = (0, Builder_1.create)([]); + this.#dropdownToggle = this.element.querySelector(dropdownToggleSelector); + this.#dropdownMenu = (0, Builder_1.create)([]); // @see WoltLabSuite/Core/Ui/Dropdown/Builder::attach() - Simple_1.default.initFragment(this.dropdownToggle, this.dropdownMenu); - this.dropdownToggle.addEventListener("click", (event) => { + Simple_1.default.initFragment(this.#dropdownToggle, this.#dropdownMenu); + this.#dropdownToggle.addEventListener("click", (event) => { event.preventDefault(); event.stopPropagation(); // Rebuild the menu to ensure the menu items are displayed correctly, // as states may change externally and cannot be detected automatically this.#rebuildDropdownMenu(); - Simple_1.default.toggleDropdown(this.dropdownToggle.id); + Simple_1.default.toggleDropdown(this.#dropdownToggle.id); }); inlineEditors.set(this.element, this); } @@ -54,10 +54,10 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Ui/Dropdown/Builder", }); } /** - * Sets the permissions for the inline editor. + * Merge the current permissions with the provided permissions. */ - setPermissions(permissions) { - this.permissions = permissions; + addPermissions(permissions) { + this.permissions = { ...this.permissions, ...permissions }; } /** * Gets the permissions for the inline editor. @@ -66,31 +66,31 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Ui/Dropdown/Builder", return this.permissions; } /** - * Adds a menu item to the dropdown menu and rebuilds the menu. + * Adds an action to the inline editor. */ - addMenuItem(menuItem) { - this.menuItems.push(menuItem); + addAction(action) { + this.#actions.push(action); } /** - * Adds multiple menu items to the dropdown menu and rebuilds the menu. + * Adds multiple actions to the inline editor. */ - addMenuItems(menuItems) { - this.menuItems.push(...menuItems); + addActions(actions) { + this.#actions.push(...actions); } /** * Rebuilds the dropdown menu based on the current menu items and their visibility. */ #rebuildDropdownMenu() { - const dropdownMenuItems = this.menuItems + const dropdownMenuItems = this.#actions .filter((item) => { return item.isVisible === undefined || item.isVisible(); }) .map((item) => item.item); if (dropdownMenuItems.length === 0) { - this.dropdownMenu.innerHTML = ""; + this.#dropdownMenu.innerHTML = ""; } else { - (0, Builder_1.setItems)(this.dropdownMenu, dropdownMenuItems); + (0, Builder_1.setItems)(this.#dropdownMenu, dropdownMenuItems); } } } From c4d11ce54c292f51e497f4186352646a0faf8a12 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Tue, 28 Jan 2025 12:06:20 +0100 Subject: [PATCH 08/12] Don't export RPC request function. --- .../Core/Component/Inline/Actions/Disable.ts | 2 +- .../Core/Component/Inline/Actions/Enable.ts | 35 +++++++++++++++++-- .../Core/Component/Inline/Actions/Disable.js | 1 - .../Core/Component/Inline/Actions/Enable.js | 33 +++++++++++++++-- 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts index 2b2e5cfbef9..79d66d88e20 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts @@ -45,7 +45,7 @@ export class Disable implements Action { } } -export async function disable(endpoint: string | URL): Promise> { +async function disable(endpoint: string | URL): Promise> { try { await prepareRequest(endpoint).post().fetchAsJson(); } catch (e) { diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts index bf0b3b767d2..81bf61c456e 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts @@ -9,21 +9,42 @@ import { Action, InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor"; import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; +import { ApiResult, apiResultFromError, apiResultFromValue } from "WoltLabSuite/Core/Api/Result"; +import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; +import { dialogFactory } from "WoltLabSuite/Core/Component/Dialog"; export class Enable implements Action { protected readonly inlineEditor: InlineEditor; protected readonly endpoint: string | URL; + protected readonly useFormBuilder: boolean; - constructor(inlineEditor: InlineEditor, endpoint: string | URL) { + constructor(inlineEditor: InlineEditor, endpoint: string | URL, useFormBuilder: boolean = false) { this.inlineEditor = inlineEditor; this.endpoint = endpoint; + this.useFormBuilder = useFormBuilder; } get item(): DropdownBuilderItemData { return { label: "wcf.global.button.enable", callback: async () => { - // TODO + if (this.useFormBuilder) { + const response = await dialogFactory() + .usingFormBuilder() + .fromEndpoint<{ isDisabled: boolean }>(this.endpoint.toString()); + + if (response.ok) { + this.inlineEditor.updateState({ + isDisabled: response.result.isDisabled, + }); + } + } else { + if ((await enable(this.endpoint)).ok) { + this.inlineEditor.updateState({ + isDisabled: false, + }); + } + } }, }; } @@ -36,3 +57,13 @@ export class Enable implements Action { ); } } + +async function enable(endpoint: string | URL): Promise> { + try { + await prepareRequest(endpoint).post().fetchAsJson(); + } catch (e) { + return apiResultFromError(e); + } + + return apiResultFromValue([]); +} diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js index 5d2a5ae8860..ddb6771b362 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js @@ -10,7 +10,6 @@ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Co "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Disable = void 0; - exports.disable = disable; class Disable { inlineEditor; endpoint; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js index 9fe3bf981b9..7ca6450b72d 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js @@ -6,22 +6,40 @@ * @license GNU Lesser General Public License * @since 6.2 */ -define(["require", "exports"], function (require, exports) { +define(["require", "exports", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Component/Dialog"], function (require, exports, Result_1, Backend_1, Dialog_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Enable = void 0; class Enable { inlineEditor; endpoint; - constructor(inlineEditor, endpoint) { + useFormBuilder; + constructor(inlineEditor, endpoint, useFormBuilder = false) { this.inlineEditor = inlineEditor; this.endpoint = endpoint; + this.useFormBuilder = useFormBuilder; } get item() { return { label: "wcf.global.button.enable", callback: async () => { - // TODO + if (this.useFormBuilder) { + const response = await (0, Dialog_1.dialogFactory)() + .usingFormBuilder() + .fromEndpoint(this.endpoint.toString()); + if (response.ok) { + this.inlineEditor.updateState({ + isDisabled: response.result.isDisabled, + }); + } + } + else { + if ((await enable(this.endpoint)).ok) { + this.inlineEditor.updateState({ + isDisabled: false, + }); + } + } }, }; } @@ -32,4 +50,13 @@ define(["require", "exports"], function (require, exports) { } } exports.Enable = Enable; + async function enable(endpoint) { + try { + await (0, Backend_1.prepareRequest)(endpoint).post().fetchAsJson(); + } + catch (e) { + return (0, Result_1.apiResultFromError)(e); + } + return (0, Result_1.apiResultFromValue)([]); + } }); From ef9e8bd0ded62a659a7e7ec17cb96dff1cffc8d4 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Tue, 28 Jan 2025 13:33:18 +0100 Subject: [PATCH 09/12] Implement `Trash` action --- .../Core/Component/Inline/Actions/Disable.ts | 2 +- .../Core/Component/Inline/Actions/Enable.ts | 4 +- .../Core/Component/Inline/Actions/Trash.ts | 69 +++++++++++++++++++ .../Core/Component/Inline/Editor.ts | 8 ++- .../Core/Component/Inline/Actions/Disable.js | 2 +- .../Core/Component/Inline/Actions/Enable.js | 4 +- .../Core/Component/Inline/Editor.js | 6 +- 7 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts index 79d66d88e20..318efb6c282 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts @@ -28,7 +28,7 @@ export class Disable implements Action { const response = await disable(this.endpoint); if (response.ok) { - this.inlineEditor.updateState({ + this.inlineEditor.update({ isDisabled: true, }); } diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts index 81bf61c456e..a7fecf95e39 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts @@ -34,13 +34,13 @@ export class Enable implements Action { .fromEndpoint<{ isDisabled: boolean }>(this.endpoint.toString()); if (response.ok) { - this.inlineEditor.updateState({ + this.inlineEditor.update({ isDisabled: response.result.isDisabled, }); } } else { if ((await enable(this.endpoint)).ok) { - this.inlineEditor.updateState({ + this.inlineEditor.update({ isDisabled: false, }); } diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts new file mode 100644 index 00000000000..270fb6c5b4c --- /dev/null +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts @@ -0,0 +1,69 @@ +/** + * Inline editor action to trash an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ + +import { Action, InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor"; +import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; +import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; +import { apiResultFromError, apiResultFromValue, ApiResult } from "WoltLabSuite/Core/Api/Result"; +import { confirmationFactory } from "WoltLabSuite/Core/Component/Confirmation"; + +export class Trash implements Action { + protected readonly inlineEditor: InlineEditor; + protected readonly endpoint: string | URL; + protected readonly title: string; + + constructor(inlineEditor: InlineEditor, title: string, endpoint: string | URL) { + this.inlineEditor = inlineEditor; + this.endpoint = endpoint; + this.title = title; + } + + get item(): DropdownBuilderItemData { + return { + label: "wcf.global.button.trash", + callback: async () => { + const result = await confirmationFactory().softDelete(this.title, true); + if (!result.result) { + return; + } + + const response = await trash(this.endpoint, result.reason); + if (response.ok) { + this.inlineEditor.update({ + isDeleted: true, + deleteNote: response.value, + }); + } + }, + }; + } + + isVisible(): boolean { + return this.inlineEditor.getPermissions()["canTrash"] && !this.inlineEditor.getState("isDeleted"); + } +} + +type Response = { + deleteNote: string; +}; + +async function trash(endpoint: string | URL, reason: string): Promise> { + let response: Response; + try { + response = (await prepareRequest(endpoint) + .post({ + reason, + }) + .fetchAsJson()) as Response; + } catch (e) { + return apiResultFromError(e); + } + + return apiResultFromValue(response.deleteNote); +} diff --git a/ts/WoltLabSuite/Core/Component/Inline/Editor.ts b/ts/WoltLabSuite/Core/Component/Inline/Editor.ts index 5780c134411..04deebccb53 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Editor.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Editor.ts @@ -22,6 +22,8 @@ export interface Action { get item(): DropdownBuilderItemData; } +export type State = string | boolean | number; + const inlineEditors = new Map(); export class InlineEditor { @@ -65,13 +67,13 @@ export class InlineEditor { } /** - * Updates the state of the element's dataset with the provided data. + * Updates the element's dataset with the provided data. */ - public updateState(data: Record): void { + public update(data: Record): void { showNotification(); Object.entries(data).forEach(([key, value]) => { - this.element.dataset[key] = value ? "1" : "0"; + this.element.dataset[key] = value.toString(); }); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js index ddb6771b362..08da415f61b 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js @@ -23,7 +23,7 @@ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Co callback: async () => { const response = await disable(this.endpoint); if (response.ok) { - this.inlineEditor.updateState({ + this.inlineEditor.update({ isDisabled: true, }); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js index 7ca6450b72d..4d48e56b066 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js @@ -28,14 +28,14 @@ define(["require", "exports", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core .usingFormBuilder() .fromEndpoint(this.endpoint.toString()); if (response.ok) { - this.inlineEditor.updateState({ + this.inlineEditor.update({ isDisabled: response.result.isDisabled, }); } } else { if ((await enable(this.endpoint)).ok) { - this.inlineEditor.updateState({ + this.inlineEditor.update({ isDisabled: false, }); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js index 3070bee78c4..1c22f4bc791 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js @@ -45,12 +45,12 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Ui/Dropdown/Builder", return (0, Core_1.stringToBool)(this.element.dataset[propertyName]); } /** - * Updates the state of the element's dataset with the provided data. + * Updates the element's dataset with the provided data. */ - updateState(data) { + update(data) { (0, Notification_1.show)(); Object.entries(data).forEach(([key, value]) => { - this.element.dataset[key] = value ? "1" : "0"; + this.element.dataset[key] = value.toString(); }); } /** From 9cfcb7f385fe80096aa826c8d238e089c64abd14 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Tue, 28 Jan 2025 13:33:37 +0100 Subject: [PATCH 10/12] Run `tsc` --- .../Core/Component/Inline/Actions/Trash.js | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js new file mode 100644 index 00000000000..acbcc3273ca --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js @@ -0,0 +1,59 @@ +/** + * Inline editor action to trash an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ +define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Component/Confirmation"], function (require, exports, Backend_1, Result_1, Confirmation_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Trash = void 0; + class Trash { + inlineEditor; + endpoint; + title; + constructor(inlineEditor, title, endpoint) { + this.inlineEditor = inlineEditor; + this.endpoint = endpoint; + this.title = title; + } + get item() { + return { + label: "wcf.global.button.trash", + callback: async () => { + const result = await (0, Confirmation_1.confirmationFactory)().softDelete(this.title, true); + if (!result.result) { + return; + } + const response = await trash(this.endpoint, result.reason); + if (response.ok) { + this.inlineEditor.update({ + isDeleted: true, + deleteNote: response.value, + }); + } + }, + }; + } + isVisible() { + return this.inlineEditor.getPermissions()["canTrash"] && !this.inlineEditor.getState("isDeleted"); + } + } + exports.Trash = Trash; + async function trash(endpoint, reason) { + let response; + try { + response = (await (0, Backend_1.prepareRequest)(endpoint) + .post({ + reason, + }) + .fetchAsJson()); + } + catch (e) { + return (0, Result_1.apiResultFromError)(e); + } + return (0, Result_1.apiResultFromValue)(response.deleteNote); + } +}); From da9e8377651e6f977a493e42c09ec97fb204f690 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Tue, 28 Jan 2025 13:35:20 +0100 Subject: [PATCH 11/12] Use `getPhrase` --- ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts | 3 ++- ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts | 3 ++- ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts | 3 ++- .../js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js | 4 ++-- .../js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js | 4 ++-- .../js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js | 4 ++-- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts index 318efb6c282..54ee9732e90 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts @@ -11,6 +11,7 @@ import { Action, InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor" import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; import { apiResultFromError, apiResultFromValue, ApiResult } from "WoltLabSuite/Core/Api/Result"; +import { getPhrase } from "WoltLabSuite/Core/Language"; export class Disable implements Action { protected readonly inlineEditor: InlineEditor; @@ -23,7 +24,7 @@ export class Disable implements Action { get item(): DropdownBuilderItemData { return { - label: "wcf.global.button.disable", + label: getPhrase("wcf.global.button.disable"), callback: async () => { const response = await disable(this.endpoint); diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts index a7fecf95e39..e1707e78615 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts @@ -12,6 +12,7 @@ import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; import { ApiResult, apiResultFromError, apiResultFromValue } from "WoltLabSuite/Core/Api/Result"; import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; import { dialogFactory } from "WoltLabSuite/Core/Component/Dialog"; +import { getPhrase } from "WoltLabSuite/Core/Language"; export class Enable implements Action { protected readonly inlineEditor: InlineEditor; @@ -26,7 +27,7 @@ export class Enable implements Action { get item(): DropdownBuilderItemData { return { - label: "wcf.global.button.enable", + label: getPhrase("wcf.global.button.enable"), callback: async () => { if (this.useFormBuilder) { const response = await dialogFactory() diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts index 270fb6c5b4c..fd2c985e727 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts @@ -12,6 +12,7 @@ import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; import { apiResultFromError, apiResultFromValue, ApiResult } from "WoltLabSuite/Core/Api/Result"; import { confirmationFactory } from "WoltLabSuite/Core/Component/Confirmation"; +import { getPhrase } from "WoltLabSuite/Core/Language"; export class Trash implements Action { protected readonly inlineEditor: InlineEditor; @@ -26,7 +27,7 @@ export class Trash implements Action { get item(): DropdownBuilderItemData { return { - label: "wcf.global.button.trash", + label: getPhrase("wcf.global.button.trash"), callback: async () => { const result = await confirmationFactory().softDelete(this.title, true); if (!result.result) { diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js index 08da415f61b..1379b14dc90 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js @@ -6,7 +6,7 @@ * @license GNU Lesser General Public License * @since 6.2 */ -define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Api/Result"], function (require, exports, Backend_1, Result_1) { +define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Language"], function (require, exports, Backend_1, Result_1, Language_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Disable = void 0; @@ -19,7 +19,7 @@ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Co } get item() { return { - label: "wcf.global.button.disable", + label: (0, Language_1.getPhrase)("wcf.global.button.disable"), callback: async () => { const response = await disable(this.endpoint); if (response.ok) { diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js index 4d48e56b066..c6762351e84 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js @@ -6,7 +6,7 @@ * @license GNU Lesser General Public License * @since 6.2 */ -define(["require", "exports", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Component/Dialog"], function (require, exports, Result_1, Backend_1, Dialog_1) { +define(["require", "exports", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Component/Dialog", "WoltLabSuite/Core/Language"], function (require, exports, Result_1, Backend_1, Dialog_1, Language_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Enable = void 0; @@ -21,7 +21,7 @@ define(["require", "exports", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core } get item() { return { - label: "wcf.global.button.enable", + label: (0, Language_1.getPhrase)("wcf.global.button.enable"), callback: async () => { if (this.useFormBuilder) { const response = await (0, Dialog_1.dialogFactory)() diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js index acbcc3273ca..5fd6a5602ae 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js @@ -6,7 +6,7 @@ * @license GNU Lesser General Public License * @since 6.2 */ -define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Component/Confirmation"], function (require, exports, Backend_1, Result_1, Confirmation_1) { +define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Component/Confirmation", "WoltLabSuite/Core/Language"], function (require, exports, Backend_1, Result_1, Confirmation_1, Language_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Trash = void 0; @@ -21,7 +21,7 @@ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Co } get item() { return { - label: "wcf.global.button.trash", + label: (0, Language_1.getPhrase)("wcf.global.button.trash"), callback: async () => { const result = await (0, Confirmation_1.confirmationFactory)().softDelete(this.title, true); if (!result.result) { From 85cee102467c6dab32583a3ac0e72b542654a949 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Tue, 28 Jan 2025 14:07:43 +0100 Subject: [PATCH 12/12] Add restore action --- .../Core/Component/Inline/Actions/Disable.ts | 42 +++------------- .../Core/Component/Inline/Actions/Enable.ts | 4 +- .../Core/Component/Inline/Actions/Restore.ts | 27 ++++++++++ .../Core/Component/Inline/Actions/Simple.ts | 50 +++++++++++++++++++ .../Core/Component/Inline/Actions/Trash.ts | 2 +- .../Core/Component/Inline/Editor.ts | 2 +- .../Core/Component/Inline/Actions/Disable.js | 34 +++---------- .../Core/Component/Inline/Actions/Enable.js | 4 +- .../Core/Component/Inline/Actions/Restore.js | 27 ++++++++++ .../Core/Component/Inline/Actions/Simple.js | 44 ++++++++++++++++ .../Core/Component/Inline/Actions/Trash.js | 2 +- .../Core/Component/Inline/Editor.js | 2 +- ...PreloadPhrasesCollectingListener.class.php | 2 + 13 files changed, 173 insertions(+), 69 deletions(-) create mode 100644 ts/WoltLabSuite/Core/Component/Inline/Actions/Restore.ts create mode 100644 ts/WoltLabSuite/Core/Component/Inline/Actions/Simple.ts create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Restore.js create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Simple.js diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts index 54ee9732e90..2745b02da98 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Disable.ts @@ -7,34 +7,18 @@ * @since 6.2 */ -import { Action, InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor"; -import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; -import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; -import { apiResultFromError, apiResultFromValue, ApiResult } from "WoltLabSuite/Core/Api/Result"; -import { getPhrase } from "WoltLabSuite/Core/Language"; - -export class Disable implements Action { - protected readonly inlineEditor: InlineEditor; - protected readonly endpoint: string | URL; +import { InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor"; +import { Simple } from "WoltLabSuite/Core/Component/Inline/Actions/Simple"; +export class Disable extends Simple { constructor(inlineEditor: InlineEditor, endpoint: string | URL) { - this.inlineEditor = inlineEditor; - this.endpoint = endpoint; + super(inlineEditor, "wcf.global.button.disable", endpoint); } - get item(): DropdownBuilderItemData { - return { - label: getPhrase("wcf.global.button.disable"), - callback: async () => { - const response = await disable(this.endpoint); - - if (response.ok) { - this.inlineEditor.update({ - isDisabled: true, - }); - } - }, - }; + responseOk(): void { + this.inlineEditor.update({ + isDisabled: 1, + }); } isVisible(): boolean { @@ -45,13 +29,3 @@ export class Disable implements Action { ); } } - -async function disable(endpoint: string | URL): Promise> { - try { - await prepareRequest(endpoint).post().fetchAsJson(); - } catch (e) { - return apiResultFromError(e); - } - - return apiResultFromValue([]); -} diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts index e1707e78615..03b5778933a 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Enable.ts @@ -36,13 +36,13 @@ export class Enable implements Action { if (response.ok) { this.inlineEditor.update({ - isDisabled: response.result.isDisabled, + isDisabled: response.result.isDisabled ? 1 : 0, }); } } else { if ((await enable(this.endpoint)).ok) { this.inlineEditor.update({ - isDisabled: false, + isDisabled: 0, }); } } diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Restore.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Restore.ts new file mode 100644 index 00000000000..16b38545851 --- /dev/null +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Restore.ts @@ -0,0 +1,27 @@ +/** + * Inline editor action to restore an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ + +import { InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor"; +import { Simple } from "WoltLabSuite/Core/Component/Inline/Actions/Simple"; + +export class Restore extends Simple { + constructor(inlineEditor: InlineEditor, endpoint: string | URL) { + super(inlineEditor, "wcf.global.button.restore", endpoint); + } + + responseOk(): void { + this.inlineEditor.update({ + isDeleted: 0, + }); + } + + isVisible(): boolean { + return this.inlineEditor.getPermissions()["canRestore"] && this.inlineEditor.getState("isDeleted"); + } +} diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Simple.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Simple.ts new file mode 100644 index 00000000000..00e6d388741 --- /dev/null +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Simple.ts @@ -0,0 +1,50 @@ +/** + * Inline editor action to restore an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ + +import { Action, InlineEditor } from "WoltLabSuite/Core/Component/Inline/Editor"; +import { DropdownBuilderItemData } from "WoltLabSuite/Core/Ui/Dropdown/Builder"; +import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend"; +import { apiResultFromError, apiResultFromValue, ApiResult } from "WoltLabSuite/Core/Api/Result"; +import { getPhrase } from "WoltLabSuite/Core/Language"; + +export abstract class Simple implements Action { + protected readonly inlineEditor: InlineEditor; + protected readonly endpoint: string | URL; + protected readonly label: string; + + protected constructor(inlineEditor: InlineEditor, label: string, endpoint: string | URL) { + this.inlineEditor = inlineEditor; + this.endpoint = endpoint; + this.label = label; + } + + get item(): DropdownBuilderItemData { + return { + label: getPhrase(this.label), + callback: async () => { + const response = await request(this.endpoint); + if (response.ok) { + this.responseOk(); + } + }, + }; + } + + abstract responseOk(): void; +} + +async function request(endpoint: string | URL): Promise> { + try { + await prepareRequest(endpoint).post().fetchAsJson(); + } catch (e) { + return apiResultFromError(e); + } + + return apiResultFromValue([]); +} diff --git a/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts b/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts index fd2c985e727..fc12ff8fbe2 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Actions/Trash.ts @@ -37,7 +37,7 @@ export class Trash implements Action { const response = await trash(this.endpoint, result.reason); if (response.ok) { this.inlineEditor.update({ - isDeleted: true, + isDeleted: 1, deleteNote: response.value, }); } diff --git a/ts/WoltLabSuite/Core/Component/Inline/Editor.ts b/ts/WoltLabSuite/Core/Component/Inline/Editor.ts index 04deebccb53..d857680f8cd 100644 --- a/ts/WoltLabSuite/Core/Component/Inline/Editor.ts +++ b/ts/WoltLabSuite/Core/Component/Inline/Editor.ts @@ -73,7 +73,7 @@ export class InlineEditor { showNotification(); Object.entries(data).forEach(([key, value]) => { - this.element.dataset[key] = value.toString(); + this.element.dataset[key] = typeof value === "boolean" ? (value ? "1" : "0") : value.toString(); }); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js index 1379b14dc90..9f166f52015 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Disable.js @@ -6,29 +6,18 @@ * @license GNU Lesser General Public License * @since 6.2 */ -define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Language"], function (require, exports, Backend_1, Result_1, Language_1) { +define(["require", "exports", "WoltLabSuite/Core/Component/Inline/Actions/Simple"], function (require, exports, Simple_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Disable = void 0; - class Disable { - inlineEditor; - endpoint; + class Disable extends Simple_1.Simple { constructor(inlineEditor, endpoint) { - this.inlineEditor = inlineEditor; - this.endpoint = endpoint; + super(inlineEditor, "wcf.global.button.disable", endpoint); } - get item() { - return { - label: (0, Language_1.getPhrase)("wcf.global.button.disable"), - callback: async () => { - const response = await disable(this.endpoint); - if (response.ok) { - this.inlineEditor.update({ - isDisabled: true, - }); - } - }, - }; + responseOk() { + this.inlineEditor.update({ + isDisabled: 1, + }); } isVisible() { return (this.inlineEditor.getPermissions()["canEnable"] && @@ -37,13 +26,4 @@ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Co } } exports.Disable = Disable; - async function disable(endpoint) { - try { - await (0, Backend_1.prepareRequest)(endpoint).post().fetchAsJson(); - } - catch (e) { - return (0, Result_1.apiResultFromError)(e); - } - return (0, Result_1.apiResultFromValue)([]); - } }); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js index c6762351e84..19675f91a2e 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Enable.js @@ -29,14 +29,14 @@ define(["require", "exports", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core .fromEndpoint(this.endpoint.toString()); if (response.ok) { this.inlineEditor.update({ - isDisabled: response.result.isDisabled, + isDisabled: response.result.isDisabled ? 1 : 0, }); } } else { if ((await enable(this.endpoint)).ok) { this.inlineEditor.update({ - isDisabled: false, + isDisabled: 0, }); } } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Restore.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Restore.js new file mode 100644 index 00000000000..8e2074274d6 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Restore.js @@ -0,0 +1,27 @@ +/** + * Inline editor action to restore an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ +define(["require", "exports", "WoltLabSuite/Core/Component/Inline/Actions/Simple"], function (require, exports, Simple_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Restore = void 0; + class Restore extends Simple_1.Simple { + constructor(inlineEditor, endpoint) { + super(inlineEditor, "wcf.global.button.restore", endpoint); + } + responseOk() { + this.inlineEditor.update({ + isDeleted: 0, + }); + } + isVisible() { + return this.inlineEditor.getPermissions()["canRestore"] && this.inlineEditor.getState("isDeleted"); + } + } + exports.Restore = Restore; +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Simple.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Simple.js new file mode 100644 index 00000000000..3885dbac108 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Simple.js @@ -0,0 +1,44 @@ +/** + * Inline editor action to restore an object. + * + * @author Olaf Braun + * @copyright 2001-2025 WoltLab GmbH + * @license GNU Lesser General Public License + * @since 6.2 + */ +define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Core/Api/Result", "WoltLabSuite/Core/Language"], function (require, exports, Backend_1, Result_1, Language_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.Simple = void 0; + class Simple { + inlineEditor; + endpoint; + label; + constructor(inlineEditor, label, endpoint) { + this.inlineEditor = inlineEditor; + this.endpoint = endpoint; + this.label = label; + } + get item() { + return { + label: (0, Language_1.getPhrase)(this.label), + callback: async () => { + const response = await request(this.endpoint); + if (response.ok) { + this.responseOk(); + } + }, + }; + } + } + exports.Simple = Simple; + async function request(endpoint) { + try { + await (0, Backend_1.prepareRequest)(endpoint).post().fetchAsJson(); + } + catch (e) { + return (0, Result_1.apiResultFromError)(e); + } + return (0, Result_1.apiResultFromValue)([]); + } +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js index 5fd6a5602ae..91d22576abf 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Actions/Trash.js @@ -30,7 +30,7 @@ define(["require", "exports", "WoltLabSuite/Core/Ajax/Backend", "WoltLabSuite/Co const response = await trash(this.endpoint, result.reason); if (response.ok) { this.inlineEditor.update({ - isDeleted: true, + isDeleted: 1, deleteNote: response.value, }); } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js index 1c22f4bc791..9bc8449f0b2 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Component/Inline/Editor.js @@ -50,7 +50,7 @@ define(["require", "exports", "tslib", "WoltLabSuite/Core/Ui/Dropdown/Builder", update(data) { (0, Notification_1.show)(); Object.entries(data).forEach(([key, value]) => { - this.element.dataset[key] = value.toString(); + this.element.dataset[key] = typeof value === "boolean" ? (value ? "1" : "0") : value.toString(); }); } /** diff --git a/wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php b/wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php index 503adcdfd09..64c44596829 100644 --- a/wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php +++ b/wcfsetup/install/files/lib/system/event/listener/PreloadPhrasesCollectingListener.class.php @@ -80,6 +80,8 @@ public function __invoke(PreloadPhrasesCollecting $event): void $event->preload('wcf.global.button.submit'); $event->preload('wcf.global.button.upload'); $event->preload('wcf.global.button.replace'); + $event->preload('wcf.global.button.trash'); + $event->preload('wcf.global.button.restore'); $event->preload('wcf.global.confirmation.cancel'); $event->preload('wcf.global.confirmation.confirm');