Compare commits

..

No commits in common. "feature/rearrange-sidebar-tabs" and "main" have entirely different histories.

11 changed files with 1 additions and 371 deletions

View file

@ -1 +0,0 @@
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path d="M45.832 75c0 4.582-3.75 8.332-8.332 8.332s-8.332-3.75-8.332-8.332 3.75-8.332 8.332-8.332 8.332 3.75 8.332 8.332M37.5 41.668c-4.582 0-8.332 3.75-8.332 8.332s3.75 8.332 8.332 8.332 8.332-3.75 8.332-8.332-3.75-8.332-8.332-8.332m0-25c-4.582 0-8.332 3.75-8.332 8.332s3.75 8.332 8.332 8.332 8.332-3.75 8.332-8.332-3.75-8.332-8.332-8.332m25 16.664c4.582 0 8.332-3.75 8.332-8.332s-3.75-8.332-8.332-8.332-8.332 3.75-8.332 8.332 3.75 8.332 8.332 8.332m0 8.336c-4.582 0-8.332 3.75-8.332 8.332s3.75 8.332 8.332 8.332 8.332-3.75 8.332-8.332-3.75-8.332-8.332-8.332m0 25c-4.582 0-8.332 3.75-8.332 8.332s3.75 8.332 8.332 8.332 8.332-3.75 8.332-8.332-3.75-8.332-8.332-8.332"/></svg>

Before

Width:  |  Height:  |  Size: 736 B

View file

@ -76,11 +76,6 @@
"name": "Hotbar Settings", "name": "Hotbar Settings",
"hint": "Tweaks that modify Foundry's hotbar", "hint": "Tweaks that modify Foundry's hotbar",
"label": "Configure Hotbar" "label": "Configure Hotbar"
},
"rearrangeSidebarTabs": {
"name": "Rearrange Sidebar Tabs",
"hint": "(v13+) Allows you to customize the order the right-hand sidebar tabs appear in. Including module-added sidebar tabs.",
"label": "Change Tab Order"
} }
}, },
"keybindings": { "keybindings": {
@ -90,7 +85,6 @@
} }
}, },
"apps": { "apps": {
"discard-changes": "Discard Changes",
"no-settings-to-display": "No settings to display", "no-settings-to-display": "No settings to display",
"make-global-reference": "Make Global Reference", "make-global-reference": "Make Global Reference",
"StatusEffectIconConfig": { "StatusEffectIconConfig": {
@ -98,11 +92,6 @@
"no-status-effects": "No status effects detected, this is most likely due to your game system or other modules.", "no-status-effects": "No status effects detected, this is most likely due to your game system or other modules.",
"remove-override": "Remove custom override", "remove-override": "Remove custom override",
"select-using-image-tagger": "Select using Image Tagger" "select-using-image-tagger": "Select using Image Tagger"
},
"SidebarTabRearranger": {
"title": "Rearrange Sidebar Tabs",
"top": "Top",
"bottom": "Bottom"
} }
}, },
"notifs": { "notifs": {

View file

@ -1,164 +0,0 @@
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
};

View file

@ -10,7 +10,6 @@ import { hotbarButtonGap } from "../tweaks/hotbarButtonGap.mjs";
import { hotbarButtonSize } from "../tweaks/hotbarButtonSize.mjs"; import { hotbarButtonSize } from "../tweaks/hotbarButtonSize.mjs";
import { preventTokenRotation } from "../tweaks/preventTokenRotation.mjs"; import { preventTokenRotation } from "../tweaks/preventTokenRotation.mjs";
import { preventUserConfigOpen } from "../tweaks/preventUserConfigOpen.mjs"; import { preventUserConfigOpen } from "../tweaks/preventUserConfigOpen.mjs";
import { rearrangeSidebarTabs } from "../tweaks/rearrangeSidebarTabs.mjs";
import { repositionHotbar } from "../tweaks/repositionHotbar.mjs"; import { repositionHotbar } from "../tweaks/repositionHotbar.mjs";
import { startingSidebarTab } from "../tweaks/startingSidebarTab.mjs"; import { startingSidebarTab } from "../tweaks/startingSidebarTab.mjs";
import { startSidebarExpanded } from "../tweaks/startSidebarExpanded.mjs"; import { startSidebarExpanded } from "../tweaks/startSidebarExpanded.mjs";
@ -51,7 +50,6 @@ Hooks.on(`setup`, () => {
repositionHotbar(); repositionHotbar();
customStatusIcons(); customStatusIcons();
rearrangeSidebarTabs();
chatImageLinks(); chatImageLinks();
chatSidebarBackground(); chatSidebarBackground();
startSidebarExpanded(); startSidebarExpanded();

View file

@ -1,39 +0,0 @@
import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs";
import { __ID__ } from "../consts.mjs";
import { Logger } from "../utils/Logger.mjs";
import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs";
import { SidebarTabRearranger } from "../apps/SidebarTabRearranger.mjs";
export const key = `rearrangeSidebarTabs`;
export function rearrangeSidebarTabs() {
status[key] = SettingStatusEnum.Unknown;
if (preventTweakRegistration(key)) { return };
// #region Registration
Logger.log(`Registering tweak: ${key}`);
game.settings.registerMenu(__ID__, `${key}Menu`, {
name: `OFT.menu.${key}.name`,
hint: `OFT.menu.${key}.hint`,
label: `OFT.menu.${key}.label`,
restricted: false,
type: SidebarTabRearranger,
});
game.settings.register(__ID__, `${key}World`, {
scope: `world`,
config: false,
type: Array,
});
game.settings.register(__ID__, `${key}User`, {
scope: `user`,
config: false,
type: Array,
});
// #endregion Registration
// #region Implementation
// TODO: do this
// #endregion Implementation
status[key] = SettingStatusEnum.Registered;
};

View file

@ -1,31 +0,0 @@
export function performArraySort(
element,
{ list, targetIndex },
) {
// Case: same position
if (list.indexOf(el => el === element) === targetIndex) {
return Array.from(list);
};
// Case: start of array
if (targetIndex === 0) {
list = list.filter(el => el !== element);
return [element, ...list];
};
// Case: end of array
if (targetIndex === list.length - 1) {
list = list.filter(el => el !== element);
return [...list, element];
};
// Case: middle of array
const front = list
.slice(0, targetIndex)
.filter(el => el !== element);
const back = list
.slice(targetIndex)
.filter(el => el !== element);
return [...front, element, ...back];
};

View file

@ -1,69 +0,0 @@
.oft.SidebarTabRearranger {
> .window-content {
gap: 16px;
}
footer {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.drop-zone {
--colour: transparent;
background: color-mix(in srgb, var(--colour) 30%, transparent 70%);
border: 1px solid var(--colour);
border-radius: 4px;
transition: all 150ms ease-in-out;
width: 12px;
}
.tab-list {
display: flex;
flex-direction: row;
row-gap: 8px;
column-gap: 2px;
list-style-type: none;
margin: 0;
padding: 0;
&.dragging > .drop-zone {
--colour: #c9593f;
}
}
.tab {
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
cursor: var(--cursor-grab);
> .drag-handle-icon {
display: none;
}
&:hover:not(.no-hover-styles) {
> .sidebar-icon {
display: none;
}
> .drag-handle-icon {
display: initial;
}
}
}
.emulate-button {
--size: 32px;
display: flex;
justify-content: center;
align-items: center;
height: var(--size);
aspect-ratio: 1;
border: 1px solid var(--color-light-5);
border-radius: 4px;
}
.bottom-label {
text-align: right;
}
}

View file

@ -6,7 +6,6 @@
@import url("./repositionHotbar.css") layer(tweaks); @import url("./repositionHotbar.css") layer(tweaks);
@import url("./apps/common.css") layer(apps); @import url("./apps/common.css") layer(apps);
@import url("./apps/SidebarTabRearranger.css") layer(apps);
@import url("./apps/StatusEffectIconConfig.css") layer(apps); @import url("./apps/StatusEffectIconConfig.css") layer(apps);
/* Make the chat sidebar the same width as all the other tabs */ /* Make the chat sidebar the same width as all the other tabs */

View file

@ -2,12 +2,7 @@
<button <button
type="submit" type="submit"
> >
<oft-icon <i class="fa-solid fa-floppy-disk" inert aria-hidden="true"></i>
name="icons/save"
aria-hidden="true"
var:fill="currentColor"
var:size="1.25rem"
></oft-icon>
{{localize "SETTINGS.Save"}} {{localize "SETTINGS.Save"}}
</button> </button>
</footer> </footer>

View file

@ -1,14 +0,0 @@
<footer>
<button type="close">
{{ localize "OFT.apps.discard-changes" }}
</button>
<button>
<oft-icon
name="icons/save"
aria-hidden="true"
var:fill="currentColor"
var:size="1rem"
></oft-icon>
{{ localize "Save Changes" }}
</button>
</footer>

View file

@ -1,33 +0,0 @@
<main>
<div class="top-label">
{{ localize "OFT.apps.SidebarTabRearranger.top" }}
</div>
<div class="tab-list" data-tooltip-direction="UP">
<div
class="drop-zone"
data-move-to-index="0"
></div>
{{#each tabs as | tab |}}
<div
class="emulate-button tab"
data-tab-id="{{tab.id}}"
data-tooltip="{{tab.name}}"
>
<span class="sidebar-icon {{tab.icon}}"></span>
<oft-icon
class="drag-handle-icon"
name="icons/drag-handle"
var:fill="color-mix(in srgb, currentColor 75%, black 25%)"
var:size="1rem"
></oft-icon>
</div>
<div
class="drop-zone"
data-move-to-index="{{tab.nextIndex}}"
></div>
{{/each}}
</div>
<div class="bottom-label">
{{ localize "OFT.apps.SidebarTabRearranger.bottom" }}
</div>
</main>