Refine BIM comment panel interactions

This commit is contained in:
CODEX 2026-06-20 19:40:32 +03:00
parent b7fa6b5fbd
commit 7d65070f59
3 changed files with 61 additions and 17 deletions

View File

@ -612,14 +612,24 @@ header {
.comment-list-item:hover, .comment-list-item:hover,
.comment-list-item.active { .comment-list-item.active {
border-color: color-mix(in srgb, var(--comment-target-active) 55%, var(--modal-border)); border-color: transparent;
background: color-mix(in srgb, var(--comment-target-active) 16%, var(--modal-focus)); background: var(--toolbar-bg-active, var(--comment-target-active));
color: var(--toolbar-bg-active-contrast, #15170f);
} }
.comment-list-item:active { .comment-list-item:active {
transform: translateY(1px); transform: translateY(1px);
} }
.comment-list-item:hover .comment-list-title,
.comment-list-item:hover .comment-list-body,
.comment-list-item:hover .comment-list-meta,
.comment-list-item.active .comment-list-title,
.comment-list-item.active .comment-list-body,
.comment-list-item.active .comment-list-meta {
color: inherit;
}
.comment-list-title { .comment-list-title {
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
@ -726,7 +736,7 @@ header {
.nodedc-dropdown-surface { .nodedc-dropdown-surface {
position: fixed; position: fixed;
z-index: 30041; z-index: 50000;
padding: 0.65rem; padding: 0.65rem;
border: 0; border: 0;
border-radius: 1.25rem; border-radius: 1.25rem;
@ -1225,7 +1235,7 @@ header {
} }
.nodedc-dropdown-surface.nodedc-select__menu { .nodedc-dropdown-surface.nodedc-select__menu {
z-index: 30060; z-index: 50010;
display: grid; display: grid;
gap: 2px; gap: 2px;
max-height: 232px; max-height: 232px;
@ -1843,7 +1853,7 @@ header {
} }
.bim-subitem-menu { .bim-subitem-menu {
z-index: 30070; z-index: 50020;
display: grid; display: grid;
gap: 0.18rem; gap: 0.18rem;
min-width: 17.25rem; min-width: 17.25rem;

View File

@ -554,7 +554,7 @@ const setCommentModalError = (message = "") => {
const bringFloatingWindowToFront = (id) => { const bringFloatingWindowToFront = (id) => {
if (!id) return; if (!id) return;
floatingWindowOrder = [...floatingWindowOrder.filter((item) => item !== id), id]; floatingWindowOrder = [...floatingWindowOrder.filter((item) => item !== id), id];
const zBase = 21000; const zBase = 30080;
floatingWindowOrder.forEach((item, index) => { floatingWindowOrder.forEach((item, index) => {
const element = floatingWindowElements.get(item); const element = floatingWindowElements.get(item);
if (element) element.style.zIndex = `${zBase + index * 10}`; if (element) element.style.zIndex = `${zBase + index * 10}`;
@ -566,7 +566,7 @@ const registerFloatingWindowElement = (id, element) => {
floatingWindowElements.set(id, element); floatingWindowElements.set(id, element);
if (!floatingWindowOrder.includes(id)) floatingWindowOrder.push(id); if (!floatingWindowOrder.includes(id)) floatingWindowOrder.push(id);
element.addEventListener("pointerdown", (event) => { element.addEventListener("pointerdown", (event) => {
if (event.button !== 0) return; if (event.pointerType === "mouse" && event.button !== 0) return;
bringFloatingWindowToFront(id); bringFloatingWindowToFront(id);
}, true); }, true);
bringFloatingWindowToFront(id); bringFloatingWindowToFront(id);
@ -666,6 +666,14 @@ const getCommentDisplayBody = (comment) => {
return lastMessage?.body || ""; return lastMessage?.body || "";
}; };
const syncCommentListActiveState = () => {
if (!commentsList) return;
commentsList.querySelectorAll(".comment-list-item").forEach((item) => {
const commentId = item.dataset.commentId;
item.classList.toggle("active", activeCommentId === commentId || hoverCommentId === commentId);
});
};
const renderCommentsPanel = () => { const renderCommentsPanel = () => {
if (!commentsList) return; if (!commentsList) return;
commentsList.replaceChildren(); commentsList.replaceChildren();
@ -697,12 +705,12 @@ const renderCommentsPanel = () => {
item.append(title, body, meta); item.append(title, body, meta);
item.addEventListener("mouseenter", () => { item.addEventListener("mouseenter", () => {
hoverCommentId = comment.id; hoverCommentId = comment.id;
renderCommentsPanel(); syncCommentListActiveState();
renderCommentTargets(); renderCommentTargets();
}); });
item.addEventListener("mouseleave", () => { item.addEventListener("mouseleave", () => {
if (hoverCommentId === comment.id) hoverCommentId = null; if (hoverCommentId === comment.id) hoverCommentId = null;
renderCommentsPanel(); syncCommentListActiveState();
renderCommentTargets(); renderCommentTargets();
}); });
item.addEventListener("click", () => openCommentModalForComment(comment.id)); item.addEventListener("click", () => openCommentModalForComment(comment.id));
@ -770,6 +778,17 @@ const stopCommentTargetLoop = () => {
} }
}; };
const syncCommentTargetActiveState = () => {
if (!commentTargetsLayer) return;
commentTargetsLayer.querySelectorAll(".comment-target").forEach((targetEl) => {
const commentId = targetEl.dataset.commentId;
targetEl.classList.toggle(
"active",
commentId === "__draft_comment__" || activeCommentId === commentId || hoverCommentId === commentId
);
});
};
const renderCommentTargets = () => { const renderCommentTargets = () => {
if (!commentTargetsLayer) return; if (!commentTargetsLayer) return;
commentTargetsLayer.replaceChildren(); commentTargetsLayer.replaceChildren();
@ -790,13 +809,13 @@ const renderCommentTargets = () => {
btn.innerHTML = COMMENT_TARGET_ICON; btn.innerHTML = COMMENT_TARGET_ICON;
btn.addEventListener("mouseenter", () => { btn.addEventListener("mouseenter", () => {
if (!comment.draft) hoverCommentId = comment.id; if (!comment.draft) hoverCommentId = comment.id;
renderCommentTargets(); syncCommentTargetActiveState();
renderCommentsPanel(); syncCommentListActiveState();
}); });
btn.addEventListener("mouseleave", () => { btn.addEventListener("mouseleave", () => {
if (hoverCommentId === comment.id) hoverCommentId = null; if (hoverCommentId === comment.id) hoverCommentId = null;
renderCommentTargets(); syncCommentTargetActiveState();
renderCommentsPanel(); syncCommentListActiveState();
}); });
btn.addEventListener("click", (event) => { btn.addEventListener("click", (event) => {
event.stopPropagation(); event.stopPropagation();
@ -1227,7 +1246,7 @@ const openCommentModalForTarget = () => {
renderCommentThread(null); renderCommentThread(null);
commentModalBackdrop?.classList.remove("hidden"); commentModalBackdrop?.classList.remove("hidden");
commentModal.classList.remove("hidden"); commentModal.classList.remove("hidden");
bringFloatingWindowToFront("comments"); bringFloatingWindowToFront("commentModal");
updateCommentsVisibility(); updateCommentsVisibility();
commentTitleInput?.focus(); commentTitleInput?.focus();
}; };
@ -1258,6 +1277,11 @@ const openCommentModalForComment = (commentId) => {
renderCommentThread(comment); renderCommentThread(comment);
commentModalBackdrop?.classList.remove("hidden"); commentModalBackdrop?.classList.remove("hidden");
commentModal.classList.remove("hidden"); commentModal.classList.remove("hidden");
commentsModeActive = true;
commentsCollapsed = false;
bringFloatingWindowToFront("commentModal");
updateCommentsVisibility();
commentTitleInput?.focus();
renderCommentsPanel(); renderCommentsPanel();
renderCommentTargets(); renderCommentTargets();
}; };
@ -1968,12 +1992,14 @@ const closeTemplatesPanel = () => {
const openSettingsPanel = () => { const openSettingsPanel = () => {
closeTemplatesPanel(); closeTemplatesPanel();
settingsMenu?.show(); settingsMenu?.show();
bringFloatingWindowToFront("settings");
setToolbarButtonActive(settingsButton, true); setToolbarButtonActive(settingsButton, true);
}; };
const openTemplatesPanel = () => { const openTemplatesPanel = () => {
closeSettingsPanel(); closeSettingsPanel();
templatesMenu?.show(); templatesMenu?.show();
bringFloatingWindowToFront("templates");
setToolbarButtonActive(templatesButton, true); setToolbarButtonActive(templatesButton, true);
}; };
@ -5164,6 +5190,7 @@ const loadProjectSnapshot = async (project) => {
fetchProjects: () => loadProjectsList(), fetchProjects: () => loadProjectsList(),
openProject: (projectId) => loadProjectFromServer(projectId), openProject: (projectId) => loadProjectFromServer(projectId),
onShow: () => { onShow: () => {
bringFloatingWindowToFront("templates");
setToolbarButtonActive(templatesButton, true); setToolbarButtonActive(templatesButton, true);
setToolbarButtonActive(settingsButton, false); setToolbarButtonActive(settingsButton, false);
}, },
@ -5197,6 +5224,7 @@ const loadProjectSnapshot = async (project) => {
focusCanvasAndControls(); focusCanvasAndControls();
}, },
onShow: () => { onShow: () => {
bringFloatingWindowToFront("settings");
setToolbarButtonActive(settingsButton, true); setToolbarButtonActive(settingsButton, true);
setToolbarButtonActive(templatesButton, false); setToolbarButtonActive(templatesButton, false);
closeTemplatesPanel(); closeTemplatesPanel();
@ -5269,9 +5297,15 @@ const loadProjectSnapshot = async (project) => {
zoomSpeedControl.replaceChildren(zoomSpeedSlider); zoomSpeedControl.replaceChildren(zoomSpeedSlider);
} }
inspectorCollapseButton?.addEventListener("click", () => setInspectorCollapsed(true)); inspectorCollapseButton?.addEventListener("click", () => setInspectorCollapsed(true));
inspectorRestoreButton?.addEventListener("click", () => setInspectorCollapsed(false)); inspectorRestoreButton?.addEventListener("click", () => {
setInspectorCollapsed(false);
bringFloatingWindowToFront("inspector");
});
registerFloatingWindowElement("inspector", sidePanelEl); registerFloatingWindowElement("inspector", sidePanelEl);
registerFloatingWindowElement("comments", commentsPanel); registerFloatingWindowElement("comments", commentsPanel);
registerFloatingWindowElement("settings", settingsPanel);
registerFloatingWindowElement("templates", templatesPanel);
registerFloatingWindowElement("commentModal", commentModal);
setupDraggableFixedWindow(commentsPanel, commentsPanelHeader); setupDraggableFixedWindow(commentsPanel, commentsPanelHeader);
setupDraggableFixedWindow(commentModal, commentModalHeader); setupDraggableFixedWindow(commentModal, commentModalHeader);
commentsCollapseButton?.addEventListener("click", () => setCommentsCollapsed(true)); commentsCollapseButton?.addEventListener("click", () => setCommentsCollapsed(true));

View File

@ -134,7 +134,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./dcViewer.css?v=26"> <link rel="stylesheet" href="./dcViewer.css?v=27">
</head> </head>
<body> <body>
<header> <header>
@ -570,6 +570,6 @@
<input id="fileInput" class="hidden" type="file" <input id="fileInput" class="hidden" type="file"
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl"> accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
<script type="module" src="./dcViewer.js?v=37"></script> <script type="module" src="./dcViewer.js?v=40"></script>
</body> </body>
</html> </html>