экспорт маппинга
This commit is contained in:
@@ -5951,8 +5951,14 @@ function openTimerQuickEditor(focusActionId = "") {
|
||||
function normalizeHockeyPlayer(component, row, side, index) {
|
||||
const props = component.props || {};
|
||||
const get = (fieldName, fallback = "") => formatValue(getByPath(row, props[fieldName]), fallback);
|
||||
const externalId = String(get("idField", "") || row?.external_id || row?.id || "");
|
||||
const databaseId = String(row?.db_id ?? row?.database_id ?? "");
|
||||
return {
|
||||
id: String(get("idField", `${side}-${index + 1}`)),
|
||||
// `id` остаётся external_id для совместимости со старым Mapping.
|
||||
// `dbId` — реальный PK hockey_players.id из нашей базы.
|
||||
id: externalId || `${side}-${index + 1}`,
|
||||
externalId,
|
||||
dbId: databaseId,
|
||||
side,
|
||||
number: get("numberField", ""),
|
||||
name: get("nameField", `Игрок ${index + 1}`),
|
||||
@@ -6028,6 +6034,9 @@ function openTimerQuickEditor(focusActionId = "") {
|
||||
selectedInfraction: null,
|
||||
selectedPreset: null,
|
||||
selectedEventId: null,
|
||||
// Последний выбранный для редактирования штраф остаётся общим, а
|
||||
// preview/Mapping-выбор хранится отдельно для левой и правой команды.
|
||||
selectedPreviewEventIds: { home: "", away: "" },
|
||||
penalties: [],
|
||||
history: [],
|
||||
historyOpen: false,
|
||||
@@ -6128,11 +6137,15 @@ function openTimerQuickEditor(focusActionId = "") {
|
||||
}
|
||||
|
||||
function hockeyPenaltyContext(event) {
|
||||
const playerExternalId = String(event.player?.externalId || event.player?.external_id || event.player?.id || "");
|
||||
const playerDbId = String(event.player?.dbId || event.player?.db_id || event.player?.database_id || event.player?.raw?.db_id || "");
|
||||
return {
|
||||
penalty_id: event.id,
|
||||
event_time: event.eventTime,
|
||||
event_time_ms: event.eventTimeMs,
|
||||
player: event.player ? clone(event.player) : null,
|
||||
player_id: playerExternalId,
|
||||
player_db_id: playerDbId,
|
||||
team_penalty: Boolean(event.teamPenalty),
|
||||
infraction: event.infraction ? clone(event.infraction) : null,
|
||||
preset: event.preset,
|
||||
@@ -6458,6 +6471,7 @@ function openTimerQuickEditor(focusActionId = "") {
|
||||
const side = String(event.player?.side || event.side || "").toLowerCase();
|
||||
const remainingOnSide = board.penalties.filter((item) => !item.finished && hockeyEventReady(item) && String(item.player?.side || item.side || "").toLowerCase() === side).length;
|
||||
if (board.selectedEventId === event.id) board.selectedEventId = null;
|
||||
if (board.selectedPreviewEventIds?.[side] === event.id) board.selectedPreviewEventIds[side] = "";
|
||||
persistHockeyBoard(component, board, true);
|
||||
refreshHockeyBoardNodes(component);
|
||||
rebalanceVmixPenaltyTargets({ force: true, hideUnused: true })
|
||||
@@ -6476,6 +6490,10 @@ function openTimerQuickEditor(focusActionId = "") {
|
||||
} else if (command === "remove") {
|
||||
board.penalties = board.penalties.filter((item) => item.id !== eventId);
|
||||
if (board.selectedEventId === eventId) board.selectedEventId = null;
|
||||
{
|
||||
const removedSide = String(event.player?.side || event.side || "").toLowerCase();
|
||||
if (board.selectedPreviewEventIds?.[removedSide] === eventId) board.selectedPreviewEventIds[removedSide] = "";
|
||||
}
|
||||
addHockeyHistory(
|
||||
component,
|
||||
board,
|
||||
@@ -7043,21 +7061,71 @@ function hockeyInfractionDropdown(component, runtime) {
|
||||
return field;
|
||||
}
|
||||
|
||||
function hockeyPenaltyPlayerIdentifiers(component, event) {
|
||||
const side = String(event.player?.side || event.side || "").toLowerCase();
|
||||
let externalId = String(
|
||||
event.player?.externalId
|
||||
|| event.player?.external_id
|
||||
|| event.player?.id
|
||||
|| event.player?.raw?.external_id
|
||||
|| event.player?.raw?.id
|
||||
|| ""
|
||||
);
|
||||
let dbId = String(
|
||||
event.player?.dbId
|
||||
|| event.player?.db_id
|
||||
|| event.player?.database_id
|
||||
|| event.player?.raw?.db_id
|
||||
|| event.player?.raw?.database_id
|
||||
|| ""
|
||||
);
|
||||
|
||||
// Для сохранённых удалений из старых сборок dbId мог отсутствовать.
|
||||
// В этом случае восстанавливаем его по актуальному составу из БД.
|
||||
if (externalId && !dbId && ["home", "away"].includes(side)) {
|
||||
const rosterPlayer = hockeyRoster(component, side).find((player) =>
|
||||
String(player.externalId || player.id || "") === externalId
|
||||
);
|
||||
if (rosterPlayer) {
|
||||
dbId = String(rosterPlayer.dbId || rosterPlayer.raw?.db_id || "");
|
||||
externalId = String(rosterPlayer.externalId || rosterPlayer.id || externalId);
|
||||
}
|
||||
}
|
||||
return { externalId, dbId };
|
||||
}
|
||||
|
||||
function hockeyPenaltyIsPreviewSelected(board, event) {
|
||||
const side = String(event.player?.side || event.side || "").toLowerCase();
|
||||
return ["home", "away"].includes(side)
|
||||
&& String(board.selectedPreviewEventIds?.[side] || "") === String(event.id || "");
|
||||
}
|
||||
|
||||
async function hockeySelectPenaltyForPreview(component, event) {
|
||||
const board = ensureHockeyBoardState(component);
|
||||
board.selectedEventId = event.id;
|
||||
if (!board.selectedPreviewEventIds || typeof board.selectedPreviewEventIds !== "object") {
|
||||
board.selectedPreviewEventIds = { home: "", away: "" };
|
||||
}
|
||||
|
||||
const side = String(event.player?.side || event.side || "").toLowerCase();
|
||||
if (["home", "away"].includes(side)) board.selectedPreviewEventIds[side] = event.id;
|
||||
|
||||
const teamId = side === "home"
|
||||
? String(getByPath(state.data, "hockey.selected_game.home.id") || getByPath(state.data, "hockey.selected_game.home.external_id") || "")
|
||||
: side === "away"
|
||||
? String(getByPath(state.data, "hockey.selected_game.away.id") || getByPath(state.data, "hockey.selected_game.away.external_id") || "")
|
||||
: "";
|
||||
const playerIds = hockeyPenaltyPlayerIdentifiers(component, event);
|
||||
const detail = {
|
||||
item_id: String(event.external_id || event.id || ""),
|
||||
value: String(event.external_id || event.id || ""),
|
||||
penalty_id: String(event.external_id || event.id || ""),
|
||||
player_id: String(event.player?.id || ""),
|
||||
// Старый player_id остаётся external_id, чтобы не ломать существующие SQL.
|
||||
player_id: playerIds.externalId,
|
||||
player_external_id: playerIds.externalId,
|
||||
player_db_id: playerIds.dbId,
|
||||
team_id: teamId,
|
||||
team_penalty: Boolean(event.teamPenalty),
|
||||
infraction_id: String(event.infraction?.id || ""),
|
||||
preset_id: String(event.preset || ""),
|
||||
side,
|
||||
@@ -7067,7 +7135,14 @@ async function hockeySelectPenaltyForPreview(component, event) {
|
||||
status: event.finished ? "finished" : event.running ? "running" : hockeyEventReady(event) ? "ready" : "draft",
|
||||
selected: true,
|
||||
};
|
||||
window.UIBuilderRuntime?.patchData?.({ hockey: { selected_penalty: detail } }, { render: false });
|
||||
|
||||
const sidePatch = ["home", "away"].includes(side) ? { [side]: detail } : {};
|
||||
window.UIBuilderRuntime?.patchData?.({
|
||||
hockey: {
|
||||
selected_penalty: detail,
|
||||
selected_penalties: sidePatch,
|
||||
}
|
||||
}, { render: false });
|
||||
refreshHockeyBoardNodes(component);
|
||||
|
||||
const gameId = hockeyTimerSelectedGameId();
|
||||
@@ -7075,24 +7150,45 @@ async function hockeySelectPenaltyForPreview(component, event) {
|
||||
emitInteraction(component, "penalty_selected", detail);
|
||||
return detail;
|
||||
}
|
||||
|
||||
const values = {
|
||||
selected_penalty_id: detail.penalty_id,
|
||||
selected_penalty_player_id: detail.player_id,
|
||||
selected_penalty_player_db_id: detail.player_db_id,
|
||||
selected_penalty_team_penalty: detail.team_penalty ? "1" : "0",
|
||||
selected_penalty_team_id: detail.team_id,
|
||||
selected_penalty_infraction_id: detail.infraction_id,
|
||||
selected_penalty_preset_id: detail.preset_id,
|
||||
selected_penalty_side: detail.side,
|
||||
selected_penalty_event_time: detail.event_time,
|
||||
selected_penalty_status: detail.status,
|
||||
selected_penalty_remaining_ms: String(detail.remaining_ms),
|
||||
selected_penalty_duration_ms: String(detail.duration_ms),
|
||||
selected_event_id: detail.penalty_id,
|
||||
};
|
||||
|
||||
if (side === "home") {
|
||||
Object.assign(values, {
|
||||
selected_home_penalty_id: detail.penalty_id,
|
||||
selected_home_penalty_player_id: detail.player_id,
|
||||
selected_home_penalty_player_db_id: detail.player_db_id,
|
||||
selected_home_penalty_team_penalty: detail.team_penalty ? "1" : "0",
|
||||
});
|
||||
} else if (side === "away") {
|
||||
Object.assign(values, {
|
||||
selected_away_penalty_id: detail.penalty_id,
|
||||
selected_away_penalty_player_id: detail.player_id,
|
||||
selected_away_penalty_player_db_id: detail.player_db_id,
|
||||
selected_away_penalty_team_penalty: detail.team_penalty ? "1" : "0",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/hockey/context/batch", {
|
||||
method: "POST", cache: "no-store", credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
values: {
|
||||
selected_penalty_id: detail.penalty_id,
|
||||
selected_penalty_player_id: detail.player_id,
|
||||
selected_penalty_team_id: detail.team_id,
|
||||
selected_penalty_infraction_id: detail.infraction_id,
|
||||
selected_penalty_preset_id: detail.preset_id,
|
||||
selected_penalty_side: detail.side,
|
||||
selected_penalty_event_time: detail.event_time,
|
||||
selected_penalty_status: detail.status,
|
||||
selected_penalty_remaining_ms: String(detail.remaining_ms),
|
||||
selected_penalty_duration_ms: String(detail.duration_ms),
|
||||
selected_event_id: detail.penalty_id,
|
||||
},
|
||||
values,
|
||||
context: {
|
||||
game_id: gameId,
|
||||
device_id: currentRuntimeVmixDeviceId(),
|
||||
@@ -7119,6 +7215,7 @@ function hockeyPenaltyCard(component, event, runtime) {
|
||||
const card = div(`hpd-team-penalty-card team-${side}`);
|
||||
card.dataset.penaltyId = event.id;
|
||||
card.classList.toggle("is-selected", board.selectedEventId === event.id);
|
||||
card.classList.toggle("is-preview-selected", hockeyPenaltyIsPreviewSelected(board, event));
|
||||
card.classList.toggle("is-running", event.running);
|
||||
card.classList.toggle("is-finished", event.finished);
|
||||
card.classList.toggle("is-ready", hockeyEventReady(event) && !event.running && !event.finished);
|
||||
@@ -7134,6 +7231,8 @@ function hockeyPenaltyCard(component, event, runtime) {
|
||||
const presetText = event.preset
|
||||
? `${escapeHtml(event.preset)} · ${escapeHtml(formatHockeyPenaltyTime(event.durationMs))}`
|
||||
: "Выберите длительность";
|
||||
const previewSelected = hockeyPenaltyIsPreviewSelected(board, event);
|
||||
const playerIds = hockeyPenaltyPlayerIdentifiers(component, event);
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="hpd-team-card-accent"></div>
|
||||
@@ -7165,9 +7264,18 @@ function hockeyPenaltyCard(component, event, runtime) {
|
||||
<span>Командное удаление — без игрока</span>
|
||||
</label>
|
||||
|
||||
${previewSelected ? `
|
||||
<div class="hpd-team-card-identifiers" title="Идентификаторы выбранного удаления для Mapping">
|
||||
<span><b>ID удаления</b>${escapeHtml(String(event.external_id || event.id || "—"))}</span>
|
||||
<span><b>Player external ID</b>${escapeHtml(playerIds.externalId || "—")}</span>
|
||||
<span><b>Player DB ID</b>${escapeHtml(playerIds.dbId || "—")}</span>
|
||||
<span><b>Командное</b>${event.teamPenalty ? "1" : "0"}</span>
|
||||
</div>
|
||||
` : ""}
|
||||
|
||||
<div class="hpd-team-card-footer">
|
||||
<span class="hpd-team-card-status">${
|
||||
board.selectedEventId === event.id
|
||||
previewSelected
|
||||
? "Выбрано · подсмотр"
|
||||
: event.finished
|
||||
? "Завершено"
|
||||
@@ -11116,7 +11224,25 @@ function renderHockeyPenaltyDashboard(node, component, runtime) {
|
||||
el.runtimeSizer.style.height = `${Math.round(canvasHeight * scale * 1000) / 1000}px`;
|
||||
el.runtimeStage.style.width = `${canvasWidth}px`;
|
||||
el.runtimeStage.style.height = `${canvasHeight}px`;
|
||||
el.runtimeStage.style.transform = `translate3d(0, 0, 0) scale(${scale})`;
|
||||
|
||||
// BUILD 70: scale the operator UI with CSS zoom when available. Unlike a
|
||||
// fractional transform: scale(), CSS zoom asks Chromium to lay out and
|
||||
// rasterize text/1px borders at the requested size instead of enlarging a
|
||||
// pre-rendered texture. This keeps every runtime tab visibly sharper.
|
||||
const canUseCssZoom = typeof CSS !== "undefined"
|
||||
&& typeof CSS.supports === "function"
|
||||
&& CSS.supports("zoom", "1");
|
||||
if (canUseCssZoom) {
|
||||
el.runtimeStage.style.zoom = String(scale);
|
||||
el.runtimeStage.style.transform = "none";
|
||||
el.runtimeStage.style.willChange = "auto";
|
||||
} else {
|
||||
// Old embedded browsers still get the previous responsive behaviour,
|
||||
// but avoid translate3d so we do not force an extra GPU texture layer.
|
||||
el.runtimeStage.style.zoom = "";
|
||||
el.runtimeStage.style.transform = `scale(${scale})`;
|
||||
el.runtimeStage.style.willChange = "transform";
|
||||
}
|
||||
el.runtimeStage.style.background = state.config.canvas.background || "#0c1421";
|
||||
el.runtimeStage.dataset.runtimeScale = String(scale);
|
||||
}
|
||||
@@ -12718,6 +12844,9 @@ function renderHockeyPenaltyDashboard(node, component, runtime) {
|
||||
state.modifierShortcutChordUsedKey = true;
|
||||
}
|
||||
if (editorHotkeyPressed(event)) {
|
||||
// In runtime the editor entry button is revealed only for admins by
|
||||
// the hockey account bootstrap. Hidden means editor access is denied.
|
||||
if (boot.mode !== "editor" && el.runtimeEditorBtn?.classList.contains("hidden")) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (boot.mode === "editor") window.location.href = boot.runtimeUrl;
|
||||
|
||||
@@ -156,12 +156,12 @@
|
||||
<header class="runtime-topbar">
|
||||
<div>
|
||||
<div class="eyebrow">RUNTIME INTERFACE</div>
|
||||
<div class="runtime-title-line"><strong id="runtimeTitle">Интерфейс</strong><small class="runtime-build-badge">BUILD 2026.08.19.24</small></div>
|
||||
<div class="runtime-title-line"><strong id="runtimeTitle">Интерфейс</strong><small class="runtime-build-badge">BUILD 2026.08.19.28</small></div>
|
||||
</div>
|
||||
<div id="runtimeTabs" class="runtime-tabs"></div>
|
||||
<div class="runtime-actions">
|
||||
<button id="runtimeEventsToggleBtn" class="icon-btn runtime-events-toggle is-on" type="button" title="Toast-уведомления включены для этого аккаунта" aria-label="Включить или выключить toast-уведомления">🔔</button>
|
||||
<button id="runtimeEditorBtn" class="icon-btn" type="button" title="Открыть конструктор (Ctrl+Shift+E)" aria-label="Открыть конструктор">✎</button>
|
||||
<button id="runtimeEditorBtn" class="icon-btn hidden" type="button" title="Открыть конструктор (Ctrl+Shift+E)" aria-label="Открыть конструктор">✎</button>
|
||||
<button id="runtimeShortcutsBtn" class="icon-btn" type="button" title="Шорткаты" aria-label="Шорткаты">⌨</button>
|
||||
<button id="quickTimersBtn" class="icon-btn" type="button" title="Быстрая настройка таймеров" aria-label="Быстрая настройка таймеров">⏱</button>
|
||||
<button id="runtimeAdminSettingsBtn" class="icon-btn hidden" type="button" title="Настройки базы данных" aria-label="Настройки базы данных">⚙</button>
|
||||
|
||||
55
ui_builder/static/runtime.html
Normal file
55
ui_builder/static/runtime.html
Normal file
@@ -0,0 +1,55 @@
|
||||
<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Хоккейный vMix</title>
|
||||
<link rel="icon" type="image/svg+xml" href="__UI_BUILDER_ASSETS__/favicon.svg?v=__UI_BUILDER_ASSET_VERSION__">
|
||||
<link rel="stylesheet" href="__UI_BUILDER_ASSETS__/styles.css?v=__UI_BUILDER_ASSET_VERSION__">
|
||||
__UI_BUILDER_CUSTOM_STYLES__
|
||||
<script>
|
||||
window.UI_BUILDER_BOOT = {
|
||||
api: "__UI_BUILDER_API__",
|
||||
authApi: "__UI_BUILDER_AUTH_API__",
|
||||
assets: "__UI_BUILDER_ASSETS__",
|
||||
editorUrl: "__UI_BUILDER_EDITOR__",
|
||||
runtimeUrl: "__UI_BUILDER_RUNTIME__",
|
||||
editorHotkey: "__UI_BUILDER_EDITOR_HOTKEY__",
|
||||
mode: "runtime"
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body class="runtime-entry">
|
||||
<section id="runtimeView" class="runtime-view runtime-only">
|
||||
<header class="runtime-topbar">
|
||||
<div>
|
||||
<div class="eyebrow">RUNTIME INTERFACE</div>
|
||||
<div class="runtime-title-line"><strong id="runtimeTitle">Интерфейс</strong><small class="runtime-build-badge">BUILD 2026.08.19.28</small></div>
|
||||
</div>
|
||||
<div id="runtimeTabs" class="runtime-tabs"></div>
|
||||
<div class="runtime-actions">
|
||||
<button id="runtimeEventsToggleBtn" class="icon-btn runtime-events-toggle is-on" type="button" title="Toast-уведомления включены для этого аккаунта" aria-label="Включить или выключить toast-уведомления">🔔</button>
|
||||
<button id="runtimeEditorBtn" class="icon-btn hidden" type="button" title="Открыть конструктор (Ctrl+Shift+E)" aria-label="Открыть конструктор">✎</button>
|
||||
<button id="runtimeShortcutsBtn" class="icon-btn" type="button" title="Шорткаты" aria-label="Шорткаты">⌨</button>
|
||||
<button id="quickTimersBtn" class="icon-btn" type="button" title="Быстрая настройка таймеров" aria-label="Быстрая настройка таймеров">⏱</button>
|
||||
<button id="runtimeAdminSettingsBtn" class="icon-btn hidden" type="button" title="Настройки базы данных" aria-label="Настройки базы данных">⚙</button>
|
||||
<button id="runtimeLogoutBtn" class="icon-btn runtime-logout-btn" type="button" title="Выйти из аккаунта" aria-label="Выйти из аккаунта">⎋</button>
|
||||
<button id="closePreviewBtn" class="icon-btn btn-accent preview-only hidden" type="button" title="Вернуться в конструктор" aria-label="Вернуться в конструктор">↩</button>
|
||||
</div>
|
||||
</header>
|
||||
<div id="runtimeViewport" class="runtime-viewport">
|
||||
<div id="runtimeSizer" class="runtime-sizer">
|
||||
<div id="runtimeStage" class="runtime-stage"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="runtimeButtonDock" class="runtime-button-dock hidden"></div>
|
||||
</section>
|
||||
|
||||
<div id="modalHost"></div>
|
||||
<div id="toast" class="toast"></div>
|
||||
<datalist id="dataPathList"></datalist>
|
||||
|
||||
<script src="__UI_BUILDER_ASSETS__/app.js?v=__UI_BUILDER_ASSET_VERSION__"></script>
|
||||
__UI_BUILDER_CUSTOM_SCRIPTS__
|
||||
</body>
|
||||
</html>
|
||||
@@ -173,7 +173,7 @@ h2 { margin: 0 0 10px; color: #cbd8ea; font-size: 12px; letter-spacing: .08em; t
|
||||
|
||||
/* Runtime and rendered controls */
|
||||
.runtime-view { width: 100%; height: 100vh; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: #080d15; }
|
||||
.runtime-topbar { min-height: 70px; display: grid; grid-template-columns: minmax(160px, .55fr) minmax(220px, 1fr) max-content; align-items: center; column-gap: 15px; row-gap: 8px; flex: 0 0 auto; padding: 9px 18px 0; border-bottom: 1px solid var(--line); background: rgba(8,13,21,.97); position: sticky; top: 0; z-index: 30; backdrop-filter: blur(16px); }
|
||||
.runtime-topbar { min-height: 70px; display: grid; grid-template-columns: minmax(160px, .55fr) minmax(220px, 1fr) max-content; align-items: center; column-gap: 15px; row-gap: 8px; flex: 0 0 auto; padding: 9px 18px 0; border-bottom: 1px solid var(--line); background: #080d15; position: sticky; top: 0; z-index: 30; }
|
||||
.runtime-actions { flex-wrap: nowrap; white-space: nowrap; }
|
||||
.runtime-actions > .hockey-selected-chip { display: none !important; }
|
||||
.runtime-topbar > div:first-child .eyebrow { font-size: 8px; }
|
||||
@@ -182,7 +182,16 @@ h2 { margin: 0 0 10px; color: #cbd8ea; font-size: 12px; letter-spacing: .08em; t
|
||||
.runtime-tab { min-height: 35px; padding: 0 13px; color: #9dafc7; background: #141f31; border: 1px solid var(--line); border-radius: 9px; }
|
||||
.runtime-tab.active { color: #06130f; background: var(--accent); border-color: transparent; font-weight: 800; }
|
||||
.runtime-viewport { min-height: 0; display: flex; align-items: center; justify-content: center; flex: 1 1 auto; overflow: hidden; padding: 10px; }
|
||||
.runtime-stage { position: relative; margin: 0 auto; transform-origin: top left; overflow: hidden; box-shadow: 0 18px 70px rgba(0,0,0,.36); }
|
||||
.runtime-stage {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
transform-origin: top left;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 18px 70px rgba(0,0,0,.36);
|
||||
/* Keep browser-native font rasterization in runtime mode. */
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: auto;
|
||||
}
|
||||
.runtime-component { position: absolute; overflow: hidden; }
|
||||
|
||||
.ui-box { width: 100%; height: 100%; overflow: hidden; }
|
||||
@@ -7208,3 +7217,37 @@ body.hockey-navigation-open .runtime-viewport.has-hockey-pbp { gap: 14px !import
|
||||
border-color: rgba(255, 112, 112, .42);
|
||||
background: rgba(126, 35, 47, .2);
|
||||
}
|
||||
|
||||
|
||||
/* BUILD71: side-specific penalty selection identifiers */
|
||||
.hpd-team-penalty-card.is-preview-selected {
|
||||
outline: 2px solid rgba(255, 255, 255, .72);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.hpd-team-card-identifiers {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 4px 10px;
|
||||
margin-top: 6px;
|
||||
padding: 7px 9px;
|
||||
border: 1px solid rgba(255, 255, 255, .12);
|
||||
border-radius: 7px;
|
||||
background: rgba(0, 0, 0, .16);
|
||||
font-size: 10px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.hpd-team-card-identifiers span {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hpd-team-card-identifiers b {
|
||||
display: block;
|
||||
margin-bottom: 1px;
|
||||
opacity: .58;
|
||||
font-size: 8px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .035em;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user