feat: consolidate legacy parity migration baseline

This commit is contained in:
2026-07-20 22:06:12 +09:00
parent 5eb4054120
commit 4a54977341
66 changed files with 5391 additions and 460 deletions

View File

@@ -6,6 +6,7 @@
const searchButton = document.getElementById("stock-search-button");
const stockResults = document.getElementById("stock-results");
const cutList = document.getElementById("cut-list");
const cutDragFeedback = document.getElementById("cut-drag-feedback");
const playlistRows = document.getElementById("playlist-rows");
const playlistDropZone = document.getElementById("playlist-drop-zone");
const playlistMoveUp = document.getElementById("playlist-move-up");
@@ -123,6 +124,7 @@
let operatorCatalogDraftKey = "";
let manualResultClickTimer = null;
let comparisonTargetClickTimer = null;
let themeResultClickTimer = null;
let manualViResultClickTimer = null;
let operatorCatalogStockClickTimer = null;
let pendingNamedPlaylistCommand = null;
@@ -357,6 +359,8 @@
treeFocusOwnedBeforeRender = categoryTree.contains(document.activeElement);
if (renderedTreeTabId) captureTreeUiState(renderedTreeTabId);
if (nextTabId !== renderedTreeTabId) {
clearDelayedClick(themeResultClickTimer);
themeResultClickTimer = null;
renderedTreeTabId = nextTabId;
pendingTreeResetTabs.clear();
if (nextTabId) pendingTreeResetTabs.add(nextTabId);
@@ -461,6 +465,14 @@
playout.isTakeOutCompletionPending === true || playout.phase !== "idle"));
}
function isCutSelectionLocked(state) {
const playout = state && state.playout;
return !state || state.isBusy === true || state.fixedSectionBatch != null ||
(playout && (playout.isBusy === true || playout.outcomeUnknown === true ||
playout.isPlayCompletionPending === true ||
playout.isTakeOutCompletionPending === true));
}
function dragDropPosition(event, element) {
const bounds = element.getBoundingClientRect();
return event.clientY < bounds.top + (bounds.height / 2) ? "before" : "after";
@@ -669,10 +681,15 @@
function clearCutDragGesture(releaseCapture) {
const gesture = cutDragGesture;
if (!gesture) return;
cutDragGesture = null;
gesture.element.classList.remove("dragging");
playlistDropZone.classList.remove("cut-drag-over");
cutDragFeedback.hidden = true;
cutDragFeedback.classList.remove("allowed");
delete cutDragFeedback.dataset.dropAllowed;
cutDragFeedback.style.removeProperty("left");
cutDragFeedback.style.removeProperty("top");
if (!gesture) return;
gesture.element.classList.remove("dragging");
if (releaseCapture !== false &&
typeof gesture.element.hasPointerCapture === "function" &&
gesture.element.hasPointerCapture(gesture.pointerId) &&
@@ -685,6 +702,21 @@
}
}
function showCutDragFeedback(event, allowed) {
cutDragFeedback.hidden = false;
cutDragFeedback.classList.toggle("allowed", allowed);
cutDragFeedback.dataset.dropAllowed = allowed ? "true" : "false";
const gap = 14;
const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight;
const width = cutDragFeedback.offsetWidth;
const height = cutDragFeedback.offsetHeight;
const left = Math.max(4, Math.min(event.clientX + gap, viewportWidth - width - 4));
const top = Math.max(4, Math.min(event.clientY + gap, viewportHeight - height - 4));
cutDragFeedback.style.left = Math.round(left) + "px";
cutDragFeedback.style.top = Math.round(top) + "px";
}
function beginCutDragGesture(element, event, position) {
clearCutDragGesture();
if (event.button !== 0) return;
@@ -719,9 +751,10 @@
}
if (gesture.started) {
event.preventDefault();
playlistDropZone.classList.toggle(
"cut-drag-over",
!playlistMutationLocked && pointInsideElement(playlistDropZone, event));
const dropAllowed = !playlistMutationLocked &&
pointInsideElement(playlistDropZone, event);
playlistDropZone.classList.toggle("cut-drag-over", dropAllowed);
showCutDragFeedback(event, dropAllowed);
}
}
@@ -790,7 +823,6 @@
element.append(number, label);
element.addEventListener("pointerdown", function (event) {
if (playlistMutationLocked) return event.preventDefault();
if (!cutSelectionEnabled) return event.preventDefault();
focusCutElement(element, false);
const position = pointerPosition(event);
@@ -802,9 +834,12 @@
// equality contract. Drag geometry keeps the raw fractional CSS point.
x: Math.round(position.x),
y: Math.round(position.y),
timestampMilliseconds: Math.max(0, Math.round(event.timeStamp))
timestampMilliseconds: Math.max(0, Math.round(event.timeStamp)),
allowAppend: !playlistMutationLocked
});
beginCutDragGesture(element, event, position);
if (!playlistMutationLocked) {
beginCutDragGesture(element, event, position);
}
});
element.addEventListener("pointermove", onCutPointerMove);
element.addEventListener("pointerup", onCutPointerUp);
@@ -816,7 +851,6 @@
// The native ListView consumes these keys. Home/End still bubble to the
// document-level MainForm KeyPreview equivalent for its playlist side effect.
event.preventDefault();
if (playlistMutationLocked) return;
if (!cutSelectionEnabled) return;
const target = cutKeyboardTarget(element, event.key);
focusCutElement(target, true);
@@ -831,7 +865,8 @@
function renderCuts(state) {
const cutDragLocked = isOperatorMutationLocked(state);
cutSelectionEnabled = Number.isInteger(state.selectedStockIndex) && !cutDragLocked;
cutSelectionEnabled = Number.isInteger(state.selectedStockIndex) &&
!isCutSelectionLocked(state);
const focusedCutIndex = state.cutRows.findIndex(function (row) {
return row.isFocused === true;
});
@@ -864,7 +899,9 @@
? 0
: -1;
element.draggable = false;
element.dataset.cutDragEnabled = cutSelectionEnabled ? "true" : "false";
element.dataset.cutDragEnabled = cutSelectionEnabled && !cutDragLocked
? "true"
: "false";
});
while (cutList.children.length > state.cutRows.length) {
@@ -876,7 +913,7 @@
}
cutList.addEventListener("pointerdown", function (event) {
if (event.target !== cutList || playlistMutationLocked) return;
if (event.target !== cutList || !cutSelectionEnabled) return;
send("clear-cut-selection", {});
});
@@ -4000,6 +4037,8 @@
const themeResult = event.target.closest("button[data-theme-result-id]");
if (themeResult && !themeResult.disabled) {
event.preventDefault();
clearDelayedClick(themeResultClickTimer);
themeResultClickTimer = null;
send("activate-theme-result", {
resultId: themeResult.dataset.themeResultId
});
@@ -4260,7 +4299,12 @@
}
const themeResult = event.target.closest("button[data-theme-result-id]");
if (themeResult) {
send("select-theme", { resultId: themeResult.dataset.themeResultId });
clearDelayedClick(themeResultClickTimer);
const resultId = themeResult.dataset.themeResultId;
themeResultClickTimer = window.setTimeout(function () {
themeResultClickTimer = null;
send("select-theme", { resultId: resultId });
}, 250);
return;
}
if (event.target.closest("button[data-theme-search]")) {