Implemented the focused Floor Plans refinement pass.
Changed: [Edit.cshtml (line 172)](C:/Source/PlotLine/PlotLine/Views/FloorPlans/Edit.cshtml:172)Added optional grid coordinate toggle with localStorage persistence. Added adjacent-floor ghost/reference overlay selector. Added overlap-aware Stairs Up/Down target filtering. Added explicit fallback button: “Show all locations on floor above/below”. Kept door/window same-floor adjacency filtering and Hidden/Open/Secret Passage unrestricted behaviour. [site.css (line 4534)](C:/Source/PlotLine/PlotLine/wwwroot/css/site.css:4534)Added subtle coordinate labels, ghost floor blocks, warning styling, and dark-theme support. [site.min.css](C:/Source/PlotLine/PlotLine/wwwroot/css/site.min.css)Synced minified CSS. Database changes: none. Stored procedures modified: none. No Features, no transition redesign, no schema changes.
This commit is contained in:
parent
89c381e6bc
commit
1786d8c7b1
@ -167,6 +167,18 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="button-row">
|
<div class="button-row">
|
||||||
|
<label class="floor-plan-toolbar-check">
|
||||||
|
<input type="checkbox" data-grid-coordinates-toggle />
|
||||||
|
Show grid coordinates
|
||||||
|
</label>
|
||||||
|
<label class="floor-plan-reference-control">
|
||||||
|
<span class="muted">Reference floor</span>
|
||||||
|
<select class="form-select form-select-sm" data-reference-floor-select>
|
||||||
|
<option value="none">None</option>
|
||||||
|
<option value="below">Floor below</option>
|
||||||
|
<option value="above">Floor above</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
<span class="floor-plan-save-status" data-save-status>Saved</span>
|
<span class="floor-plan-save-status" data-save-status>Saved</span>
|
||||||
<a class="btn btn-outline-secondary btn-sm" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Back to floor plans</a>
|
<a class="btn btn-outline-secondary btn-sm" asp-action="Index" asp-route-projectId="@Model.Project.ProjectID">Back to floor plans</a>
|
||||||
<button class="btn btn-primary btn-sm" type="submit" data-save-layout-button>Save layout</button>
|
<button class="btn btn-primary btn-sm" type="submit" data-save-layout-button>Save layout</button>
|
||||||
@ -508,6 +520,7 @@
|
|||||||
<option value="">Choose a From Location first</option>
|
<option value="">Choose a From Location first</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="form-text" data-transition-to-message="@floor.FloorPlanFloorID"></div>
|
<div class="form-text" data-transition-to-message="@floor.FloorPlanFloorID"></div>
|
||||||
|
<button class="btn btn-link btn-sm px-0 d-none" type="button" data-transition-stair-fallback="@floor.FloorPlanFloorID">Show all locations on adjacent floor</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2 d-none" data-transition-position-panel="@floor.FloorPlanFloorID">
|
<div class="mb-2 d-none" data-transition-position-panel="@floor.FloorPlanFloorID">
|
||||||
<label class="form-label" for="transition-position-@floor.FloorPlanFloorID">Position Within Location</label>
|
<label class="form-label" for="transition-position-@floor.FloorPlanFloorID">Position Within Location</label>
|
||||||
@ -890,6 +903,8 @@
|
|||||||
const toolboxPanels = [...editor.querySelectorAll("[data-toolbox-panel]")];
|
const toolboxPanels = [...editor.querySelectorAll("[data-toolbox-panel]")];
|
||||||
const saveStatus = editor.querySelector("[data-save-status]");
|
const saveStatus = editor.querySelector("[data-save-status]");
|
||||||
const saveButtons = [...editor.querySelectorAll("[data-save-layout-button]")];
|
const saveButtons = [...editor.querySelectorAll("[data-save-layout-button]")];
|
||||||
|
const gridCoordinatesToggle = editor.querySelector("[data-grid-coordinates-toggle]");
|
||||||
|
const referenceFloorSelect = editor.querySelector("[data-reference-floor-select]");
|
||||||
const initialSelectedBlockId = options.selectedBlockId || "@selectedBlockId";
|
const initialSelectedBlockId = options.selectedBlockId || "@selectedBlockId";
|
||||||
const saveStatusText = {
|
const saveStatusText = {
|
||||||
saved: "Saved",
|
saved: "Saved",
|
||||||
@ -938,6 +953,8 @@
|
|||||||
let saveAgain = false;
|
let saveAgain = false;
|
||||||
let refreshAfterSave = false;
|
let refreshAfterSave = false;
|
||||||
const backgroundTimers = new Map();
|
const backgroundTimers = new Map();
|
||||||
|
const gridCoordinatesStorageKey = "floorPlan.showGridCoordinates";
|
||||||
|
const referenceFloorStorageKey = "floorPlan.referenceFloor";
|
||||||
|
|
||||||
function setToolboxTab(tabName) {
|
function setToolboxTab(tabName) {
|
||||||
toolboxTabs.forEach(tab => {
|
toolboxTabs.forEach(tab => {
|
||||||
@ -958,6 +975,118 @@
|
|||||||
saveStatus.textContent = message || saveStatusText[state] || state;
|
saveStatus.textContent = message || saveStatusText[state] || state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function gridColumnLabel(index) {
|
||||||
|
let label = "";
|
||||||
|
let value = index + 1;
|
||||||
|
while (value > 0) {
|
||||||
|
value--;
|
||||||
|
label = String.fromCharCode(65 + (value % 26)) + label;
|
||||||
|
value = Math.floor(value / 26);
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshGridCoordinateLabels() {
|
||||||
|
const show = gridCoordinatesToggle?.checked === true;
|
||||||
|
panels.forEach(panel => {
|
||||||
|
const grid = panel.querySelector("[data-floor-grid]");
|
||||||
|
if (!grid) return;
|
||||||
|
grid.classList.toggle("show-grid-coordinates", show);
|
||||||
|
grid.querySelectorAll("[data-grid-coordinate-label]").forEach(label => label.remove());
|
||||||
|
if (!show) return;
|
||||||
|
|
||||||
|
const width = Number(grid.dataset.gridWidth || 0);
|
||||||
|
const height = Number(grid.dataset.gridHeight || 0);
|
||||||
|
for (let x = 0; x < width; x++) {
|
||||||
|
const label = document.createElement("span");
|
||||||
|
label.dataset.gridCoordinateLabel = "column";
|
||||||
|
label.className = "floor-plan-coordinate-label floor-plan-coordinate-label--column";
|
||||||
|
label.style.setProperty("--coord-x", x);
|
||||||
|
label.textContent = gridColumnLabel(x);
|
||||||
|
grid.append(label);
|
||||||
|
}
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
const label = document.createElement("span");
|
||||||
|
label.dataset.gridCoordinateLabel = "row";
|
||||||
|
label.className = "floor-plan-coordinate-label floor-plan-coordinate-label--row";
|
||||||
|
label.style.setProperty("--coord-y", y);
|
||||||
|
label.textContent = String(y + 1);
|
||||||
|
grid.append(label);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function floorForLevel(currentFloorId, offset) {
|
||||||
|
const current = tabs.find(tab => tab.dataset.floorTab === String(currentFloorId));
|
||||||
|
if (!current) return null;
|
||||||
|
const targetLevel = Number(current.dataset.floorOrder || 0) + offset;
|
||||||
|
return tabs.find(tab => Number(tab.dataset.floorOrder || 0) === targetLevel) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function referenceFloorForActivePanel() {
|
||||||
|
const activePanel = panels.find(panel => panel.classList.contains("active"));
|
||||||
|
if (!activePanel || !referenceFloorSelect || referenceFloorSelect.value === "none") return null;
|
||||||
|
return floorForLevel(activePanel.dataset.floorPanel, referenceFloorSelect.value === "above" ? 1 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshReferenceFloorOptions() {
|
||||||
|
if (!referenceFloorSelect) return;
|
||||||
|
const activePanel = panels.find(panel => panel.classList.contains("active"));
|
||||||
|
const belowOption = [...referenceFloorSelect.options].find(option => option.value === "below");
|
||||||
|
const aboveOption = [...referenceFloorSelect.options].find(option => option.value === "above");
|
||||||
|
const belowFloor = activePanel ? floorForLevel(activePanel.dataset.floorPanel, -1) : null;
|
||||||
|
const aboveFloor = activePanel ? floorForLevel(activePanel.dataset.floorPanel, 1) : null;
|
||||||
|
if (belowOption) {
|
||||||
|
belowOption.disabled = !belowFloor;
|
||||||
|
belowOption.textContent = belowFloor ? `Floor below (${belowFloor.textContent.trim()})` : "No floor below";
|
||||||
|
}
|
||||||
|
if (aboveOption) {
|
||||||
|
aboveOption.disabled = !aboveFloor;
|
||||||
|
aboveOption.textContent = aboveFloor ? `Floor above (${aboveFloor.textContent.trim()})` : "No floor above";
|
||||||
|
}
|
||||||
|
if (referenceFloorSelect.selectedOptions[0]?.disabled) {
|
||||||
|
referenceFloorSelect.value = "none";
|
||||||
|
window.localStorage?.setItem(referenceFloorStorageKey, "none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshReferenceOverlay() {
|
||||||
|
refreshReferenceFloorOptions();
|
||||||
|
panels.forEach(panel => {
|
||||||
|
panel.querySelectorAll("[data-reference-floor-overlay], [data-reference-grid-warning]").forEach(item => item.remove());
|
||||||
|
});
|
||||||
|
|
||||||
|
const activePanel = panels.find(panel => panel.classList.contains("active"));
|
||||||
|
const grid = activePanel?.querySelector("[data-floor-grid]");
|
||||||
|
const referenceFloor = referenceFloorForActivePanel();
|
||||||
|
if (!activePanel || !grid || !referenceFloor) return;
|
||||||
|
|
||||||
|
const referenceGrid = editor.querySelector(`[data-floor-grid="${referenceFloor.dataset.floorTab}"]`);
|
||||||
|
const overlay = document.createElement("div");
|
||||||
|
overlay.className = "floor-plan-reference-overlay";
|
||||||
|
overlay.dataset.referenceFloorOverlay = referenceFloor.dataset.floorTab;
|
||||||
|
overlay.setAttribute("aria-hidden", "true");
|
||||||
|
blockItemsForFloor(referenceFloor.dataset.floorTab).forEach(item => {
|
||||||
|
const block = document.createElement("span");
|
||||||
|
block.className = "floor-plan-reference-block";
|
||||||
|
block.style.setProperty("--x", item.x);
|
||||||
|
block.style.setProperty("--y", item.y);
|
||||||
|
block.style.setProperty("--w", item.w);
|
||||||
|
block.style.setProperty("--h", item.h);
|
||||||
|
block.textContent = item.block.dataset.locationName || item.block.textContent.trim();
|
||||||
|
overlay.append(block);
|
||||||
|
});
|
||||||
|
grid.prepend(overlay);
|
||||||
|
|
||||||
|
if (referenceGrid && (referenceGrid.dataset.gridWidth !== grid.dataset.gridWidth || referenceGrid.dataset.gridHeight !== grid.dataset.gridHeight)) {
|
||||||
|
const warning = document.createElement("div");
|
||||||
|
warning.className = "floor-plan-reference-warning";
|
||||||
|
warning.dataset.referenceGridWarning = "true";
|
||||||
|
warning.textContent = "Reference floor uses a different grid size.";
|
||||||
|
grid.append(warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function markDirty() {
|
function markDirty() {
|
||||||
setSaveStatus("dirty");
|
setSaveStatus("dirty");
|
||||||
window.clearTimeout(saveTimer);
|
window.clearTimeout(saveTimer);
|
||||||
@ -1131,6 +1260,9 @@
|
|||||||
|
|
||||||
grid.querySelectorAll("[data-block]").forEach(block => writeBlock(block, readBlock(block)));
|
grid.querySelectorAll("[data-block]").forEach(block => writeBlock(block, readBlock(block)));
|
||||||
checkOverlaps();
|
checkOverlaps();
|
||||||
|
refreshGridCoordinateLabels();
|
||||||
|
refreshReferenceOverlay();
|
||||||
|
refreshTransitionDestination(floorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setActiveFloor(floorId) {
|
function setActiveFloor(floorId) {
|
||||||
@ -1154,6 +1286,8 @@
|
|||||||
}
|
}
|
||||||
filterAddBlockLocations(activeTab?.dataset.floorLocationId || "");
|
filterAddBlockLocations(activeTab?.dataset.floorLocationId || "");
|
||||||
checkOverlaps();
|
checkOverlaps();
|
||||||
|
refreshReferenceOverlay();
|
||||||
|
refreshTransitionDestination(floorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function backgroundFields(floorId) {
|
function backgroundFields(floorId) {
|
||||||
@ -1296,6 +1430,7 @@
|
|||||||
if (!selectedBlock) return;
|
if (!selectedBlock) return;
|
||||||
selectedBlock.dataset.locationId = locationSelect.value || "";
|
selectedBlock.dataset.locationId = locationSelect.value || "";
|
||||||
checkOverlaps();
|
checkOverlaps();
|
||||||
|
refreshTransitionDestination(selectedBlock.dataset.floorId);
|
||||||
markDirty();
|
markDirty();
|
||||||
});
|
});
|
||||||
locationWrap.append(locationSelect);
|
locationWrap.append(locationSelect);
|
||||||
@ -1528,6 +1663,10 @@
|
|||||||
return (verticalTouch && verticalOverlap) || (horizontalTouch && horizontalOverlap);
|
return (verticalTouch && verticalOverlap) || (horizontalTouch && horizontalOverlap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function blocksOverlapArea(a, b) {
|
||||||
|
return a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
|
||||||
|
}
|
||||||
|
|
||||||
function blockItemsForFloor(floorId) {
|
function blockItemsForFloor(floorId) {
|
||||||
return [...editor.querySelectorAll(`[data-block][data-floor-id="${floorId}"]`)].map(block => ({ block, ...readBlock(block) }));
|
return [...editor.querySelectorAll(`[data-block][data-floor-id="${floorId}"]`)].map(block => ({ block, ...readBlock(block) }));
|
||||||
}
|
}
|
||||||
@ -1565,18 +1704,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshTransitionDestination(floorId) {
|
function refreshTransitionDestination(floorId, showAllStairTargets = false) {
|
||||||
const typeSelect = editor.querySelector(`[data-transition-type-select="${floorId}"]`);
|
const typeSelect = editor.querySelector(`[data-transition-type-select="${floorId}"]`);
|
||||||
const fromSelect = editor.querySelector(`[data-transition-from-select="${floorId}"]`);
|
const fromSelect = editor.querySelector(`[data-transition-from-select="${floorId}"]`);
|
||||||
const toSelect = editor.querySelector(`[data-transition-to-select="${floorId}"]`);
|
const toSelect = editor.querySelector(`[data-transition-to-select="${floorId}"]`);
|
||||||
const message = editor.querySelector(`[data-transition-to-message="${floorId}"]`);
|
const message = editor.querySelector(`[data-transition-to-message="${floorId}"]`);
|
||||||
const positionPanel = editor.querySelector(`[data-transition-position-panel="${floorId}"]`);
|
const positionPanel = editor.querySelector(`[data-transition-position-panel="${floorId}"]`);
|
||||||
|
const fallbackButton = editor.querySelector(`[data-transition-stair-fallback="${floorId}"]`);
|
||||||
if (!typeSelect || !fromSelect || !toSelect) return;
|
if (!typeSelect || !fromSelect || !toSelect) return;
|
||||||
|
|
||||||
const type = typeSelect.value;
|
const type = typeSelect.value;
|
||||||
const fromLocationId = fromSelect.value;
|
const fromLocationId = fromSelect.value;
|
||||||
const stair = isStairTransition(type);
|
const stair = isStairTransition(type);
|
||||||
positionPanel?.classList.toggle("d-none", !stair);
|
positionPanel?.classList.toggle("d-none", !stair);
|
||||||
|
fallbackButton?.classList.add("d-none");
|
||||||
if (!fromLocationId) {
|
if (!fromLocationId) {
|
||||||
setTransitionOptions(toSelect, [], message, "Choose a From Location first.");
|
setTransitionOptions(toSelect, [], message, "Choose a From Location first.");
|
||||||
return;
|
return;
|
||||||
@ -1587,15 +1728,41 @@
|
|||||||
if (stair) {
|
if (stair) {
|
||||||
const currentFloor = tabs.find(tab => tab.dataset.floorTab === String(floorId));
|
const currentFloor = tabs.find(tab => tab.dataset.floorTab === String(floorId));
|
||||||
const currentLevel = Number(currentFloor?.dataset.floorOrder || 0);
|
const currentLevel = Number(currentFloor?.dataset.floorOrder || 0);
|
||||||
const targetLevel = currentLevel + (normalizeTransitionType(type) === "stairsup" ? 1 : -1);
|
const isUp = normalizeTransitionType(type) === "stairsup";
|
||||||
|
const targetLevel = currentLevel + (isUp ? 1 : -1);
|
||||||
const targetFloor = tabs.find(tab => Number(tab.dataset.floorOrder || 0) === targetLevel);
|
const targetFloor = tabs.find(tab => Number(tab.dataset.floorOrder || 0) === targetLevel);
|
||||||
emptyMessage = normalizeTransitionType(type) === "stairsup"
|
emptyMessage = isUp
|
||||||
? "No floor exists above this level."
|
? "No floor exists above this level."
|
||||||
: "No floor exists below this level.";
|
: "No floor exists below this level.";
|
||||||
if (targetFloor) {
|
if (targetFloor) {
|
||||||
options = locationOptionsFromBlocks(blockItemsForFloor(targetFloor.dataset.floorTab))
|
const fromBlocks = blockItemsForFloor(floorId)
|
||||||
.filter(option => option.id !== fromLocationId);
|
.filter(item => item.block.dataset.locationId === fromLocationId);
|
||||||
emptyMessage = options.length ? "" : "No locations are placed on the adjacent floor.";
|
const targetBlocks = blockItemsForFloor(targetFloor.dataset.floorTab);
|
||||||
|
const overlappingLocationIds = new Set();
|
||||||
|
if (!showAllStairTargets) {
|
||||||
|
targetBlocks.forEach(target => {
|
||||||
|
const targetLocationId = target.block.dataset.locationId;
|
||||||
|
if (!targetLocationId || targetLocationId === fromLocationId) return;
|
||||||
|
if (fromBlocks.some(from => blocksOverlapArea(from, target))) {
|
||||||
|
overlappingLocationIds.add(targetLocationId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
options = locationOptionsFromBlocks(targetBlocks)
|
||||||
|
.filter(option => option.id !== fromLocationId)
|
||||||
|
.filter(option => showAllStairTargets || overlappingLocationIds.has(option.id));
|
||||||
|
emptyMessage = isUp
|
||||||
|
? "No locations directly above this location."
|
||||||
|
: "No locations directly below this location.";
|
||||||
|
if (!options.length && locationOptionsFromBlocks(targetBlocks).some(option => option.id !== fromLocationId)) {
|
||||||
|
fallbackButton?.classList.remove("d-none");
|
||||||
|
if (fallbackButton) {
|
||||||
|
fallbackButton.textContent = isUp
|
||||||
|
? "Show all locations on floor above"
|
||||||
|
: "Show all locations on floor below";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (isUnrestrictedTransition(type)) {
|
} else if (isUnrestrictedTransition(type)) {
|
||||||
options = locationOptionsFromBlocks([...editor.querySelectorAll("[data-block]")].map(block => ({ block, ...readBlock(block) })))
|
options = locationOptionsFromBlocks([...editor.querySelectorAll("[data-block]")].map(block => ({ block, ...readBlock(block) })))
|
||||||
@ -1881,6 +2048,26 @@
|
|||||||
input.addEventListener("change", () => refreshTransitionDestination(floorId));
|
input.addEventListener("change", () => refreshTransitionDestination(floorId));
|
||||||
refreshTransitionDestination(floorId);
|
refreshTransitionDestination(floorId);
|
||||||
});
|
});
|
||||||
|
editor.querySelectorAll("[data-transition-stair-fallback]").forEach(button => {
|
||||||
|
button.addEventListener("click", () => refreshTransitionDestination(button.dataset.transitionStairFallback, true));
|
||||||
|
});
|
||||||
|
if (gridCoordinatesToggle) {
|
||||||
|
gridCoordinatesToggle.checked = window.localStorage?.getItem(gridCoordinatesStorageKey) === "true";
|
||||||
|
gridCoordinatesToggle.addEventListener("change", () => {
|
||||||
|
window.localStorage?.setItem(gridCoordinatesStorageKey, gridCoordinatesToggle.checked ? "true" : "false");
|
||||||
|
refreshGridCoordinateLabels();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (referenceFloorSelect) {
|
||||||
|
const storedReferenceFloor = window.localStorage?.getItem(referenceFloorStorageKey);
|
||||||
|
if (storedReferenceFloor && [...referenceFloorSelect.options].some(option => option.value === storedReferenceFloor)) {
|
||||||
|
referenceFloorSelect.value = storedReferenceFloor;
|
||||||
|
}
|
||||||
|
referenceFloorSelect.addEventListener("change", () => {
|
||||||
|
window.localStorage?.setItem(referenceFloorStorageKey, referenceFloorSelect.value);
|
||||||
|
refreshReferenceOverlay();
|
||||||
|
});
|
||||||
|
}
|
||||||
document.querySelectorAll("[data-edit-transition-type]").forEach(select => {
|
document.querySelectorAll("[data-edit-transition-type]").forEach(select => {
|
||||||
const panel = select.closest(".modal-body")?.querySelector("[data-edit-transition-position-panel]");
|
const panel = select.closest(".modal-body")?.querySelector("[data-edit-transition-position-panel]");
|
||||||
const refreshPositionPanel = () => panel?.classList.toggle("d-none", !isStairTransition(select.value));
|
const refreshPositionPanel = () => panel?.classList.toggle("d-none", !isStairTransition(select.value));
|
||||||
@ -1960,11 +2147,19 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
editor.addEventListener("pointerup", () => {
|
editor.addEventListener("pointerup", () => {
|
||||||
if (pointerState) markDirty();
|
if (pointerState) {
|
||||||
|
const floorId = pointerState.block.dataset.floorId;
|
||||||
|
markDirty();
|
||||||
|
refreshTransitionDestination(floorId);
|
||||||
|
}
|
||||||
pointerState = null;
|
pointerState = null;
|
||||||
});
|
});
|
||||||
editor.addEventListener("pointercancel", () => {
|
editor.addEventListener("pointercancel", () => {
|
||||||
if (pointerState) markDirty();
|
if (pointerState) {
|
||||||
|
const floorId = pointerState.block.dataset.floorId;
|
||||||
|
markDirty();
|
||||||
|
refreshTransitionDestination(floorId);
|
||||||
|
}
|
||||||
pointerState = null;
|
pointerState = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1995,6 +2190,7 @@
|
|||||||
if (!selectedBlock) return;
|
if (!selectedBlock) return;
|
||||||
selectedBlock.dataset.locationId = select.value || "";
|
selectedBlock.dataset.locationId = select.value || "";
|
||||||
checkOverlaps();
|
checkOverlaps();
|
||||||
|
refreshTransitionDestination(selectedBlock.dataset.floorId);
|
||||||
markDirty();
|
markDirty();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -2054,6 +2250,8 @@
|
|||||||
} else if (tabs[0]) {
|
} else if (tabs[0]) {
|
||||||
setActiveFloor(tabs[0].dataset.floorTab);
|
setActiveFloor(tabs[0].dataset.floorTab);
|
||||||
}
|
}
|
||||||
|
refreshGridCoordinateLabels();
|
||||||
|
refreshReferenceOverlay();
|
||||||
checkOverlaps();
|
checkOverlaps();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -4531,6 +4531,26 @@ body.dragging-location [data-drag-type="location"] {
|
|||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floor-plan-toolbar-check,
|
||||||
|
.floor-plan-reference-control {
|
||||||
|
align-items: center;
|
||||||
|
color: var(--muted-color, #6b756f);
|
||||||
|
display: inline-flex;
|
||||||
|
font-size: .82rem;
|
||||||
|
font-weight: 700;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor-plan-reference-control {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor-plan-reference-control .form-select {
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
.floor-plan-save-status {
|
.floor-plan-save-status {
|
||||||
border: 1px solid rgba(47, 111, 99, .2);
|
border: 1px solid rgba(47, 111, 99, .2);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
@ -4594,6 +4614,71 @@ body.dragging-location [data-drag-type="location"] {
|
|||||||
width: calc(var(--grid-width) * var(--cell-size));
|
width: calc(var(--grid-width) * var(--cell-size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floor-plan-coordinate-label {
|
||||||
|
color: rgba(33, 61, 55, .42);
|
||||||
|
font-size: .62rem;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
text-shadow: 0 1px 0 rgba(255, 255, 255, .7);
|
||||||
|
user-select: none;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor-plan-coordinate-label--column {
|
||||||
|
left: calc(var(--coord-x) * var(--cell-size) + 4px);
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor-plan-coordinate-label--row {
|
||||||
|
left: 4px;
|
||||||
|
top: calc(var(--coord-y) * var(--cell-size) + 4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor-plan-reference-overlay {
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor-plan-reference-block {
|
||||||
|
align-items: center;
|
||||||
|
background: rgba(47, 111, 99, .08);
|
||||||
|
border: 1px dashed rgba(47, 111, 99, .34);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: rgba(27, 55, 49, .46);
|
||||||
|
display: flex;
|
||||||
|
font-size: .62rem;
|
||||||
|
font-weight: 800;
|
||||||
|
height: calc(var(--h) * var(--cell-size));
|
||||||
|
justify-content: center;
|
||||||
|
left: calc(var(--x) * var(--cell-size));
|
||||||
|
line-height: 1.05;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 3px;
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
top: calc(var(--y) * var(--cell-size));
|
||||||
|
width: calc(var(--w) * var(--cell-size));
|
||||||
|
}
|
||||||
|
|
||||||
|
.floor-plan-reference-warning {
|
||||||
|
background: rgba(251, 236, 211, .92);
|
||||||
|
border: 1px solid rgba(184, 118, 43, .34);
|
||||||
|
border-radius: 999px;
|
||||||
|
color: #7b4b12;
|
||||||
|
font-size: .76rem;
|
||||||
|
font-weight: 800;
|
||||||
|
left: 10px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
z-index: 6;
|
||||||
|
}
|
||||||
|
|
||||||
.floor-plan-background-image {
|
.floor-plan-background-image {
|
||||||
height: auto;
|
height: auto;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -5066,6 +5151,26 @@ body.dragging-location [data-drag-type="location"] {
|
|||||||
color: #f1f6f3;
|
color: #f1f6f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-bs-theme="dark"] .floor-plan-coordinate-label,
|
||||||
|
.dark .floor-plan-coordinate-label {
|
||||||
|
color: rgba(244, 234, 220, .46);
|
||||||
|
text-shadow: 0 1px 0 rgba(0, 0, 0, .55);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme="dark"] .floor-plan-reference-block,
|
||||||
|
.dark .floor-plan-reference-block {
|
||||||
|
background: rgba(131, 199, 242, .08);
|
||||||
|
border-color: rgba(131, 199, 242, .32);
|
||||||
|
color: rgba(244, 234, 220, .46);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme="dark"] .floor-plan-reference-warning,
|
||||||
|
.dark .floor-plan-reference-warning {
|
||||||
|
background: rgba(84, 57, 25, .92);
|
||||||
|
border-color: rgba(244, 177, 100, .32);
|
||||||
|
color: #ffd9a5;
|
||||||
|
}
|
||||||
|
|
||||||
[data-bs-theme="dark"] .floor-plan-transition-marker,
|
[data-bs-theme="dark"] .floor-plan-transition-marker,
|
||||||
.dark .floor-plan-transition-marker {
|
.dark .floor-plan-transition-marker {
|
||||||
--transition-marker-ink: #f4eadc;
|
--transition-marker-ink: #f4eadc;
|
||||||
|
|||||||
2
PlotLine/wwwroot/css/site.min.css
vendored
2
PlotLine/wwwroot/css/site.min.css
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user