From 1d81070976ce89f912848d08f368fada553734b3 Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Sun, 14 Mar 2021 13:26:02 +0100 Subject: [PATCH] Add draft for global `WCF.Action.(Delete|Toggle)` replacement modules --- ts/WoltLabSuite/Core/Bootstrap.ts | 6 ++ ts/WoltLabSuite/Core/Controller/Clipboard.ts | 50 +---------- .../Core/Controller/ClipboardData.ts | 49 ++++++++++ ts/WoltLabSuite/Core/Ui/Object/Action.ts | 90 +++++++++++++++++++ .../Core/Ui/Object/Action/Delete.ts | 19 ++++ .../Core/Ui/Object/Action/Handler.ts | 62 +++++++++++++ .../Core/Ui/Object/Action/Toogle.ts | 36 ++++++++ ts/WoltLabSuite/Core/Ui/Object/Data.ts | 13 +++ .../install/files/acp/templates/adList.tpl | 13 +-- .../install/files/acp/templates/tagList.tpl | 15 +--- .../files/js/WoltLabSuite/Core/Bootstrap.js | 8 +- .../Core/Controller/ClipboardData.js | 4 + .../js/WoltLabSuite/Core/Ui/Object/Action.js | 85 ++++++++++++++++++ .../Core/Ui/Object/Action/Delete.js | 21 +++++ .../Core/Ui/Object/Action/Handler.js | 49 ++++++++++ .../Core/Ui/Object/Action/Toogle.js | 36 ++++++++ .../js/WoltLabSuite/Core/Ui/Object/Data.js | 4 + 17 files changed, 490 insertions(+), 70 deletions(-) create mode 100644 ts/WoltLabSuite/Core/Controller/ClipboardData.ts create mode 100644 ts/WoltLabSuite/Core/Ui/Object/Action.ts create mode 100644 ts/WoltLabSuite/Core/Ui/Object/Action/Delete.ts create mode 100644 ts/WoltLabSuite/Core/Ui/Object/Action/Handler.ts create mode 100644 ts/WoltLabSuite/Core/Ui/Object/Action/Toogle.ts create mode 100644 ts/WoltLabSuite/Core/Ui/Object/Data.ts create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Controller/ClipboardData.js create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action.js create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Delete.js create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Handler.js create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Toogle.js create mode 100644 wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Data.js diff --git a/ts/WoltLabSuite/Core/Bootstrap.ts b/ts/WoltLabSuite/Core/Bootstrap.ts index dd137132f2..27ce26f39e 100644 --- a/ts/WoltLabSuite/Core/Bootstrap.ts +++ b/ts/WoltLabSuite/Core/Bootstrap.ts @@ -27,6 +27,9 @@ import * as UiTooltip from "./Ui/Tooltip"; import * as UiPageJumpTo from "./Ui/Page/JumpTo"; import * as UiPassword from "./Ui/Password"; import * as UiEmpty from "./Ui/Empty"; +import * as UiObjectAction from "./Ui/Object/Action"; +import * as UiObjectActionDelete from "./Ui/Object/Action/Delete"; +import * as UiObjectActionToggle from "./Ui/Object/Action/Toogle"; // perfectScrollbar does not need to be bound anywhere, it just has to be loaded for WCF.js import "perfect-scrollbar"; @@ -92,6 +95,9 @@ export function setup(options: BoostrapOptions): void { UiTooltip.setup(); UiPassword.setup(); UiEmpty.setup(); + UiObjectAction.setup(); + UiObjectActionDelete.setup(); + UiObjectActionToggle.setup(); // Convert forms with `method="get"` into `method="post"` document.querySelectorAll("form[method=get]").forEach((form: HTMLFormElement) => { diff --git a/ts/WoltLabSuite/Core/Controller/Clipboard.ts b/ts/WoltLabSuite/Core/Controller/Clipboard.ts index 6df0aeeb37..123b3f381d 100644 --- a/ts/WoltLabSuite/Core/Controller/Clipboard.ts +++ b/ts/WoltLabSuite/Core/Controller/Clipboard.ts @@ -18,55 +18,7 @@ import * as UiConfirmation from "../Ui/Confirmation"; import UiDropdownSimple from "../Ui/Dropdown/Simple"; import * as UiPageAction from "../Ui/Page/Action"; import * as UiScreen from "../Ui/Screen"; - -interface ClipboardOptions { - hasMarkedItems: boolean; - pageClassName: string; - pageObjectId?: number; -} - -interface ContainerData { - checkboxes: HTMLCollectionOf; - element: HTMLElement; - markAll: HTMLInputElement | null; - markedObjectIds: Set; -} - -interface ItemData { - items: { [key: string]: ClipboardActionData }; - label: string; - reloadPageOnSuccess: string[]; -} - -interface ClipboardActionData { - actionName: string; - internalData: ArbitraryObject; - label: string; - parameters: { - actionName?: string; - className?: string; - objectIDs: number[]; - template: string; - }; - url: string; -} - -interface AjaxResponseMarkedItems { - [key: string]: number[]; -} - -interface AjaxResponse { - actionName: string; - returnValues: { - action: string; - items?: { - // They key is the `typeName` - [key: string]: ItemData; - }; - markedItems?: AjaxResponseMarkedItems; - objectType: string; - }; -} +import { ClipboardOptions, ContainerData, ClipboardActionData, AjaxResponse } from "./ClipboardData"; const _specialCheckboxSelector = '.messageCheckboxLabel > input[type="checkbox"], .message .messageClipboardCheckbox > input[type="checkbox"], .messageGroupList .columnMark > label > input[type="checkbox"]'; diff --git a/ts/WoltLabSuite/Core/Controller/ClipboardData.ts b/ts/WoltLabSuite/Core/Controller/ClipboardData.ts new file mode 100644 index 0000000000..3fa82d5f08 --- /dev/null +++ b/ts/WoltLabSuite/Core/Controller/ClipboardData.ts @@ -0,0 +1,49 @@ +import { DatabaseObjectActionResponse } from "../Ajax/Data"; + +export interface ClipboardOptions { + hasMarkedItems: boolean; + pageClassName: string; + pageObjectId?: number; +} + +export interface ContainerData { + checkboxes: HTMLCollectionOf; + element: HTMLElement; + markAll: HTMLInputElement | null; + markedObjectIds: Set; +} + +export interface ClipboardItemData { + items: { [key: string]: ClipboardActionData }; + label: string; + reloadPageOnSuccess: string[]; +} + +export interface ClipboardActionData { + actionName: string; + internalData: ArbitraryObject; + label: string; + parameters: { + actionName?: string; + className?: string; + objectIDs: number[]; + template: string; + }; + url: string; +} + +export interface AjaxResponseMarkedItems { + [key: string]: number[]; +} + +export interface AjaxResponse extends DatabaseObjectActionResponse { + returnValues: { + action: string; + items?: { + // They key is the `typeName` + [key: string]: ClipboardItemData; + }; + markedItems?: AjaxResponseMarkedItems; + objectType: string; + }; +} diff --git a/ts/WoltLabSuite/Core/Ui/Object/Action.ts b/ts/WoltLabSuite/Core/Ui/Object/Action.ts new file mode 100644 index 0000000000..7932840ff0 --- /dev/null +++ b/ts/WoltLabSuite/Core/Ui/Object/Action.ts @@ -0,0 +1,90 @@ +/** + * Handles actions that can be executed on (database) objects by clicking on specific action buttons. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Object/Action + */ + +import * as Ajax from "../../Ajax"; +import * as EventHandler from "../../Event/Handler"; +import { DatabaseObjectActionResponse, ResponseData } from "../../Ajax/Data"; +import { ObjectActionData } from "./Data"; +import * as UiConfirmation from "../Confirmation"; +import * as Language from "../../Language"; +import * as StringUtil from "../../StringUtil"; + +const containerSelector = ".jsObjectActionContainer[data-object-action-class-name]"; +const objectSelector = ".jsObjectActionObject[data-object-id]"; +const actionSelector = ".jsObjectAction[data-object-action]"; + +function executeAction(event: Event): void { + const actionElement = event.currentTarget as HTMLElement; + const objectAction = actionElement.dataset.objectAction!; + + // To support additional actions added by plugins, action elements can override the default object + // action class name and object id. + let objectActionClassName = (actionElement.closest(containerSelector) as HTMLElement).dataset.objectActionClassName; + if (actionElement.dataset.objectActionClassName) { + objectActionClassName = actionElement.dataset.objectActionClassName; + } + + let objectId = (actionElement.closest(objectSelector) as HTMLElement).dataset.objectId; + if (actionElement.dataset.objectId) { + objectId = actionElement.dataset.objectId; + } + + // Collect additional request parameters. + // TODO: Is still untested. + const parameters = {}; + Object.entries(actionElement.dataset).forEach(([key, value]) => { + if (/^objectActionParameterData.+/.exec(key)) { + if (!("data" in parameters)) { + parameters["data"] = {}; + } + parameters[StringUtil.lcfirst(key.replace(/^objectActionParameterData/, ""))] = value; + } else if (/^objectActionParameter.+/.exec(key)) { + parameters[StringUtil.lcfirst(key.replace(/^objectActionParameter/, ""))] = value; + } + }); + + function sendRequest(): void { + Ajax.apiOnce({ + data: { + actionName: objectAction, + className: objectActionClassName, + objectIDs: [objectId], + parameters: parameters, + }, + success: (data) => processAction(actionElement, data), + }); + } + + if (actionElement.dataset.confirmMessage) { + UiConfirmation.show({ + confirm: sendRequest, + message: Language.get(actionElement.dataset.confirmMessage), + messageIsHtml: true, + }); + } else { + sendRequest(); + } +} + +function processAction(actionElement: HTMLElement, data: ResponseData | DatabaseObjectActionResponse): void { + EventHandler.fire("WoltLabSuite/Core/Ui/Object/Action", actionElement.dataset.objectAction!, { + data, + objectElement: actionElement.closest(objectSelector), + } as ObjectActionData); +} + +export function setup(): void { + document + .querySelectorAll(`${containerSelector} ${objectSelector} ${actionSelector}`) + .forEach((action: HTMLElement) => { + action.addEventListener("click", (ev) => executeAction(ev)); + }); + + // TODO: handle elements added later on +} diff --git a/ts/WoltLabSuite/Core/Ui/Object/Action/Delete.ts b/ts/WoltLabSuite/Core/Ui/Object/Action/Delete.ts new file mode 100644 index 0000000000..b69a13277e --- /dev/null +++ b/ts/WoltLabSuite/Core/Ui/Object/Action/Delete.ts @@ -0,0 +1,19 @@ +/** + * Reacts to objects being deleted. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Object/Action/Delete + */ + +import UiObjectActionHandler from "./Handler"; +import { DatabaseObjectActionResponse } from "../../../Ajax/Data"; + +function deleteObject(data: DatabaseObjectActionResponse, objectElement: HTMLElement): void { + objectElement.remove(); +} + +export function setup(): void { + new UiObjectActionHandler("delete", ["delete"], deleteObject); +} diff --git a/ts/WoltLabSuite/Core/Ui/Object/Action/Handler.ts b/ts/WoltLabSuite/Core/Ui/Object/Action/Handler.ts new file mode 100644 index 0000000000..738b12c360 --- /dev/null +++ b/ts/WoltLabSuite/Core/Ui/Object/Action/Handler.ts @@ -0,0 +1,62 @@ +/** + * Default handler to react to a specific object action. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Object/Action/Handler + */ + +import * as EventHandler from "../../../Event/Handler"; +import { ClipboardData, ObjectActionData } from "../Data"; +import * as ControllerClipboard from "../../../Controller/Clipboard"; +import { DatabaseObjectActionResponse } from "../../../Ajax/Data"; + +export type ObjectAction = (data: DatabaseObjectActionResponse, objectElement: HTMLElement) => void; + +export default class UiObjectActionHandler { + protected readonly objectAction: ObjectAction; + + constructor(actionName: string, clipboardActionNames: string[], objectAction: ObjectAction) { + this.objectAction = objectAction; + + EventHandler.add("WoltLabSuite/Core/Ui/Object/Action", actionName, (data: ObjectActionData) => + this.handleObjectAction(data), + ); + + document.querySelectorAll(".jsClipboardContainer[data-type]").forEach((container: HTMLElement) => { + EventHandler.add("com.woltlab.wcf.clipboard", container.dataset.type!, (data: ClipboardData) => { + // Only consider events if the action has actually been executed. + if (data.responseData === null) { + return; + } + + if (clipboardActionNames.indexOf(data.responseData.actionName) !== -1) { + this.handleClipboardAction(data); + } + }); + }); + } + + protected handleClipboardAction(data: ClipboardData): void { + const clipboardObjectType = data.listItem.dataset.type!; + + document + .querySelectorAll(`.jsClipboardContainer[data-type="${clipboardObjectType}"] .jsClipboardObject`) + .forEach((clipboardObject: HTMLElement) => { + const objectId = clipboardObject.dataset.objectId!; + + data.responseData.objectIDs.forEach((deletedObjectId) => { + if (~~deletedObjectId === ~~objectId) { + this.objectAction(data.responseData, clipboardObject); + } + }); + }); + } + + protected handleObjectAction(data: ObjectActionData): void { + this.objectAction(data.data, data.objectElement); + + ControllerClipboard.reload(); + } +} diff --git a/ts/WoltLabSuite/Core/Ui/Object/Action/Toogle.ts b/ts/WoltLabSuite/Core/Ui/Object/Action/Toogle.ts new file mode 100644 index 0000000000..54fb91c141 --- /dev/null +++ b/ts/WoltLabSuite/Core/Ui/Object/Action/Toogle.ts @@ -0,0 +1,36 @@ +/** + * Reacts to objects being toggled. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Object/Action/Toggle + */ + +import * as Language from "../../../Language"; +import UiObjectActionHandler from "./Handler"; +import { DatabaseObjectActionResponse } from "../../../Ajax/Data"; + +function toggleObject(data: DatabaseObjectActionResponse, objectElement: HTMLElement): void { + const toggleButton = objectElement.querySelector('.jsObjectAction[data-object-action="toggle"]') as HTMLElement; + + if (toggleButton.classList.contains("fa-square-o")) { + toggleButton.classList.replace("fa-square-o", "fa-check-square-o"); + + const newTitle = toggleButton.dataset.disableTitle + ? toggleButton.dataset.disableTitle + : Language.get("wcf.global.button.disable"); + toggleButton.title = newTitle; + } else { + toggleButton.classList.replace("fa-check-square-o", "fa-square-o"); + + const newTitle = toggleButton.dataset.enableTitle + ? toggleButton.dataset.enableTitle + : Language.get("wcf.global.button.enable"); + toggleButton.title = newTitle; + } +} + +export function setup(): void { + new UiObjectActionHandler("toggle", ["enable", "disable"], toggleObject); +} diff --git a/ts/WoltLabSuite/Core/Ui/Object/Data.ts b/ts/WoltLabSuite/Core/Ui/Object/Data.ts new file mode 100644 index 0000000000..a130777815 --- /dev/null +++ b/ts/WoltLabSuite/Core/Ui/Object/Data.ts @@ -0,0 +1,13 @@ +import { DatabaseObjectActionResponse } from "../../Ajax/Data"; +import { ClipboardActionData } from "../../Controller/ClipboardData"; + +export interface ObjectActionData { + data: DatabaseObjectActionResponse; + objectElement: HTMLElement; +} + +export interface ClipboardData { + data: ClipboardActionData; + listItem: HTMLLIElement; + responseData: DatabaseObjectActionResponse; +} diff --git a/wcfsetup/install/files/acp/templates/adList.tpl b/wcfsetup/install/files/acp/templates/adList.tpl index 05d0c92d15..0b6febe125 100644 --- a/wcfsetup/install/files/acp/templates/adList.tpl +++ b/wcfsetup/install/files/acp/templates/adList.tpl @@ -8,11 +8,6 @@ offset: {@$startIndex} }); }); - - $(function() { - new WCF.Action.Delete('wcf\\data\\ad\\AdAction', '.jsAd'); - new WCF.Action.Toggle('wcf\\data\\ad\\AdAction', '.jsAd'); - });
@@ -37,17 +32,17 @@ {if $objects|count}
-
    +
      {foreach from=$objects item='ad'} -
    1. +
    2. {$ad->adName} - + - + {event name='itemButtons'} diff --git a/wcfsetup/install/files/acp/templates/tagList.tpl b/wcfsetup/install/files/acp/templates/tagList.tpl index c42c54772b..c7618d9cfc 100644 --- a/wcfsetup/install/files/acp/templates/tagList.tpl +++ b/wcfsetup/install/files/acp/templates/tagList.tpl @@ -4,14 +4,7 @@ require(['WoltLabSuite/Core/Controller/Clipboard', 'Language'], function(ControllerClipboard, Language) { Language.add('wcf.acp.tag.setAsSynonyms', '{jslang}wcf.acp.tag.setAsSynonyms{/jslang}'); - var deleteAction = new WCF.Action.Delete('wcf\\data\\tag\\TagAction', '.jsTagRow'); - deleteAction.setCallback(ControllerClipboard.reload.bind(ControllerClipboard)); - - WCF.Clipboard.init('wcf\\acp\\page\\TagListPage', {@$hasMarkedItems}, { - 'com.woltlab.wcf.tag': { - 'delete': deleteAction - } - }); + WCF.Clipboard.init('wcf\\acp\\page\\TagListPage', {@$hasMarkedItems}); new WCF.ACP.Tag.SetAsSynonymsHandler(); }); @@ -61,7 +54,7 @@ {if $objects|count}
      - +
      @@ -77,11 +70,11 @@ {foreach from=$objects item=tag} - + diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js index 88eed5c339..94204ea920 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js @@ -8,7 +8,7 @@ * @license GNU Lesser General Public License * @module WoltLabSuite/Core/Bootstrap */ -define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Date/Time/Relative", "./Devtools", "./Dom/Change/Listener", "./Environment", "./Event/Handler", "./Language", "./StringUtil", "./Ui/Dialog", "./Ui/Dropdown/Simple", "./Ui/Mobile", "./Ui/Page/Action", "./Ui/TabMenu", "./Ui/Tooltip", "./Ui/Page/JumpTo", "./Ui/Password", "./Ui/Empty", "perfect-scrollbar"], function (require, exports, tslib_1, Core, Picker_1, DateTimeRelative, Devtools_1, Listener_1, Environment, EventHandler, Language, StringUtil, Dialog_1, Simple_1, UiMobile, UiPageAction, UiTabMenu, UiTooltip, UiPageJumpTo, UiPassword, UiEmpty) { +define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Date/Time/Relative", "./Devtools", "./Dom/Change/Listener", "./Environment", "./Event/Handler", "./Language", "./StringUtil", "./Ui/Dialog", "./Ui/Dropdown/Simple", "./Ui/Mobile", "./Ui/Page/Action", "./Ui/TabMenu", "./Ui/Tooltip", "./Ui/Page/JumpTo", "./Ui/Password", "./Ui/Empty", "./Ui/Object/Action", "./Ui/Object/Action/Delete", "./Ui/Object/Action/Toogle", "perfect-scrollbar"], function (require, exports, tslib_1, Core, Picker_1, DateTimeRelative, Devtools_1, Listener_1, Environment, EventHandler, Language, StringUtil, Dialog_1, Simple_1, UiMobile, UiPageAction, UiTabMenu, UiTooltip, UiPageJumpTo, UiPassword, UiEmpty, UiObjectAction, UiObjectActionDelete, UiObjectActionToggle) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setup = void 0; @@ -30,6 +30,9 @@ define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Date/Time/R UiPageJumpTo = tslib_1.__importStar(UiPageJumpTo); UiPassword = tslib_1.__importStar(UiPassword); UiEmpty = tslib_1.__importStar(UiEmpty); + UiObjectAction = tslib_1.__importStar(UiObjectAction); + UiObjectActionDelete = tslib_1.__importStar(UiObjectActionDelete); + UiObjectActionToggle = tslib_1.__importStar(UiObjectActionToggle); // non strict equals by intent if (window.WCF == null) { window.WCF = {}; @@ -78,6 +81,9 @@ define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Date/Time/R UiTooltip.setup(); UiPassword.setup(); UiEmpty.setup(); + UiObjectAction.setup(); + UiObjectActionDelete.setup(); + UiObjectActionToggle.setup(); // Convert forms with `method="get"` into `method="post"` document.querySelectorAll("form[method=get]").forEach((form) => { form.method = "post"; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/ClipboardData.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/ClipboardData.js new file mode 100644 index 0000000000..2ae92b6a8b --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Controller/ClipboardData.js @@ -0,0 +1,4 @@ +define(["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action.js new file mode 100644 index 0000000000..eb19054118 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action.js @@ -0,0 +1,85 @@ +/** + * Handles actions that can be executed on (database) objects by clicking on specific action buttons. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Object/Action + */ +define(["require", "exports", "tslib", "../../Ajax", "../../Event/Handler", "../Confirmation", "../../Language", "../../StringUtil"], function (require, exports, tslib_1, Ajax, EventHandler, UiConfirmation, Language, StringUtil) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.setup = void 0; + Ajax = tslib_1.__importStar(Ajax); + EventHandler = tslib_1.__importStar(EventHandler); + UiConfirmation = tslib_1.__importStar(UiConfirmation); + Language = tslib_1.__importStar(Language); + StringUtil = tslib_1.__importStar(StringUtil); + const containerSelector = ".jsObjectActionContainer[data-object-action-class-name]"; + const objectSelector = ".jsObjectActionObject[data-object-id]"; + const actionSelector = ".jsObjectAction[data-object-action]"; + function executeAction(event) { + const actionElement = event.currentTarget; + const objectAction = actionElement.dataset.objectAction; + // To support additional actions added by plugins, action elements can override the default object + // action class name and object id. + let objectActionClassName = actionElement.closest(containerSelector).dataset.objectActionClassName; + if (actionElement.dataset.objectActionClassName) { + objectActionClassName = actionElement.dataset.objectActionClassName; + } + let objectId = actionElement.closest(objectSelector).dataset.objectId; + if (actionElement.dataset.objectId) { + objectId = actionElement.dataset.objectId; + } + // Collect additional request parameters. + // TODO: Is still untested. + const parameters = {}; + Object.entries(actionElement.dataset).forEach(([key, value]) => { + if (/^objectActionParameterData.+/.exec(key)) { + if (!("data" in parameters)) { + parameters["data"] = {}; + } + parameters[StringUtil.lcfirst(key.replace(/^objectActionParameterData/, ""))] = value; + } + else if (/^objectActionParameter.+/.exec(key)) { + parameters[StringUtil.lcfirst(key.replace(/^objectActionParameter/, ""))] = value; + } + }); + function sendRequest() { + Ajax.apiOnce({ + data: { + actionName: objectAction, + className: objectActionClassName, + objectIDs: [objectId], + parameters: parameters, + }, + success: (data) => processAction(actionElement, data), + }); + } + if (actionElement.dataset.confirmMessage) { + UiConfirmation.show({ + confirm: sendRequest, + message: Language.get(actionElement.dataset.confirmMessage), + messageIsHtml: true, + }); + } + else { + sendRequest(); + } + } + function processAction(actionElement, data) { + EventHandler.fire("WoltLabSuite/Core/Ui/Object/Action", actionElement.dataset.objectAction, { + data, + objectElement: actionElement.closest(objectSelector), + }); + } + function setup() { + document + .querySelectorAll(`${containerSelector} ${objectSelector} ${actionSelector}`) + .forEach((action) => { + action.addEventListener("click", (ev) => executeAction(ev)); + }); + // TODO: handle elements added later on + } + exports.setup = setup; +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Delete.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Delete.js new file mode 100644 index 0000000000..4261900ba1 --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Delete.js @@ -0,0 +1,21 @@ +/** + * Reacts to objects being deleted. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Object/Action/Delete + */ +define(["require", "exports", "tslib", "./Handler"], function (require, exports, tslib_1, Handler_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.setup = void 0; + Handler_1 = tslib_1.__importDefault(Handler_1); + function deleteObject(data, objectElement) { + objectElement.remove(); + } + function setup() { + new Handler_1.default("delete", ["delete"], deleteObject); + } + exports.setup = setup; +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Handler.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Handler.js new file mode 100644 index 0000000000..e0b4ed656f --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Handler.js @@ -0,0 +1,49 @@ +/** + * Default handler to react to a specific object action. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Object/Action/Handler + */ +define(["require", "exports", "tslib", "../../../Event/Handler", "../../../Controller/Clipboard"], function (require, exports, tslib_1, EventHandler, ControllerClipboard) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + EventHandler = tslib_1.__importStar(EventHandler); + ControllerClipboard = tslib_1.__importStar(ControllerClipboard); + class UiObjectActionHandler { + constructor(actionName, clipboardActionNames, objectAction) { + this.objectAction = objectAction; + EventHandler.add("WoltLabSuite/Core/Ui/Object/Action", actionName, (data) => this.handleObjectAction(data)); + document.querySelectorAll(".jsClipboardContainer[data-type]").forEach((container) => { + EventHandler.add("com.woltlab.wcf.clipboard", container.dataset.type, (data) => { + // Only consider events if the action has actually been executed. + if (data.responseData === null) { + return; + } + if (clipboardActionNames.indexOf(data.responseData.actionName) !== -1) { + this.handleClipboardAction(data); + } + }); + }); + } + handleClipboardAction(data) { + const clipboardObjectType = data.listItem.dataset.type; + document + .querySelectorAll(`.jsClipboardContainer[data-type="${clipboardObjectType}"] .jsClipboardObject`) + .forEach((clipboardObject) => { + const objectId = clipboardObject.dataset.objectId; + data.responseData.objectIDs.forEach((deletedObjectId) => { + if (~~deletedObjectId === ~~objectId) { + this.objectAction(data.responseData, clipboardObject); + } + }); + }); + } + handleObjectAction(data) { + this.objectAction(data.data, data.objectElement); + ControllerClipboard.reload(); + } + } + exports.default = UiObjectActionHandler; +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Toogle.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Toogle.js new file mode 100644 index 0000000000..b7bbf2ab4b --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Action/Toogle.js @@ -0,0 +1,36 @@ +/** + * Reacts to objects being toggled. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Ui/Object/Action/Toggle + */ +define(["require", "exports", "tslib", "../../../Language", "./Handler"], function (require, exports, tslib_1, Language, Handler_1) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.setup = void 0; + Language = tslib_1.__importStar(Language); + Handler_1 = tslib_1.__importDefault(Handler_1); + function toggleObject(data, objectElement) { + const toggleButton = objectElement.querySelector('.jsObjectAction[data-object-action="toggle"]'); + if (toggleButton.classList.contains("fa-square-o")) { + toggleButton.classList.replace("fa-square-o", "fa-check-square-o"); + const newTitle = toggleButton.dataset.disableTitle + ? toggleButton.dataset.disableTitle + : Language.get("wcf.global.button.disable"); + toggleButton.title = newTitle; + } + else { + toggleButton.classList.replace("fa-check-square-o", "fa-square-o"); + const newTitle = toggleButton.dataset.enableTitle + ? toggleButton.dataset.enableTitle + : Language.get("wcf.global.button.enable"); + toggleButton.title = newTitle; + } + } + function setup() { + new Handler_1.default("toggle", ["enable", "disable"], toggleObject); + } + exports.setup = setup; +}); diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Data.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Data.js new file mode 100644 index 0000000000..2ae92b6a8b --- /dev/null +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Object/Data.js @@ -0,0 +1,4 @@ +define(["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); +}); -- 2.20.1
      - + {event name='rowButtons'}