feat: make SLA Campaign Clock movable, resizable, and minimizable popped-out window app
This commit is contained in:
+101
-46
@@ -211,49 +211,92 @@ async function advanceShift(delta = 1) {
|
||||
ui.notifications.info(`Campaign time advanced to ${buildCampaignStamp(nextState)}.`);
|
||||
}
|
||||
|
||||
function renderCampaignBanner() {
|
||||
let host = document.querySelector("#sla-shift-ledger-banner");
|
||||
if (!host) {
|
||||
host = document.createElement("section");
|
||||
host.id = "sla-shift-ledger-banner";
|
||||
host.className = "sla-shift-ledger-banner";
|
||||
document.body.appendChild(host);
|
||||
class SlaCampaignClockApp extends Application {
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
id: "sla-campaign-clock-app",
|
||||
title: "SLA Campaign Clock",
|
||||
classes: ["sla-campaign-clock-window", "mosh"],
|
||||
width: 580,
|
||||
height: "auto",
|
||||
resizable: true,
|
||||
minimizable: true,
|
||||
popOut: true
|
||||
});
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
const weekNumber = getWeekFromDay(state.day);
|
||||
const currentShift = getShift(state.shift);
|
||||
async _renderInner(data) {
|
||||
const state = getState();
|
||||
const weekNumber = getWeekFromDay(state.day);
|
||||
const currentShift = getShift(state.shift);
|
||||
|
||||
host.innerHTML = `
|
||||
<div class="sla-shift-ledger-banner-inner">
|
||||
<div class="sla-shift-ledger-banner-meta">
|
||||
<div class="sla-shift-ledger-banner-kicker">SLA Campaign Clock</div>
|
||||
<div class="sla-shift-ledger-banner-title">Day ${state.day} · Week ${weekNumber}</div>
|
||||
<div class="sla-shift-ledger-banner-subtitle">${currentShift.label}</div>
|
||||
</div>
|
||||
<div class="sla-shift-ledger-banner-main">
|
||||
<div class="sla-shift-ledger-shifts">
|
||||
${SHIFTS.map((shift, index) => `
|
||||
<div class="sla-shift-ledger-shift ${index === Number(state.shift) ? "is-active" : ""}">
|
||||
${shift.short}
|
||||
</div>
|
||||
`).join("")}
|
||||
const html = `
|
||||
<div class="sla-shift-ledger-window-inner">
|
||||
<div class="sla-shift-ledger-banner-meta">
|
||||
<div class="sla-shift-ledger-banner-kicker" style="color: #7cc4ff; font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase;">SLA Campaign Clock</div>
|
||||
<div class="sla-shift-ledger-banner-title" style="font-size: 1.2rem; font-weight: 700; color: #f4f8fd; margin: 4px 0;">Day ${state.day} · Week ${weekNumber}</div>
|
||||
<div class="sla-shift-ledger-banner-subtitle" style="font-size: 0.85rem; color: #9eb5cf; margin-top: 2px;">${currentShift.label}</div>
|
||||
</div>
|
||||
<div class="sla-shift-ledger-banner-controls">
|
||||
${game.user?.isGM ? `
|
||||
<button type="button" class="sla-shift-ledger-button" data-action="prev">Prev</button>
|
||||
<button type="button" class="sla-shift-ledger-button" data-action="next">Next</button>
|
||||
<button type="button" class="sla-shift-ledger-button" data-action="open">Open</button>
|
||||
` : ""}
|
||||
<div class="sla-shift-ledger-banner-main" style="margin-top: 15px;">
|
||||
<div class="sla-shift-ledger-shifts" style="display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px;">
|
||||
${SHIFTS.map((shift, index) => `
|
||||
<div class="sla-shift-ledger-shift ${index === Number(state.shift) ? "is-active" : ""}" style="border-radius: 12px; border: 1px solid ${index === Number(state.shift) ? "rgba(124, 196, 255, 0.4)" : "rgba(255, 255, 255, 0.1)"}; background: ${index === Number(state.shift) ? "rgba(31, 93, 156, 0.3)" : "rgba(255, 255, 255, 0.04)"}; min-height: 48px; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 0.9rem;">
|
||||
${shift.short}
|
||||
</div>
|
||||
`).join("")}
|
||||
</div>
|
||||
<div class="sla-shift-ledger-banner-controls" style="margin-top: 15px; display: flex; gap: 8px; justify-content: flex-end;">
|
||||
${game.user?.isGM ? `
|
||||
<button type="button" class="sla-shift-ledger-button" data-action="prev"><i class="fas fa-arrow-left"></i> Prev</button>
|
||||
<button type="button" class="sla-shift-ledger-button" data-action="next">Next <i class="fas fa-arrow-right"></i></button>
|
||||
<button type="button" class="sla-shift-ledger-button" data-action="open"><i class="fas fa-cog"></i> Config</button>
|
||||
` : ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
return $(html);
|
||||
}
|
||||
|
||||
if (game.user?.isGM) {
|
||||
host.querySelector("[data-action='prev']")?.addEventListener("click", () => advanceShift(-1));
|
||||
host.querySelector("[data-action='next']")?.addEventListener("click", () => advanceShift(1));
|
||||
host.querySelector("[data-action='open']")?.addEventListener("click", () => SlaShiftLedgerApp.open());
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
if (game.user?.isGM) {
|
||||
html.find("[data-action='prev']").on("click", () => advanceShift(-1));
|
||||
html.find("[data-action='next']").on("click", () => advanceShift(1));
|
||||
html.find("[data-action='open']").on("click", () => SlaShiftLedgerApp.open());
|
||||
}
|
||||
}
|
||||
|
||||
static get activeApp() {
|
||||
return Object.values(ui.windows).find(app => app.constructor === SlaCampaignClockApp) ?? null;
|
||||
}
|
||||
|
||||
static open() {
|
||||
const active = SlaCampaignClockApp.activeApp;
|
||||
if (active) {
|
||||
active.render(true);
|
||||
active.maximize();
|
||||
return active;
|
||||
}
|
||||
const app = new SlaCampaignClockApp();
|
||||
app.render(true);
|
||||
return app;
|
||||
}
|
||||
|
||||
static toggle() {
|
||||
const active = SlaCampaignClockApp.activeApp;
|
||||
if (active) {
|
||||
active.close();
|
||||
} else {
|
||||
SlaCampaignClockApp.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderCampaignBanner() {
|
||||
const active = SlaCampaignClockApp.activeApp;
|
||||
if (active) {
|
||||
active.render(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +368,6 @@ class SlaShiftLedgerApp extends FormApplication {
|
||||
}
|
||||
|
||||
function installSceneButton(_app, html) {
|
||||
if (!game.user?.isGM) return;
|
||||
const root = getHtmlRoot(html);
|
||||
const directoryHeader = root?.querySelector(".directory-header");
|
||||
if (!directoryHeader) return;
|
||||
@@ -335,13 +377,25 @@ function installSceneButton(_app, html) {
|
||||
header.className = "header-actions sla-shift-ledger-fallback-actions";
|
||||
directoryHeader.appendChild(header);
|
||||
}
|
||||
if (!header || header.querySelector(".sla-shift-ledger-open")) return;
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "sla-shift-ledger-open";
|
||||
button.innerHTML = `<i class="fas fa-calendar-days"></i> Shift Ledger`;
|
||||
button.addEventListener("click", () => SlaShiftLedgerApp.open());
|
||||
header.appendChild(button);
|
||||
|
||||
if (!header.querySelector(".sla-campaign-clock-open")) {
|
||||
const clockBtn = document.createElement("button");
|
||||
clockBtn.type = "button";
|
||||
clockBtn.className = "sla-campaign-clock-open";
|
||||
clockBtn.style.marginRight = "4px";
|
||||
clockBtn.innerHTML = `<i class="fas fa-clock"></i> Campaign Clock`;
|
||||
clockBtn.addEventListener("click", () => SlaCampaignClockApp.toggle());
|
||||
header.appendChild(clockBtn);
|
||||
}
|
||||
|
||||
if (game.user?.isGM && !header.querySelector(".sla-shift-ledger-open")) {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "sla-shift-ledger-open";
|
||||
button.innerHTML = `<i class="fas fa-calendar-days"></i> Shift Ledger`;
|
||||
button.addEventListener("click", () => SlaShiftLedgerApp.open());
|
||||
header.appendChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
function installSceneButtonFromSidebarTab(app, html) {
|
||||
@@ -564,9 +618,10 @@ Hooks.once("ready", () => {
|
||||
getState,
|
||||
getConfig,
|
||||
advanceShift,
|
||||
applyWeeklyBilling
|
||||
applyWeeklyBilling,
|
||||
openClock: () => SlaCampaignClockApp.open()
|
||||
};
|
||||
renderCampaignBanner();
|
||||
SlaCampaignClockApp.open();
|
||||
});
|
||||
|
||||
Hooks.on("renderSceneDirectory", installSceneButton);
|
||||
|
||||
Reference in New Issue
Block a user