Refine BIM comment modal glass and resize

This commit is contained in:
CODEX 2026-06-20 20:04:59 +03:00
parent 7d65070f59
commit 778a56ea39
3 changed files with 195 additions and 10 deletions

View File

@ -720,11 +720,8 @@ header {
display: block;
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 1.7;
stroke-linecap: round;
stroke-linejoin: round;
fill: currentColor;
stroke: none;
filter: drop-shadow(0 5px 12px rgba(0, 0, 0, 0.48));
overflow: visible;
}
@ -1491,6 +1488,10 @@ header {
#commentModalBackdrop {
z-index: 30045;
background: transparent;
backdrop-filter: none;
-webkit-backdrop-filter: none;
pointer-events: none;
}
.templates-panel.measurement-modal {
@ -1632,7 +1633,7 @@ header {
position: fixed;
--env-control-h: 42px;
--ops-comment-accent: var(--comment-target-active, #c4ff67);
--ops-detail-bg: rgba(18, 18, 20, 0.96);
--ops-detail-bg: color-mix(in srgb, var(--modal-bg) var(--modal-bg-alpha, 100%), transparent);
--ops-detail-surface: rgba(28, 28, 31, 0.76);
--ops-detail-surface-strong: rgba(32, 32, 35, 0.92);
--ops-detail-border: rgba(255, 255, 255, 0.055);
@ -1647,7 +1648,6 @@ header {
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 30px;
background:
radial-gradient(circle at 80% 20%, rgba(130, 58, 120, 0.13), transparent 34%),
linear-gradient(180deg, rgba(255, 255, 255, 0.018), rgba(255, 255, 255, 0.006)),
var(--ops-detail-bg);
box-shadow: 0 28px 80px rgba(0, 0, 0, 0.64);
@ -1669,6 +1669,81 @@ header {
display: none;
}
.window-resize-handle {
position: absolute;
z-index: 4;
background: transparent;
touch-action: none;
}
.window-resize-handle--n,
.window-resize-handle--s {
left: 22px;
right: 22px;
height: 10px;
cursor: ns-resize;
}
.window-resize-handle--n {
top: 0;
}
.window-resize-handle--s {
bottom: 0;
}
.window-resize-handle--e,
.window-resize-handle--w {
top: 22px;
bottom: 22px;
width: 10px;
cursor: ew-resize;
}
.window-resize-handle--e {
right: 0;
}
.window-resize-handle--w {
left: 0;
}
.window-resize-handle--ne,
.window-resize-handle--nw,
.window-resize-handle--se,
.window-resize-handle--sw {
width: 22px;
height: 22px;
}
.window-resize-handle--ne {
top: 0;
right: 0;
cursor: nesw-resize;
}
.window-resize-handle--nw {
top: 0;
left: 0;
cursor: nwse-resize;
}
.window-resize-handle--se {
right: 0;
bottom: 0;
cursor: nwse-resize;
}
.window-resize-handle--sw {
left: 0;
bottom: 0;
cursor: nesw-resize;
}
.comment-card-modal.is-expanded .window-resize-handle {
display: none;
}
.comment-card-modal__header {
display: flex;
align-items: center;

View File

@ -525,7 +525,7 @@ const getShareTokenFromPath = () => {
const COMMENT_TARGET_ICON = `
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M6.5 5.5h11a2.5 2.5 0 0 1 2.5 2.5v6.25a2.5 2.5 0 0 1-2.5 2.5H12l-4.3 3.1v-3.1H6.5A2.5 2.5 0 0 1 4 14.25V8a2.5 2.5 0 0 1 2.5-2.5Z"></path>
<path d="M6.5 4.75h11A3.25 3.25 0 0 1 20.75 8v6.25a3.25 3.25 0 0 1-3.25 3.25h-5.26l-5.29 3.82V17.5H6.5a3.25 3.25 0 0 1-3.25-3.25V8A3.25 3.25 0 0 1 6.5 4.75Z"></path>
</svg>
`;
@ -1485,6 +1485,115 @@ const setupDraggableFixedWindow = (element, handle) => {
handle.addEventListener("pointercancel", endDrag);
};
const setupResizableFixedWindow = (element, { minWidth = 520, minHeight = 420, margin = 8 } = {}) => {
if (!element || element.dataset.resizableReady === "true") return;
element.dataset.resizableReady = "true";
const directions = ["n", "e", "s", "w", "ne", "nw", "se", "sw"];
let resize = null;
const fixCurrentRect = () => {
const rect = element.getBoundingClientRect();
element.style.left = `${rect.left}px`;
element.style.top = `${rect.top}px`;
element.style.right = "auto";
element.style.bottom = "auto";
element.style.width = `${rect.width}px`;
element.style.height = `${rect.height}px`;
element.style.maxHeight = "none";
element.style.transform = "none";
return rect;
};
const beginResize = (direction, pointerId, clientX, clientY) => {
bringFloatingWindowToFront(element.id === "commentModal" ? "commentModal" : element.id);
const rect = fixCurrentRect();
resize = {
pointerId,
direction,
startX: clientX,
startY: clientY,
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
};
};
const updateResize = (clientX, clientY) => {
if (!resize) return;
const dx = clientX - resize.startX;
const dy = clientY - resize.startY;
const right = resize.left + resize.width;
const bottom = resize.top + resize.height;
let left = resize.left;
let top = resize.top;
let width = resize.width;
let height = resize.height;
if (resize.direction.includes("w")) {
left = Math.max(margin, Math.min(resize.left + dx, right - minWidth));
width = right - left;
}
if (resize.direction.includes("e")) {
width = Math.max(minWidth, resize.width + dx);
width = Math.min(width, window.innerWidth - margin - left);
}
if (resize.direction.includes("n")) {
top = Math.max(margin, Math.min(resize.top + dy, bottom - minHeight));
height = bottom - top;
}
if (resize.direction.includes("s")) {
height = Math.max(minHeight, resize.height + dy);
height = Math.min(height, window.innerHeight - margin - top);
}
element.style.left = `${Math.round(left)}px`;
element.style.top = `${Math.round(top)}px`;
element.style.width = `${Math.round(width)}px`;
element.style.height = `${Math.round(height)}px`;
};
directions.forEach((direction) => {
const handle = document.createElement("span");
handle.className = `window-resize-handle window-resize-handle--${direction}`;
handle.dataset.resizeDirection = direction;
handle.setAttribute("aria-hidden", "true");
element.appendChild(handle);
handle.addEventListener("pointerdown", (event) => {
if (event.pointerType === "mouse" && event.button !== 0) return;
if (element.classList.contains("is-expanded")) return;
event.preventDefault();
event.stopPropagation();
beginResize(direction, event.pointerId, event.clientX, event.clientY);
handle.setPointerCapture?.(event.pointerId);
});
handle.addEventListener("pointermove", (event) => {
if (!resize || resize.pointerId !== event.pointerId) return;
updateResize(event.clientX, event.clientY);
});
handle.addEventListener("mousedown", (event) => {
if (event.button !== 0 || element.classList.contains("is-expanded")) return;
event.preventDefault();
event.stopPropagation();
beginResize(direction, "mouse", event.clientX, event.clientY);
});
const endResize = (event) => {
if (!resize || resize.pointerId !== event.pointerId) return;
handle.releasePointerCapture?.(event.pointerId);
resize = null;
};
handle.addEventListener("pointerup", endResize);
handle.addEventListener("pointercancel", endResize);
});
window.addEventListener("mousemove", (event) => {
if (!resize || resize.pointerId !== "mouse") return;
updateResize(event.clientX, event.clientY);
});
window.addEventListener("mouseup", () => {
if (resize?.pointerId === "mouse") resize = null;
});
};
const bindFooterButtonAction = (button, handler) => {
if (!button || typeof handler !== "function") return;
button.addEventListener("click", handler);
@ -5308,6 +5417,7 @@ const loadProjectSnapshot = async (project) => {
registerFloatingWindowElement("commentModal", commentModal);
setupDraggableFixedWindow(commentsPanel, commentsPanelHeader);
setupDraggableFixedWindow(commentModal, commentModalHeader);
setupResizableFixedWindow(commentModal, { minWidth: 560, minHeight: 420 });
commentsCollapseButton?.addEventListener("click", () => setCommentsCollapsed(true));
commentsRestoreButton?.addEventListener("click", () => {
commentsModeActive = true;

View File

@ -134,7 +134,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<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 rel="stylesheet" href="./dcViewer.css?v=27">
<link rel="stylesheet" href="./dcViewer.css?v=29">
</head>
<body>
<header>
@ -570,6 +570,6 @@
<input id="fileInput" class="hidden" type="file"
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
<script type="module" src="./dcViewer.js?v=40"></script>
<script type="module" src="./dcViewer.js?v=43"></script>
</body>
</html>