From d6be5a065b759f570eaa655661735289c44284d6 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Thu, 28 Nov 2024 10:16:26 +0100 Subject: [PATCH] Save sortable list --- ts/WoltLabSuite/Core/Ui/Sortable/List.ts | 40 +++++++++++++++---- .../js/WoltLabSuite/Core/Ui/Sortable/List.js | 32 +++++++++++---- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/ts/WoltLabSuite/Core/Ui/Sortable/List.ts b/ts/WoltLabSuite/Core/Ui/Sortable/List.ts index 68ba0dff4f..74d792dcbf 100644 --- a/ts/WoltLabSuite/Core/Ui/Sortable/List.ts +++ b/ts/WoltLabSuite/Core/Ui/Sortable/List.ts @@ -1,14 +1,17 @@ /** * Sortable lists with optimized handling per device sizes. * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH + * @author Olaf Braun, Alexander Ebert + * @copyright 2001-2024 WoltLab GmbH * @license GNU Lesser General Public License * @woltlabExcludeBundle tiny */ import * as Core from "../../Core"; import Sortable from "sortablejs"; +import { apiOnce } from "WoltLabSuite/Core/Ajax"; +import { show as showNotification } from "WoltLabSuite/Core/Ui/Notification"; +import { getPhrase } from "WoltLabSuite/Core/Language"; interface UnknownObject { [key: string]: unknown; @@ -55,10 +58,9 @@ class UiSortableList { className: "", offset: 0, maxNestingLevel: undefined, - toleranceElement: "> span", + toleranceElement: "span", options: { animation: 150, - swapThreshold: 0.65, fallbackOnBody: true, dataIdAttr: "data-object-id", chosenClass: "sortablePlaceholder", @@ -80,7 +82,7 @@ class UiSortableList { return true; } - return Sortable.utils.is(target, this._options.toleranceElement); + return !Sortable.utils.is(eventTarget, this._options.toleranceElement); }, onMove: (event: Sortable.MoveEvent) => { if (this._options.maxNestingLevel === undefined) { @@ -142,7 +144,7 @@ class UiSortableList { } this.#sortables.set( - 0, + sortableList.dataset.objectId ? parseInt(sortableList.dataset.objectId, 10) : 0, new Sortable(sortableList, { direction: "vertical", ...this._options.options, @@ -181,7 +183,31 @@ class UiSortableList { return; } - // TODO save postions and send them to server + const structure = Object.fromEntries( + Array.from(this.#sortables).map(([objectId, sortable]) => [objectId, sortable.toArray()]), + ); + + const parameters = Core.extend( + { + data: { + offset: this._options.offset, + structure: structure, + }, + }, + this._options.additionalParameters, + ); + + apiOnce({ + data: { + actionName: "updatePosition", + className: this._options.className, + interfaceName: "wcf\\data\\ISortableAction", + parameters: parameters, + }, + success: () => { + showNotification(getPhrase("wcf.global.success.edit")); + }, + }); } } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Sortable/List.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Sortable/List.js index ef4a8a5edd..be0623f3f3 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Sortable/List.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Sortable/List.js @@ -1,12 +1,12 @@ /** * Sortable lists with optimized handling per device sizes. * - * @author Alexander Ebert - * @copyright 2001-2019 WoltLab GmbH + * @author Olaf Braun, Alexander Ebert + * @copyright 2001-2024 WoltLab GmbH * @license GNU Lesser General Public License * @woltlabExcludeBundle tiny */ -define(["require", "exports", "tslib", "../../Core", "sortablejs"], function (require, exports, tslib_1, Core, sortablejs_1) { +define(["require", "exports", "tslib", "../../Core", "sortablejs", "WoltLabSuite/Core/Ajax", "WoltLabSuite/Core/Ui/Notification", "WoltLabSuite/Core/Language"], function (require, exports, tslib_1, Core, sortablejs_1, Ajax_1, Notification_1, Language_1) { "use strict"; Core = tslib_1.__importStar(Core); sortablejs_1 = tslib_1.__importDefault(sortablejs_1); @@ -33,10 +33,9 @@ define(["require", "exports", "tslib", "../../Core", "sortablejs"], function (re className: "", offset: 0, maxNestingLevel: undefined, - toleranceElement: "> span", + toleranceElement: "span", options: { animation: 150, - swapThreshold: 0.65, fallbackOnBody: true, dataIdAttr: "data-object-id", chosenClass: "sortablePlaceholder", @@ -56,7 +55,7 @@ define(["require", "exports", "tslib", "../../Core", "sortablejs"], function (re if (!this._options.toleranceElement) { return true; } - return sortablejs_1.default.utils.is(target, this._options.toleranceElement); + return !sortablejs_1.default.utils.is(eventTarget, this._options.toleranceElement); }, onMove: (event) => { if (this._options.maxNestingLevel === undefined) { @@ -105,7 +104,7 @@ define(["require", "exports", "tslib", "../../Core", "sortablejs"], function (re this._options.options.draggable = "tr"; this._options.toleranceElement = undefined; } - this.#sortables.set(0, new sortablejs_1.default(sortableList, { + this.#sortables.set(sortableList.dataset.objectId ? parseInt(sortableList.dataset.objectId, 10) : 0, new sortablejs_1.default(sortableList, { direction: "vertical", ...this._options.options, })); @@ -136,7 +135,24 @@ define(["require", "exports", "tslib", "../../Core", "sortablejs"], function (re if (!this._options.className) { return; } - // TODO save postions and send them to server + const structure = Object.fromEntries(Array.from(this.#sortables).map(([objectId, sortable]) => [objectId, sortable.toArray()])); + const parameters = Core.extend({ + data: { + offset: this._options.offset, + structure: structure, + }, + }, this._options.additionalParameters); + (0, Ajax_1.apiOnce)({ + data: { + actionName: "updatePosition", + className: this._options.className, + interfaceName: "wcf\\data\\ISortableAction", + parameters: parameters, + }, + success: () => { + (0, Notification_1.show)((0, Language_1.getPhrase)("wcf.global.success.edit")); + }, + }); } } return UiSortableList; -- 2.20.1