Use full-width editor device drawer
This commit is contained in:
@@ -167,6 +167,12 @@ Felder und vollständiger Zeilenbestand unverändert sind.
|
|||||||
Der Editor erzeugt die vollständigen Move-Zuweisungen aus dem geladenen Tree,
|
Der Editor erzeugt die vollständigen Move-Zuweisungen aus dem geladenen Tree,
|
||||||
vergibt für neue Ziele vor dem Kommando eine stabile UUID und führt das
|
vergibt für neue Ziele vor dem Kommando eine stabile UUID und führt das
|
||||||
Toolbar-Undo/Redo über die projektweite Historie aus.
|
Toolbar-Undo/Redo über die projektweite Historie aus.
|
||||||
|
Die Projektgerätepalette belegt keine permanente Layoutspalte mehr. Sie wird
|
||||||
|
über die Editor-Toolbar als überlagernder Drawer geöffnet, während das
|
||||||
|
Stromkreis-Grid standardmäßig die gesamte verfügbare Breite nutzt. Auswahl,
|
||||||
|
Schnelleinfügen und die vorhandenen Drag-and-drop-Payloads bleiben im Drawer
|
||||||
|
unverändert verfügbar; während eines aktiven Projektgeräte-Drags kann er nicht
|
||||||
|
geschlossen werden.
|
||||||
`circuit.reorder-section` speichert die erwartete und neue Sortierposition
|
`circuit.reorder-section` speichert die erwartete und neue Sortierposition
|
||||||
jedes Stromkreises eines vollständigen Abschnitts. Forward, Undo und Redo
|
jedes Stromkreises eines vollständigen Abschnitts. Forward, Undo und Redo
|
||||||
ändern ausschließlich `sortOrder`; Stromkreisblöcke, Gerätezeilen und BMKs
|
ändern ausschließlich `sortOrder`; Stromkreisblöcke, Gerätezeilen und BMKs
|
||||||
|
|||||||
+45
-7
@@ -71,6 +71,13 @@ body {
|
|||||||
opacity: 0.45;
|
opacity: 0.45;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-toolbar .project-device-drawer-toggle {
|
||||||
|
border-color: #2563eb;
|
||||||
|
background: #2563eb;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.active-view-summary {
|
.active-view-summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -271,12 +278,10 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tree-editor-layout {
|
.tree-editor-layout {
|
||||||
display: grid;
|
display: block;
|
||||||
grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);
|
|
||||||
gap: 0.75rem;
|
|
||||||
min-height: 560px;
|
min-height: 560px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
align-items: start;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-device-sidebar {
|
.project-device-sidebar {
|
||||||
@@ -404,6 +409,38 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.project-device-drawer {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1040;
|
||||||
|
top: 5.5rem;
|
||||||
|
left: 1rem;
|
||||||
|
width: min(360px, calc(100vw - 2rem));
|
||||||
|
max-height: calc(100vh - 7rem);
|
||||||
|
overflow: auto;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 0.75rem 2rem rgba(31, 41, 55, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-device-drawer-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-device-drawer-header span {
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-device-drawer-header button {
|
||||||
|
border: 1px solid #c4cddc;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.25rem 0.4rem;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
|
||||||
.tree-grid .header-sort-btn:hover span:first-child,
|
.tree-grid .header-sort-btn:hover span:first-child,
|
||||||
.tree-grid .header-sort-btn:focus-visible span:first-child {
|
.tree-grid .header-sort-btn:focus-visible span:first-child {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
@@ -783,8 +820,9 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 720px) {
|
||||||
.tree-editor-layout {
|
.project-device-drawer {
|
||||||
grid-template-columns: 1fr;
|
top: 4.5rem;
|
||||||
|
max-height: calc(100vh - 5.5rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export default function CircuitTreeEditPage() {
|
|||||||
const params = useParams<{ projectId: string; circuitListId: string }>();
|
const params = useParams<{ projectId: string; circuitListId: string }>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="container py-4">
|
<main className="container-fluid px-3 px-xxl-4 py-4">
|
||||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
<div className="d-flex flex-wrap justify-content-between align-items-center gap-3 mb-3">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="h4 mb-1">Stromkreisliste</h1>
|
<h1 className="h4 mb-1">Stromkreisliste</h1>
|
||||||
<p className="text-secondary mb-0">Stromkreise und zugeordnete Geräte bearbeiten.</p>
|
<p className="text-secondary mb-0">Stromkreise und zugeordnete Geräte bearbeiten.</p>
|
||||||
|
|||||||
@@ -196,6 +196,8 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
const [activeSectionId, setActiveSectionId] = useState<string | null>(null);
|
const [activeSectionId, setActiveSectionId] = useState<string | null>(null);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [projectDevices, setProjectDevices] = useState<ProjectDeviceDto[]>([]);
|
const [projectDevices, setProjectDevices] = useState<ProjectDeviceDto[]>([]);
|
||||||
|
const [isProjectDeviceDrawerOpen, setIsProjectDeviceDrawerOpen] =
|
||||||
|
useState(false);
|
||||||
const [projectDeviceSearch, setProjectDeviceSearch] = useState("");
|
const [projectDeviceSearch, setProjectDeviceSearch] = useState("");
|
||||||
const [selectedProjectDeviceId, setSelectedProjectDeviceId] = useState<string | null>(null);
|
const [selectedProjectDeviceId, setSelectedProjectDeviceId] = useState<string | null>(null);
|
||||||
const [targetSectionId, setTargetSectionId] = useState<string | null>(null);
|
const [targetSectionId, setTargetSectionId] = useState<string | null>(null);
|
||||||
@@ -2537,6 +2539,19 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
<button type="button" onClick={toggleColumnSettingsMenu} aria-expanded={isColumnMenuOpen}>
|
<button type="button" onClick={toggleColumnSettingsMenu} aria-expanded={isColumnMenuOpen}>
|
||||||
Spalten
|
Spalten
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
aria-controls="project-device-drawer"
|
||||||
|
aria-expanded={isProjectDeviceDrawerOpen}
|
||||||
|
className="project-device-drawer-toggle"
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
setIsProjectDeviceDrawerOpen((current) => !current)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isProjectDeviceDrawerOpen
|
||||||
|
? "Projektgeräte schließen"
|
||||||
|
: `Projektgeräte öffnen (${projectDevices.length})`}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={clearSortAndFilters}
|
onClick={clearSortAndFilters}
|
||||||
@@ -2566,8 +2581,25 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
) : null}
|
) : null}
|
||||||
{isSaving ? <div className="notice info">Wird gespeichert …</div> : null}
|
{isSaving ? <div className="notice info">Wird gespeichert …</div> : null}
|
||||||
<div className="tree-editor-layout">
|
<div className="tree-editor-layout">
|
||||||
<aside className="project-device-sidebar">
|
{isProjectDeviceDrawerOpen ? (
|
||||||
<h3>Projektgeräte</h3>
|
<aside
|
||||||
|
className="project-device-sidebar project-device-drawer"
|
||||||
|
id="project-device-drawer"
|
||||||
|
>
|
||||||
|
<div className="project-device-drawer-header">
|
||||||
|
<div>
|
||||||
|
<h3>Projektgeräte</h3>
|
||||||
|
<span>{projectDevices.length} verfügbar</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
aria-label="Projektgeräte schließen"
|
||||||
|
disabled={draggingProjectDeviceId !== null}
|
||||||
|
onClick={() => setIsProjectDeviceDrawerOpen(false)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Schließen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Name oder Anzeigename suchen …"
|
placeholder="Name oder Anzeigename suchen …"
|
||||||
@@ -2668,6 +2700,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
) : null}
|
||||||
<div
|
<div
|
||||||
className="tree-grid-wrap"
|
className="tree-grid-wrap"
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
|
|||||||
Reference in New Issue
Block a user