перетягивание внутри drap-and-drop блоков и поправлена настройка панели кнопок с блоками
This commit is contained in:
@@ -122,6 +122,7 @@
|
||||
hockeyTimerDirty: false,
|
||||
hockeyTimerRevision: 0,
|
||||
hockeyDragPlayer: null,
|
||||
hockeyPlayerSelectionDrag: null,
|
||||
hockeyDragInfraction: null,
|
||||
hockeyDragPreset: null,
|
||||
editorAuthTimer: null,
|
||||
@@ -7648,6 +7649,7 @@ function openHockeyPrematchButtonsEditor() {
|
||||
let dragButtonId = "";
|
||||
let dragSelectorId = "";
|
||||
let activeEditorGroupId = String(state.quickPanelActiveTab || "");
|
||||
let activeEditorSection = "buttons";
|
||||
if (activeEditorGroupId === "__ungrouped") activeEditorGroupId = "";
|
||||
if (activeEditorGroupId && !draftGroups.some((group) => group.id === activeEditorGroupId)) activeEditorGroupId = draftGroups[0]?.id || "";
|
||||
const collapsedEditorGroups = new Set();
|
||||
@@ -7755,22 +7757,31 @@ function openHockeyPrematchButtonsEditor() {
|
||||
draftPlayerPanels = normalizePlayerSelectionPanels(draftPlayerPanels);
|
||||
showSettingsModal("Нижняя панель · кнопки", `
|
||||
<div class="prematch-editor">
|
||||
<div class="prematch-editor-head">
|
||||
<p>Создавайте вкладки, кнопки и маленькие параметры рядом с кнопками. Значение параметра попадает в Mapping/SQL до запуска Shortcut Sequence.</p>
|
||||
<div class="prematch-editor-head-actions">
|
||||
<button type="button" class="btn" data-prematch-add-group>+ Вкладка</button>
|
||||
<button type="button" class="btn" data-quick-selector-add>+ Переключатель</button>
|
||||
<button type="button" class="btn btn-accent" data-prematch-add>+ Кнопка</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prematch-editor-list">
|
||||
${renderGroup(null, -1)}
|
||||
${draftGroups.map((group, index) => renderGroup(group, index)).join("")}
|
||||
</div>
|
||||
<section class="player-selection-editor">
|
||||
<header><div><span>DRAG & DROP ИГРОКОВ</span><strong>Блоки выбора игроков</strong><small>Название ID автоматически формирует идентификаторы Mapping: player_select.<ID>.player1_id и т. д.</small></div><button type="button" class="btn" data-player-panel-add>+ Блок игроков</button></header>
|
||||
<div class="player-selection-editor-list">${draftPlayerPanels.length ? draftPlayerPanels.map(renderPlayerPanelRow).join("") : `<div class="prematch-group-empty">Блоков игроков пока нет</div>`}</div>
|
||||
</section>
|
||||
<nav class="prematch-editor-tabs" aria-label="Раздел настроек нижней панели">
|
||||
<button type="button" class="prematch-editor-tab ${activeEditorSection === "buttons" ? "active" : ""}" data-prematch-editor-tab="buttons">Кнопки</button>
|
||||
<button type="button" class="prematch-editor-tab ${activeEditorSection === "players" ? "active" : ""}" data-prematch-editor-tab="players">Блоки игроков <span>${draftPlayerPanels.length}</span></button>
|
||||
</nav>
|
||||
${activeEditorSection === "buttons" ? `
|
||||
<section class="prematch-editor-section" data-prematch-editor-section="buttons">
|
||||
<div class="prematch-editor-head">
|
||||
<p>Создавайте вкладки, кнопки и маленькие параметры рядом с кнопками. Значение параметра попадает в Mapping/SQL до запуска Shortcut Sequence.</p>
|
||||
<div class="prematch-editor-head-actions">
|
||||
<button type="button" class="btn" data-prematch-add-group>+ Вкладка</button>
|
||||
<button type="button" class="btn" data-quick-selector-add>+ Переключатель</button>
|
||||
<button type="button" class="btn btn-accent" data-prematch-add>+ Кнопка</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prematch-editor-list">
|
||||
${renderGroup(null, -1)}
|
||||
${draftGroups.map((group, index) => renderGroup(group, index)).join("")}
|
||||
</div>
|
||||
</section>
|
||||
` : `
|
||||
<section class="player-selection-editor prematch-editor-section" data-prematch-editor-section="players">
|
||||
<header><div><span>DRAG & DROP ИГРОКОВ</span><strong>Блоки выбора игроков</strong><small>Создание сравнений, «3 звезды» и одиночных выборов. ID автоматически формирует Mapping: player_select.<ID>.player1_id и т. д.</small></div><button type="button" class="btn" data-player-panel-add>+ Блок игроков</button></header>
|
||||
<div class="player-selection-editor-list">${draftPlayerPanels.length ? draftPlayerPanels.map(renderPlayerPanelRow).join("") : `<div class="prematch-group-empty">Блоков игроков пока нет</div>`}</div>
|
||||
</section>
|
||||
`}
|
||||
<div class="prematch-editor-actions">
|
||||
<button type="button" class="btn" data-prematch-cancel>Отмена</button>
|
||||
<button type="button" class="btn btn-accent" data-prematch-save>Сохранить и опубликовать</button>
|
||||
@@ -7778,6 +7789,13 @@ function openHockeyPrematchButtonsEditor() {
|
||||
</div>
|
||||
`, { className: "modal-prematch-full" });
|
||||
|
||||
document.querySelectorAll("[data-prematch-editor-tab]").forEach((button) => button.addEventListener("click", () => {
|
||||
const section = String(button.dataset.prematchEditorTab || "buttons");
|
||||
if (!["buttons", "players"].includes(section) || section === activeEditorSection) return;
|
||||
activeEditorSection = section;
|
||||
renderEditor();
|
||||
}));
|
||||
|
||||
document.querySelector("[data-prematch-add-group]")?.addEventListener("click", () => {
|
||||
if (draftGroups.length >= 24) return toast("Можно создать до 24 вкладок", true);
|
||||
const newGroup = { id: `group_${draftGroups.length + 1}`, label: `Вкладка ${draftGroups.length + 1}`, sort_order: draftGroups.length * 10, enabled: true };
|
||||
@@ -8363,7 +8381,7 @@ function hockeyPlayerSelectionTeamId(player) {
|
||||
if (!player) return "";
|
||||
const side = player.side === "away" ? "away" : player.side === "home" ? "home" : "";
|
||||
const raw = player.raw || {};
|
||||
const direct = raw.team_external_id || raw.team_id || raw.club_external_id || raw.club_id || raw.team?.external_id || raw.team?.id || "";
|
||||
const direct = player.teamId || player.team_id || raw.team_external_id || raw.team_id || raw.club_external_id || raw.club_id || raw.team?.external_id || raw.team?.id || "";
|
||||
if (direct) return String(direct);
|
||||
if (!side) return "";
|
||||
const team = getByPath(state.data, `hockey.selected_game.${side}`) || {};
|
||||
@@ -8456,6 +8474,27 @@ async function hockeySetPlayerSelectionSlot(panel, slot, player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function hockeySwapPlayerSelectionSlots(panel, fromSlot, toSlot) {
|
||||
const sourceSlot = clamp(Number(fromSlot) || 1, 1, panel.slots);
|
||||
const targetSlot = clamp(Number(toSlot) || 1, 1, panel.slots);
|
||||
if (sourceSlot === targetSlot) return false;
|
||||
const source = hockeyPlayerSelectionSlot(panel, sourceSlot);
|
||||
if (!source.id && !source.dbId) return false;
|
||||
const target = hockeyPlayerSelectionSlot(panel, targetSlot);
|
||||
const sourcePlayer = { externalId: source.id, dbId: source.dbId, teamId: source.teamId, side: source.side, number: source.number, name: source.name };
|
||||
const targetPlayer = (target.id || target.dbId)
|
||||
? { externalId: target.id, dbId: target.dbId, teamId: target.teamId, side: target.side, number: target.number, name: target.name }
|
||||
: null;
|
||||
const patch = {
|
||||
...hockeyPlayerSelectionPatch(panel, targetSlot, sourcePlayer),
|
||||
...hockeyPlayerSelectionPatch(panel, sourceSlot, targetPlayer),
|
||||
};
|
||||
await hockeySetMatchValues(patch, { refreshMapping: true });
|
||||
if (panel.sync_selected_player) await hockeySyncLegacySelectedPlayer(sourcePlayer, source.teamId);
|
||||
toast(targetPlayer ? `${panel.label}: игроки ${sourceSlot} и ${targetSlot} поменяны местами` : `${panel.label}: игрок перемещён ${sourceSlot} → ${targetSlot}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function hockeyClearPlayerSelectionSlot(panel, slot) {
|
||||
await hockeySetMatchValues(hockeyPlayerSelectionPatch(panel, slot, null), { refreshMapping: true });
|
||||
if (panel.sync_selected_player) await hockeySyncLegacySelectedPlayer(null, "");
|
||||
@@ -8569,10 +8608,10 @@ function renderHockeyPlayerSelectionWindows() {
|
||||
const sideLabel = slot.side === "home" ? "HOME" : slot.side === "away" ? "AWAY" : "";
|
||||
const title = [slot.number ? `#${slot.number}` : "", slot.name].filter(Boolean).join(" ") || (filledSlot ? `Игрок ${number}` : "Перетащите игрока");
|
||||
const ids = filledSlot ? [sideLabel, slot.id ? `ID ${slot.id}` : "", slot.dbId ? `DB ${slot.dbId}` : ""].filter(Boolean).join(" · ") : "из состава слева или справа";
|
||||
return `<div class="hockey-player-select-slot ${filledSlot ? "is-filled" : ""}" data-player-panel-slot="${number}" data-player-panel-id="${escapeHtml(panel.id)}">
|
||||
return `<div class="hockey-player-select-slot ${filledSlot ? "is-filled" : ""}" data-player-panel-slot="${number}" data-player-panel-id="${escapeHtml(panel.id)}" ${filledSlot ? `draggable="true" title="Перетащите на другой слот, чтобы поменять игроков местами"` : ""}>
|
||||
<span class="hockey-player-select-index">${number}</span>
|
||||
<div><strong>${escapeHtml(title)}</strong><small>${escapeHtml(ids)}</small></div>
|
||||
${filledSlot ? `<button type="button" data-player-panel-clear-slot="${number}" data-player-panel-id="${escapeHtml(panel.id)}" title="Очистить слот">×</button>` : `<i>DROP</i>`}
|
||||
${filledSlot ? `<span class="hockey-player-select-drag" aria-hidden="true">⋮⋮</span><button type="button" data-player-panel-clear-slot="${number}" data-player-panel-id="${escapeHtml(panel.id)}" title="Очистить слот">×</button>` : `<i>DROP</i>`}
|
||||
</div>`;
|
||||
}).join("")}
|
||||
</div>
|
||||
@@ -8584,7 +8623,29 @@ function renderHockeyPlayerSelectionWindows() {
|
||||
renderRuntime();
|
||||
});
|
||||
node.querySelectorAll("[data-player-panel-slot]").forEach((slotNode) => {
|
||||
slotNode.addEventListener("dragstart", (event) => {
|
||||
const slot = Number(slotNode.dataset.playerPanelSlot || 1);
|
||||
const current = hockeyPlayerSelectionSlot(panel, slot);
|
||||
if (!current.id && !current.dbId) { event.preventDefault(); return; }
|
||||
state.hockeyPlayerSelectionDrag = { panelId: panel.id, slot };
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
event.dataTransfer.setData("application/x-ui-builder-hockey-player-selection-slot", JSON.stringify(state.hockeyPlayerSelectionDrag));
|
||||
event.dataTransfer.setData("text/plain", current.name || current.id || `player-${slot}`);
|
||||
slotNode.classList.add("is-dragging");
|
||||
});
|
||||
slotNode.addEventListener("dragend", () => {
|
||||
state.hockeyPlayerSelectionDrag = null;
|
||||
slotNode.classList.remove("is-dragging");
|
||||
node.querySelectorAll(".hockey-player-select-slot.is-over").forEach((item) => item.classList.remove("is-over"));
|
||||
});
|
||||
slotNode.addEventListener("dragover", (event) => {
|
||||
const internal = state.hockeyPlayerSelectionDrag;
|
||||
if (internal && internal.panelId === panel.id) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
slotNode.classList.add("is-over");
|
||||
return;
|
||||
}
|
||||
const player = state.hockeyDragPlayer || readHockeyDragData(event, "player");
|
||||
if (!player) return;
|
||||
event.preventDefault();
|
||||
@@ -8595,9 +8656,17 @@ function renderHockeyPlayerSelectionWindows() {
|
||||
slotNode.addEventListener("drop", async (event) => {
|
||||
event.preventDefault();
|
||||
slotNode.classList.remove("is-over");
|
||||
const targetSlot = Number(slotNode.dataset.playerPanelSlot || 1);
|
||||
const internal = state.hockeyPlayerSelectionDrag;
|
||||
if (internal && internal.panelId === panel.id) {
|
||||
const sourceSlot = Number(internal.slot || 1);
|
||||
state.hockeyPlayerSelectionDrag = null;
|
||||
await hockeySwapPlayerSelectionSlots(panel, sourceSlot, targetSlot);
|
||||
return;
|
||||
}
|
||||
const player = state.hockeyDragPlayer || readHockeyDragData(event, "player");
|
||||
if (!player) return;
|
||||
await hockeySetPlayerSelectionSlot(panel, Number(slotNode.dataset.playerPanelSlot || 1), player);
|
||||
await hockeySetPlayerSelectionSlot(panel, targetSlot, player);
|
||||
});
|
||||
});
|
||||
node.querySelectorAll("[data-player-panel-clear-slot]").forEach((button) => button.addEventListener("click", async (event) => {
|
||||
|
||||
Reference in New Issue
Block a user