162 lines
8.7 KiB
JavaScript
162 lines
8.7 KiB
JavaScript
"use strict";
|
|
|
|
(function (global) {
|
|
const htmlEscape = global.TarinaiUIHelpers.htmlEscape;
|
|
|
|
function spritePathFor(type = "smile") {
|
|
const sprites = (typeof SPRITES !== "undefined" ? SPRITES : (global.SPRITES || []));
|
|
const meta = sprites.find(s => s.id === type) || sprites[0];
|
|
return meta?.path || "assets/sprites/tarinai_01_smile.webp";
|
|
}
|
|
|
|
function ensureStyles() {
|
|
if (document.getElementById("freezeSystemStyles")) return;
|
|
const style = document.createElement("style");
|
|
style.id = "freezeSystemStyles";
|
|
style.textContent = `
|
|
.frozen-panel { grid-column: 1 / -1; margin-top: 8px; padding: 7px 8px; border: 1px solid rgba(82, 63, 43, 0.18); border-radius: 14px; background: rgba(244, 249, 255, 0.72); min-width: 0; }
|
|
.frozen-panel-title { display:flex; align-items:center; justify-content:space-between; gap:8px; font-weight:800; font-size:12px; color:#4f5f70; margin-bottom:5px; }
|
|
.frozen-panel-toggle { width:100%; border:0; background:transparent; color:inherit; font:inherit; display:flex; align-items:center; justify-content:space-between; gap:8px; padding:0; cursor:pointer; text-align:left; }
|
|
.frozen-panel-toggle::after { content:"\u6298\u308a\u7573\u307f"; font-size:11px; opacity:.62; font-weight:700; }
|
|
.frozen-panel.collapsed .frozen-panel-toggle::after { content:"\u5c55\u958b"; }
|
|
.frozen-panel-count { font-size:12px; opacity:.72; }
|
|
.frozen-list { display:flex; gap:6px; overflow-x:auto; padding-bottom:2px; }
|
|
.frozen-panel.collapsed .frozen-list { display:none; }
|
|
.frozen-panel.collapsed { padding-bottom:7px; }
|
|
.frozen-card { display:flex; align-items:center; gap:6px; flex:0 0 138px; min-width:138px; max-width:168px; border:1px solid rgba(72, 98, 124, .20); border-radius:11px; padding:5px 6px; background:rgba(255,255,255,.78); cursor:grab; color:#354657; text-align:left; box-shadow: 0 2px 7px rgba(41,53,65,.06); }
|
|
.frozen-card:active { cursor:grabbing; }
|
|
.frozen-card.pending-thaw { outline:2px solid rgba(70, 142, 210, .46); background:rgba(227, 242, 255, .86); }
|
|
.frozen-card img { width:26px; height:26px; object-fit:contain; flex:none; filter: drop-shadow(0 2px 3px rgba(34,44,55,.14)); }
|
|
.frozen-card-main { min-width:0; flex:1; }
|
|
.frozen-card-name { display:block; font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
.frozen-card-meta { display:block; font-size:11px; opacity:.70; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
.frozen-empty { font-size:12px; color:rgba(58,70,82,.68); padding:4px 2px; }
|
|
canvas.freeze-drop-ready { outline: 3px solid rgba(70, 142, 210, .45); outline-offset: -3px; }
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
function ensurePanel() {
|
|
ensureStyles();
|
|
let panel = document.getElementById("frozenPanel");
|
|
if (panel) return panel;
|
|
panel = document.createElement("section");
|
|
panel.id = "frozenPanel";
|
|
panel.className = "frozen-panel";
|
|
panel.innerHTML = `
|
|
<div class="frozen-panel-title"><button id="frozenPanelToggle" class="frozen-panel-toggle" type="button" aria-expanded="false"><span>\u51b7\u51cd\u5eab</span><span id="frozenPanelCount" class="frozen-panel-count">0</span></button></div>
|
|
<div id="frozenList" class="frozen-list"></div>
|
|
`;
|
|
const storedCollapse = global.localStorage?.getItem("tarinaiFrozenPanelCollapsed");
|
|
const collapsedByDefault = storedCollapse !== "0";
|
|
if (collapsedByDefault) {
|
|
panel.classList.add("collapsed");
|
|
panel.querySelector("#frozenPanelToggle")?.setAttribute("aria-expanded", "false");
|
|
} else {
|
|
panel.querySelector("#frozenPanelToggle")?.setAttribute("aria-expanded", "true");
|
|
}
|
|
const operateBody = document.querySelector('[data-tool-category="operate"] .tool-category-body');
|
|
const anchor = operateBody || global.ui?.toolPalette || document.getElementById("toolPalette");
|
|
if (operateBody) operateBody.appendChild(panel);
|
|
else if (anchor?.parentNode) anchor.parentNode.insertBefore(panel, anchor.nextSibling);
|
|
else document.body.appendChild(panel);
|
|
return panel;
|
|
}
|
|
|
|
function render(entries = []) {
|
|
const panel = ensurePanel();
|
|
const listEl = panel.querySelector("#frozenList");
|
|
const countEl = panel.querySelector("#frozenPanelCount");
|
|
const list = Array.isArray(entries) ? entries : [];
|
|
if (countEl) countEl.textContent = `${list.length}`;
|
|
if (!listEl) return;
|
|
if (!list.length) {
|
|
listEl.innerHTML = `<div class="frozen-empty">\u51b7\u51cd\u4e2d\u306e\u305f\u308a\u306a\u3044\u306f\u3044\u307e\u305b\u3093\u3002\u51b7\u51cd\u3057\u305f\u305f\u308a\u306a\u3044\u306f\u30ea\u30bb\u30c3\u30c8\u3092\u7121\u8996\u3057\u3066\u4fdd\u5b58\u3067\u304d\u307e\u3059\u3002</div>`;
|
|
return;
|
|
}
|
|
listEl.innerHTML = list.map(entry => {
|
|
const data = entry.data || entry;
|
|
const gen = data.generation ? `\u7b2c${data.generation}\u4e16\u4ee3` : "";
|
|
const id = htmlEscape(entry.freezeId || entry.id || data.familyKey || "");
|
|
return `<button class="frozen-card" type="button" draggable="true" data-frozen-id="${id}" title="\u30c9\u30e9\u30c3\u30b0\u3057\u3066\u30d5\u30a3\u30fc\u30eb\u30c9\u306b\u623b\u3059">
|
|
<img src="${htmlEscape(spritePathFor(data.type || "smile"))}" alt="">
|
|
<span class="frozen-card-main"><span class="frozen-card-name">${htmlEscape(data.name || "\u305f\u308a\u306a\u3044")}</span><span class="frozen-card-meta">${htmlEscape(gen)}</span></span>
|
|
</button>`;
|
|
}).join("");
|
|
}
|
|
|
|
function markPending(id = "") {
|
|
const panel = ensurePanel();
|
|
for (const c of panel.querySelectorAll(".frozen-card")) c.classList.toggle("pending-thaw", c.dataset.frozenId === id);
|
|
}
|
|
|
|
function clearPending() {
|
|
document.querySelectorAll(".frozen-card.pending-thaw").forEach(el => el.classList.remove("pending-thaw"));
|
|
}
|
|
|
|
function bind({ onSelect, onDrop } = {}) {
|
|
const panel = ensurePanel();
|
|
if (panel.dataset.freezeBound !== "1") {
|
|
panel.dataset.freezeBound = "1";
|
|
panel.addEventListener("dragstart", (e) => {
|
|
const card = e.target.closest(".frozen-card[data-frozen-id]");
|
|
if (!card) return;
|
|
const id = card.dataset.frozenId || "";
|
|
e.dataTransfer?.setData("text/plain", `tarinai-frozen:${id}`);
|
|
e.dataTransfer?.setData("application/x-tarinai-frozen", id);
|
|
e.dataTransfer.effectAllowed = "move";
|
|
global.document?.querySelector("canvas")?.classList.add("freeze-drop-ready");
|
|
});
|
|
panel.addEventListener("dragend", () => global.document?.querySelector("canvas")?.classList.remove("freeze-drop-ready"));
|
|
panel.addEventListener("click", (e) => {
|
|
const toggle = e.target.closest("#frozenPanelToggle");
|
|
if (toggle) {
|
|
const collapsed = panel.classList.toggle("collapsed");
|
|
toggle.setAttribute("aria-expanded", collapsed ? "false" : "true");
|
|
try { global.localStorage?.setItem("tarinaiFrozenPanelCollapsed", collapsed ? "1" : "0"); } catch (_) {}
|
|
global.audio?.uiFold?.();
|
|
return;
|
|
}
|
|
const card = e.target.closest(".frozen-card[data-frozen-id]");
|
|
if (!card) return;
|
|
const id = card.dataset.frozenId || "";
|
|
markPending(id);
|
|
onSelect?.(id);
|
|
});
|
|
}
|
|
const canvas = global.canvas || document.getElementById("gameCanvas");
|
|
if (canvas && canvas.dataset.freezeDropBound !== "1") {
|
|
canvas.dataset.freezeDropBound = "1";
|
|
canvas.addEventListener("dragover", (e) => {
|
|
const text = e.dataTransfer?.types?.includes?.("application/x-tarinai-frozen") || [...(e.dataTransfer?.types || [])].includes("text/plain");
|
|
if (!text) return;
|
|
e.preventDefault();
|
|
e.dataTransfer.dropEffect = "move";
|
|
canvas.classList.add("freeze-drop-ready");
|
|
});
|
|
canvas.addEventListener("dragleave", () => canvas.classList.remove("freeze-drop-ready"));
|
|
canvas.addEventListener("drop", (e) => {
|
|
const id = e.dataTransfer?.getData("application/x-tarinai-frozen") || String(e.dataTransfer?.getData("text/plain") || "").replace(/^tarinai-frozen:/, "");
|
|
if (!id) return;
|
|
e.preventDefault();
|
|
canvas.classList.remove("freeze-drop-ready");
|
|
const p = typeof global.screenToWorld === "function" ? global.screenToWorld(e) : (() => {
|
|
const rect = canvas.getBoundingClientRect();
|
|
const sx = e.clientX - rect.left;
|
|
const sy = e.clientY - rect.top;
|
|
const worldRef = global.world;
|
|
return worldRef?.screenToWorld ? worldRef.screenToWorld(sx, sy) : { x: sx, y: sy, inside: true };
|
|
})();
|
|
onDrop?.(id, p.x, p.y);
|
|
});
|
|
}
|
|
}
|
|
|
|
global.TarinaiFreezePanel = {
|
|
ensurePanel,
|
|
render,
|
|
bind,
|
|
markPending,
|
|
clearPending,
|
|
};
|
|
})(typeof window !== "undefined" ? window : globalThis);
|