import { PageMenuMainProvider } from "./Ui/Page/Menu/Main/Provider";
import { whenFirstSeen } from "./LazyLoader";
import { adoptPageOverlayContainer } from "./Helper/PageOverlay";
+import { setup as setupFormBuilderButton } from "./Component/FormBuilder/Button";
// perfectScrollbar does not need to be bound anywhere, it just has to be loaded for WCF.js
import "perfect-scrollbar";
UiObjectActionDelete.setup();
UiObjectActionToggle.setup();
initSearch();
+ setupFormBuilderButton();
// Convert forms with `method="get"` into `method="post"`
document.querySelectorAll("form[method=get]").forEach((form: HTMLFormElement) => {
--- /dev/null
+/**
+ * Binds to button-like elements with the attribute [data-formbuilder] and invokes
+ * the endpoint to request the form builder dialog.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2023 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.0
+ */
+
+import { dialogFactory } from "../Dialog";
+import { wheneverSeen } from "../../Helper/Selector";
+
+const reponseIdentifier = "__Psr15DialogFormResponse";
+
+type Psr15DialogFormResponse = {
+ payload:
+ | {
+ reload: true;
+ }
+ | {
+ redirectUrl: string;
+ };
+ __Psr15DialogFormResponse: true;
+};
+
+async function requestForm(element: HTMLElement): Promise<void> {
+ const { ok, result } = await dialogFactory().usingFormBuilder().fromEndpoint(element.dataset.endpoint!);
+ if (!ok) {
+ return;
+ }
+
+ const event = new CustomEvent<unknown>("formBuilder:result", {
+ cancelable: true,
+ detail: {
+ result,
+ },
+ });
+ element.dispatchEvent(event);
+
+ if (event.defaultPrevented) {
+ return;
+ }
+
+ if (typeof result === "object" && result !== null && Object.hasOwn(result, reponseIdentifier)) {
+ const payload = (result as Psr15DialogFormResponse).payload;
+ if ("reload" in payload) {
+ window.location.reload();
+ } else {
+ window.location.href = payload.redirectUrl;
+ }
+
+ return;
+ }
+}
+
+export function setup(): void {
+ wheneverSeen("[data-formbuilder]", (element) => {
+ if (element.tagName !== "A" && element.tagName !== "BUTTON") {
+ throw new TypeError("Cannot initialize the FormBuilder on non button-like elements", {
+ cause: {
+ element,
+ },
+ });
+ }
+
+ if (!element.dataset.endpoint) {
+ throw new Error("Missing the [data-endpoint] attribute.", {
+ cause: {
+ element,
+ },
+ });
+ }
+
+ element.addEventListener("click", (event) => {
+ event.preventDefault();
+
+ void requestForm(element);
+ });
+ });
+}
* @copyright 2001-2022 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*/
-define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Devtools", "./Dom/Change/Listener", "./Environment", "./Event/Handler", "./Form/XsrfToken", "./Language", "./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/Toggle", "./Ui/Search", "./LazyLoader", "./Helper/PageOverlay", "perfect-scrollbar"], function (require, exports, tslib_1, Core, Picker_1, Devtools_1, Listener_1, Environment, EventHandler, XsrfToken, Language, Dialog_1, Simple_1, UiMobile, UiPageAction, UiTabMenu, UiTooltip, UiPageJumpTo, UiPassword, UiEmpty, UiObjectAction, UiObjectActionDelete, UiObjectActionToggle, Search_1, LazyLoader_1, PageOverlay_1) {
+define(["require", "exports", "tslib", "./Core", "./Date/Picker", "./Devtools", "./Dom/Change/Listener", "./Environment", "./Event/Handler", "./Form/XsrfToken", "./Language", "./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/Toggle", "./Ui/Search", "./LazyLoader", "./Helper/PageOverlay", "./Component/FormBuilder/Button", "perfect-scrollbar"], function (require, exports, tslib_1, Core, Picker_1, Devtools_1, Listener_1, Environment, EventHandler, XsrfToken, Language, Dialog_1, Simple_1, UiMobile, UiPageAction, UiTabMenu, UiTooltip, UiPageJumpTo, UiPassword, UiEmpty, UiObjectAction, UiObjectActionDelete, UiObjectActionToggle, Search_1, LazyLoader_1, PageOverlay_1, Button_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setup = void 0;
UiObjectActionDelete.setup();
UiObjectActionToggle.setup();
(0, Search_1.init)();
+ (0, Button_1.setup)();
// Convert forms with `method="get"` into `method="post"`
document.querySelectorAll("form[method=get]").forEach((form) => {
form.method = "post";
--- /dev/null
+/**
+ * Binds to button-like elements with the attribute [data-formbuilder] and invokes
+ * the endpoint to request the form builder dialog.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2023 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.0
+ */
+define(["require", "exports", "../Dialog", "../../Helper/Selector"], function (require, exports, Dialog_1, Selector_1) {
+ "use strict";
+ Object.defineProperty(exports, "__esModule", { value: true });
+ exports.setup = void 0;
+ const reponseIdentifier = "__Psr15DialogFormResponse";
+ async function requestForm(element) {
+ const { ok, result } = await (0, Dialog_1.dialogFactory)().usingFormBuilder().fromEndpoint(element.dataset.endpoint);
+ if (!ok) {
+ return;
+ }
+ const event = new CustomEvent("formBuilder:result", {
+ cancelable: true,
+ detail: {
+ result,
+ },
+ });
+ element.dispatchEvent(event);
+ if (event.defaultPrevented) {
+ return;
+ }
+ if (typeof result === "object" && result !== null && Object.hasOwn(result, reponseIdentifier)) {
+ const payload = result.payload;
+ if ("reload" in payload) {
+ window.location.reload();
+ }
+ else {
+ window.location.href = payload.redirectUrl;
+ }
+ return;
+ }
+ }
+ function setup() {
+ (0, Selector_1.wheneverSeen)("[data-formbuilder]", (element) => {
+ if (element.tagName !== "A" && element.tagName !== "BUTTON") {
+ throw new TypeError("Cannot initialize the FormBuilder on non button-like elements", {
+ cause: {
+ element,
+ },
+ });
+ }
+ if (!element.dataset.endpoint) {
+ throw new Error("Missing the [data-endpoint] attribute.", {
+ cause: {
+ element,
+ },
+ });
+ }
+ element.addEventListener("click", (event) => {
+ event.preventDefault();
+ void requestForm(element);
+ });
+ });
+ }
+ exports.setup = setup;
+});
--- /dev/null
+<?php
+
+namespace wcf\system\form\builder;
+
+use Laminas\Diactoros\Response\JsonResponse;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Creates a response that is understood by the `[data-formbuilder]` implementation
+ * as a shortcut for tasks like reloading the page or redirecting to another URL.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2023 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.0
+ */
+final class Psr15DialogFormResponse
+{
+ private readonly array $payload;
+
+ private const RESPONSE_IDENTIFIER = "__Psr15DialogFormResponse";
+
+ /**
+ * Redirects the client to the provided URL.
+ */
+ public static function redirect(string $redirectUrl): self
+ {
+ return new self([
+ "redirectUrl" => $redirectUrl,
+ ]);
+ }
+
+ /**
+ * Instructs the client to reload the page.
+ */
+ public static function reload(): self
+ {
+ return new self([
+ "reload" => true,
+ ]);
+ }
+
+ /**
+ * Converts this into a PSR response.
+ */
+ public function toResponse(): ResponseInterface
+ {
+ return new JsonResponse([
+ 'payload' => $this->payload,
+ self::RESPONSE_IDENTIFIER => true,
+ ]);
+ }
+
+ private function __construct(array $payload)
+ {
+ $this->payload = $payload;
+ }
+}