sadsdf
All checks were successful
Deploy / deploy (push) Successful in 18s

This commit is contained in:
2026-05-17 00:54:41 +02:00
parent f5bb37f96e
commit 70e82cae7b
2 changed files with 284 additions and 64 deletions

View File

@@ -293,84 +293,271 @@ button, input, select {
.rack-grid { .rack-grid {
position: relative; position: relative;
border-radius: 28px; border-radius: 32px;
border: 1px solid rgba(105, 108, 115, 0.12); border: 1px solid rgba(105, 108, 115, 0.14);
background: background:
radial-gradient(circle at top left, rgba(255, 255, 255, 0.07), transparent 20%), linear-gradient(180deg, rgba(255, 255, 255, 0.78), rgba(239, 232, 214, 0.88));
linear-gradient(180deg, #26272b, #202126);
padding: 16px; padding: 16px;
overflow: hidden; overflow: hidden;
box-shadow: box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.7),
0 20px 40px rgba(20, 18, 16, 0.18); 0 24px 40px rgba(76, 60, 38, 0.14);
} }
.rack-grid__bay { .rack-shell {
position: relative; position: relative;
width: min(100%, 1180px); width: min(100%, 1180px);
margin: 0 auto; margin: 0 auto;
aspect-ratio: 3 / 2; aspect-ratio: 3 / 2;
background-image: border-radius: 28px;
linear-gradient(180deg, color-mix(in srgb, var(--rack-frame) 38%, transparent), color-mix(in srgb, var(--rack-frame) 38%, transparent)), background:
url("images/rack-template.png"); linear-gradient(180deg, rgba(255, 255, 255, 0.04), rgba(0, 0, 0, 0.16)),
background-size: cover; linear-gradient(90deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0));
background-position: center;
background-repeat: no-repeat;
border-radius: 18px;
box-shadow: box-shadow:
0 18px 34px rgba(7, 8, 10, 0.32), 0 24px 34px rgba(15, 16, 18, 0.22),
inset 0 0 0 1px rgba(255, 255, 255, 0.04); inset 0 0 0 1px rgba(255, 255, 255, 0.04);
isolation: isolate; isolation: isolate;
} }
.rack-grid__guides { .rack-shell__frame {
position: relative; position: absolute;
width: 100%; background: linear-gradient(180deg, var(--rack-frame-light), var(--rack-frame-dark));
height: 100%; box-shadow:
background: transparent; inset 0 1px 0 rgba(255, 255, 255, 0.18),
inset 0 -10px 12px rgba(0, 0, 0, 0.18);
z-index: 3;
} }
.rack-slot { .rack-shell__frame--top,
position: relative; .rack-shell__frame--bottom {
height: calc(100% / var(--rack-total-u)); left: 9%;
border-top: 0; right: 9%;
background: transparent; height: 8%;
border-radius: 14px;
} }
.rack-slot:first-child { .rack-shell__frame--top {
border-top: 0; top: 6%;
} }
.rack-slot.is-drop-target { .rack-shell__frame--bottom {
bottom: 6%;
}
.rack-shell__frame--left,
.rack-shell__frame--right {
top: 8%;
bottom: 8%;
width: 6.8%;
border-radius: 24px;
}
.rack-shell__frame--left {
left: 0.5%;
}
.rack-shell__frame--right {
right: 0.5%;
}
.rack-shell__screw {
position: absolute;
width: 3.6%;
aspect-ratio: 1;
border-radius: 50%;
background: background:
linear-gradient(180deg, rgba(236, 129, 69, 0.16), rgba(236, 129, 69, 0.08)); radial-gradient(circle at 32% 30%, rgba(255, 255, 255, 0.38), transparent 20%),
radial-gradient(circle at center, #5f646d 0 22%, #2b2f36 23% 55%, #818792 56% 70%, #23272e 71%);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.22),
0 2px 4px rgba(0, 0, 0, 0.25);
z-index: 4;
} }
.rack-slot__label { .rack-shell__screw::before,
.rack-shell__screw::after {
content: "";
position: absolute; position: absolute;
left: 5.7%; top: 50%;
top: 39%; left: 50%;
font-size: 0.78rem; background: rgba(20, 22, 26, 0.8);
color: rgba(239, 241, 245, 0.92); border-radius: 999px;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.65); transform: translate(-50%, -50%);
} }
.rack-slot__label--right { .rack-shell__screw::before {
left: auto; width: 58%;
right: 5.7%; height: 10%;
} }
.rack-items-layer { .rack-shell__screw::after {
width: 10%;
height: 58%;
}
.rack-shell__screw--tl { top: 7.4%; left: 10.6%; }
.rack-shell__screw--tr { top: 7.4%; right: 10.6%; }
.rack-shell__screw--bl { bottom: 7.4%; left: 10.6%; }
.rack-shell__screw--br { bottom: 7.4%; right: 10.6%; }
.rack-core {
position: absolute; position: absolute;
inset: 13.9% 17.4% 16.8% 17.4%; inset: 15% 12.8% 14% 12.8%;
display: grid;
grid-template-columns: 10% 1fr 10%;
gap: 0;
z-index: 2; z-index: 2;
} }
.rack-insertion-layer { .rack-rail {
position: relative;
display: grid;
grid-template-rows: repeat(var(--rack-total-u), 1fr);
background: linear-gradient(180deg, color-mix(in srgb, var(--rack-rail) 90%, #14161a), color-mix(in srgb, var(--rack-rail) 65%, #08090b));
box-shadow:
inset 0 0 0 1px rgba(255, 255, 255, 0.08),
inset 0 0 0 6px rgba(0, 0, 0, 0.18);
}
.rack-rail--left {
border-radius: 8px 0 0 8px;
}
.rack-rail--right {
border-radius: 0 8px 8px 0;
}
.rack-rail__unit {
position: relative;
min-height: 0;
}
.rack-rail__label {
position: absolute; position: absolute;
inset: 13.9% 17.4% 16.8% 17.4%; top: 50%;
pointer-events: auto; transform: translateY(-50%);
z-index: 3; font-size: 0.78rem;
color: rgba(228, 232, 238, 0.84);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}
.rack-rail__label--left {
left: -2.1rem;
}
.rack-rail__label--right {
right: -1.6rem;
}
.rack-rail__tick {
position: absolute;
top: 39%;
width: 14%;
height: 2px;
background: rgba(223, 228, 235, 0.74);
border-radius: 999px;
}
.rack-rail__tick--left {
left: 14%;
}
.rack-rail__tick--right {
right: 14%;
}
.rack-rail__holes {
position: absolute;
top: 50%;
transform: translateY(-50%);
display: grid;
grid-template-rows: repeat(3, 1fr);
gap: 0.22rem;
width: 24%;
height: 74%;
}
.rack-rail__holes--left {
right: 13%;
}
.rack-rail__holes--right {
left: 13%;
}
.rack-rail__holes span {
border-radius: 4px;
background: linear-gradient(180deg, rgba(7, 8, 11, 0.96), rgba(25, 27, 33, 0.96));
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.rack-bay {
position: relative;
overflow: hidden;
background:
radial-gradient(circle at center, rgba(96, 99, 108, 0.22), transparent 48%),
linear-gradient(180deg, #181a1f, #2a2d34 36%, #22252c 68%, #17191d 100%);
box-shadow:
inset 0 0 0 1px rgba(255, 255, 255, 0.04),
inset 0 32px 42px rgba(0, 0, 0, 0.34),
inset 0 -24px 34px rgba(0, 0, 0, 0.28);
}
.rack-bay__ceiling,
.rack-bay__floor {
position: absolute;
left: 8%;
right: 8%;
height: 14%;
background:
repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.04) 0 2%, transparent 2% 4.5%),
linear-gradient(180deg, rgba(71, 74, 82, 0.24), rgba(8, 9, 12, 0.12));
z-index: 0;
}
.rack-bay__ceiling {
top: 0;
transform: perspective(500px) rotateX(62deg);
transform-origin: top;
}
.rack-bay__floor {
bottom: 0;
transform: perspective(500px) rotateX(-62deg);
transform-origin: bottom;
}
.rack-bay__wall {
position: absolute;
top: 10%;
bottom: 10%;
width: 9%;
background: linear-gradient(90deg, rgba(7, 8, 11, 0.65), rgba(60, 64, 72, 0.12));
z-index: 0;
}
.rack-bay__wall--left {
left: 0;
}
.rack-bay__wall--right {
right: 0;
transform: scaleX(-1);
}
.rack-bay__back {
position: absolute;
inset: 14% 7% 14% 7%;
border-radius: 4px;
background:
radial-gradient(circle at 25% 25%, rgba(255, 255, 255, 0.12) 0 1%, transparent 1.2%),
radial-gradient(circle at 75% 25%, rgba(255, 255, 255, 0.12) 0 1%, transparent 1.2%),
radial-gradient(circle at 25% 75%, rgba(255, 255, 255, 0.12) 0 1%, transparent 1.2%),
radial-gradient(circle at 75% 75%, rgba(255, 255, 255, 0.12) 0 1%, transparent 1.2%),
linear-gradient(180deg, rgba(83, 86, 94, 0.16), rgba(24, 26, 31, 0.08));
box-shadow:
inset 0 0 0 1px rgba(255, 255, 255, 0.04),
inset 0 12px 20px rgba(255, 255, 255, 0.03);
} }
.rack-grid__header-badge { .rack-grid__header-badge {
@@ -395,6 +582,19 @@ button, input, select {
right: 1.8%; right: 1.8%;
} }
.rack-items-layer {
position: absolute;
inset: 0;
z-index: 2;
}
.rack-insertion-layer {
position: absolute;
inset: 0;
pointer-events: auto;
z-index: 3;
}
.rack-insert-zone { .rack-insert-zone {
position: absolute; position: absolute;
display: flex; display: flex;
@@ -939,7 +1139,7 @@ button, input, select {
padding-right: 26px; padding-right: 26px;
} }
.rack-grid__bay, .rack-shell,
.rack-items-layer { .rack-items-layer {
width: 100%; width: 100%;
} }

View File

@@ -213,29 +213,37 @@ function renderRack() {
ui.rackGrid.style.setProperty("--rack-total-u", String(rack.totalU)); ui.rackGrid.style.setProperty("--rack-total-u", String(rack.totalU));
applyRackColorTheme(ui.rackGrid, state.rackColor); applyRackColorTheme(ui.rackGrid, state.rackColor);
const slots = [];
for (let u = rack.totalU; u >= 1; u -= 1) {
slots.push(`
<div class="rack-slot" data-slot-u="${u}">
<span class="rack-slot__label">${u}U</span>
<span class="rack-slot__label rack-slot__label--right">${u}</span>
</div>
`);
}
ui.rackGrid.innerHTML = ` ui.rackGrid.innerHTML = `
<div class="rack-grid__header-badge rack-grid__header-badge--left">${rack.rackStandard === "19_inch" ? '19" Rack' : '10" Rack'}</div> <div class="rack-grid__header-badge rack-grid__header-badge--left">${rack.rackStandard === "19_inch" ? '19" Rack' : '10" Rack'}</div>
<div class="rack-grid__header-badge rack-grid__header-badge--right">${rack.totalU} HE</div> <div class="rack-grid__header-badge rack-grid__header-badge--right">${rack.totalU} HE</div>
<div class="rack-grid__bay"> <div class="rack-shell">
<div class="rack-grid__guides">${slots.join("")}</div> <div class="rack-shell__frame rack-shell__frame--top"></div>
<div class="rack-shell__frame rack-shell__frame--bottom"></div>
<div class="rack-shell__frame rack-shell__frame--left"></div>
<div class="rack-shell__frame rack-shell__frame--right"></div>
<div class="rack-shell__screw rack-shell__screw--tl"></div>
<div class="rack-shell__screw rack-shell__screw--tr"></div>
<div class="rack-shell__screw rack-shell__screw--bl"></div>
<div class="rack-shell__screw rack-shell__screw--br"></div>
<div class="rack-core">
<div class="rack-rail rack-rail--left">${renderRailScale(rack, "left")}</div>
<div class="rack-bay" id="rack-bay">
<div class="rack-bay__ceiling"></div>
<div class="rack-bay__wall rack-bay__wall--left"></div>
<div class="rack-bay__wall rack-bay__wall--right"></div>
<div class="rack-bay__back"></div>
<div class="rack-bay__floor"></div>
<div class="rack-insertion-layer" id="rack-insertion-layer"></div> <div class="rack-insertion-layer" id="rack-insertion-layer"></div>
<div class="rack-items-layer" id="rack-items-layer"></div> <div class="rack-items-layer" id="rack-items-layer"></div>
</div> </div>
<div class="rack-rail rack-rail--right">${renderRailScale(rack, "right")}</div>
</div>
</div>
`; `;
const layer = document.getElementById("rack-items-layer"); const layer = document.getElementById("rack-items-layer");
layer.style.height = "100%"; layer.style.height = "100%";
const bay = ui.rackGrid.querySelector(".rack-grid__bay"); const bay = document.getElementById("rack-bay");
bay.addEventListener("dragover", handleRackDragOver); bay.addEventListener("dragover", handleRackDragOver);
bay.addEventListener("drop", handleRackDrop); bay.addEventListener("drop", handleRackDrop);
bay.addEventListener("dragleave", handleRackDragLeave); bay.addEventListener("dragleave", handleRackDragLeave);
@@ -287,6 +295,22 @@ function renderRack() {
}); });
} }
function renderRailScale(rack, side) {
const rows = [];
for (let u = rack.totalU; u >= 1; u -= 1) {
rows.push(`
<div class="rack-rail__unit">
<span class="rack-rail__label rack-rail__label--${side}">${u}</span>
<span class="rack-rail__tick rack-rail__tick--${side}"></span>
<span class="rack-rail__holes rack-rail__holes--${side}">
<span></span><span></span><span></span>
</span>
</div>
`);
}
return rows.join("");
}
function insertSelectedComponentAt(y) { function insertSelectedComponentAt(y) {
const componentId = state.libraryDragComponentId || state.selectedComponentId; const componentId = state.libraryDragComponentId || state.selectedComponentId;
if (!componentId) { if (!componentId) {
@@ -899,10 +923,6 @@ function getPlacementCenterY(item, component) {
return item.y + getComponentHeightPx(component) / 2; return item.y + getComponentHeightPx(component) / 2;
} }
function formatRackPosition(y) {
return `${(y / getUnitHeightPx() + 1).toFixed(1)}U`;
}
function formatRackPosition(y, component) { function formatRackPosition(y, component) {
const rack = getCurrentRackTemplate(); const rack = getCurrentRackTemplate();
if (!rack) { if (!rack) {