164 lines
4.1 KiB
JavaScript
164 lines
4.1 KiB
JavaScript
import { __ID__, filePath } from "../consts.mjs";
|
|
import { performArraySort } from "../utils/performArraySort.mjs";
|
|
|
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
|
const { DragDrop } = foundry.applications.ux;
|
|
const { getDocumentClass } = foundry.utils;
|
|
|
|
export class SidebarTabRearranger extends HandlebarsApplicationMixin(ApplicationV2) {
|
|
// #region Options
|
|
static DEFAULT_OPTIONS = {
|
|
tag: `form`,
|
|
classes: [
|
|
__ID__,
|
|
`SidebarTabRearranger`,
|
|
],
|
|
window: {
|
|
title: `OFT.apps.SidebarTabRearranger.title`,
|
|
},
|
|
position: {},
|
|
form: {
|
|
handler: this.#onSubmit,
|
|
closeOnSubmit: true,
|
|
submitOnChange: false,
|
|
},
|
|
actions: {
|
|
},
|
|
};
|
|
|
|
static PARTS = {
|
|
list: { template: filePath(`templates/SidebarTabRearranger/list.hbs`) },
|
|
footer: { template: filePath(`templates/SidebarTabRearranger/footer.hbs`) },
|
|
};
|
|
// #endregion Options
|
|
|
|
// #region Instance Data
|
|
#order = [];
|
|
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
// TODO: define this using the game settings
|
|
this.#order = Object.keys(ui.sidebar.constructor.TABS);
|
|
};
|
|
// #endregion Instance Data
|
|
|
|
// #region Lifecycle
|
|
async _onRender(...args) {
|
|
await super._onRender(...args);
|
|
|
|
new DragDrop.implementation({
|
|
dragSelector: `.tab`,
|
|
dropSelector: `.drop-zone`,
|
|
callbacks: {
|
|
dragstart: this.#onDragStart.bind(this),
|
|
dragenter: this.#onDragEnter.bind(this),
|
|
dragleave: this.#onDragLeave.bind(this),
|
|
dragend: this.#onDragEnd.bind(this),
|
|
drop: this.#onDrop.bind(this),
|
|
},
|
|
}).bind(this.element);
|
|
};
|
|
|
|
/** @this {SidebarTabRearranger} */
|
|
static async #onSubmit() {};
|
|
// #endregion Lifecycle
|
|
|
|
// #region Data Prep
|
|
async _prepareContext() {
|
|
const ctx = {
|
|
meta: {
|
|
idp: this.id,
|
|
},
|
|
};
|
|
|
|
const tabs = ui.sidebar.constructor.TABS;
|
|
ctx.tabs = [];
|
|
for (let i = 0; i < this.#order.length; i++) {
|
|
const id = this.#order[i];
|
|
const tab = tabs[id];
|
|
if (!tab) { continue };
|
|
|
|
let { documentName, gmOnly, tooltip, icon } = tab;
|
|
if (gmOnly && !game.user.isGM) { continue };
|
|
|
|
if (documentName) {
|
|
tooltip ??= getDocumentClass(documentName).metadata.labelPlural;
|
|
icon ??= CONFIG[documentName]?.sidebarIcon;
|
|
};
|
|
|
|
ctx.tabs.push({
|
|
id,
|
|
name: game.i18n.localize(tooltip),
|
|
icon,
|
|
nextIndex: i + 1,
|
|
});
|
|
};
|
|
|
|
return ctx;
|
|
};
|
|
// #endregion Data Prep
|
|
|
|
// #region Actions
|
|
// #endregion Actions
|
|
|
|
// #region Drag & Drop
|
|
#onDragStart(event) {
|
|
/** @type {HTMLLIElement|undefined} */
|
|
const target = event.target.closest(`[data-tab-id]`);
|
|
if (!target) { return };
|
|
|
|
const tabID = target.dataset.tabId;
|
|
|
|
target.classList.add(`no-hover-styles`);
|
|
event.dataTransfer.setDragImage(target, 0, 0);
|
|
event.dataTransfer.setData(`oft/tab`, tabID);
|
|
target.closest(`.tab-list`)?.classList.add(`dragging`);
|
|
|
|
/*
|
|
This timeout is required to get the difference between the drag
|
|
image and the element in-DOM, because this puts the class removal
|
|
in a subsequent event cycle instead of being handled in the current
|
|
cycle.
|
|
*/
|
|
setTimeout(() => target.classList.remove(`no-hover-styles`), 0);
|
|
};
|
|
|
|
#onDragEnter(event) {
|
|
event.currentTarget.style.setProperty(`--colour`, `#00aa00`);
|
|
};
|
|
|
|
#onDragLeave(event) {
|
|
event.currentTarget.style.removeProperty(`--colour`);
|
|
};
|
|
|
|
#onDragEnd() {
|
|
this.element.querySelector(`.tab-list`)?.classList.remove(`dragging`);
|
|
this.element
|
|
.querySelectorAll(`[style="--colour: #00aa00"]`)
|
|
.forEach(el => el.style.removeProperty(`--colour`));
|
|
};
|
|
|
|
#onDrop(event) {
|
|
const droppedID = event.dataTransfer.getData(`oft/tab`);
|
|
this.element.querySelector(`.tab-list`)?.classList.remove(`dragging`);
|
|
event.currentTarget?.style?.removeProperty(`--colour`);
|
|
if (!droppedID) { return };
|
|
|
|
const droppedIndex = this.#order.findIndex(t => t === droppedID);
|
|
const dropTarget = event.currentTarget;
|
|
const targetIndex = parseInt(dropTarget?.dataset.moveToIndex);
|
|
if (
|
|
!dropTarget
|
|
|| droppedIndex < 0
|
|
|| targetIndex === droppedIndex
|
|
) { return };
|
|
|
|
this.#order = performArraySort(
|
|
droppedID,
|
|
{ targetIndex, list: this.#order },
|
|
);
|
|
this.render({ parts: [`list`] });
|
|
};
|
|
// #endregion Drag & Drop
|
|
};
|