mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
feat(ui): portable, resizable rail panels for CCTV
A drag spec can now be `portable`: dragging the panel by its header lifts
it out of the right rail into a fixed window, resizable from all four
edges and corners, that remembers its place and size under the existing
godsEyeView.v8.panelPos.<id> key ({ left, top, width?, height?, floating })
and snaps back on a header double-click. Only CCTV opts in here; the rail
skips .panel-floating panels when measuring and allocating, and its
placement rules in layers.css are guarded so no !important is needed.
PanelPositionControls gains an optional onPanelResized hook, threaded
through PanelChrome to a no-op on the application shell.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
317c0a7e0c
commit
96a1e710ea
9 files changed
+525
-26
No files matched your search
@@ -103,6 +103,116 @@
|
||||
opacity: 0.96;
|
||||
}
|
||||
|
||||
/* ── Portable rail panels ────────────────────
|
||||
A header drag lifts a portable panel (PanelPositionControls) out of its
|
||||
rail into a fixed window that remembers its place and size. The rail's
|
||||
own placement rules in layers.css are guarded with :not(.panel-floating),
|
||||
so these only have to beat a panel's base placement (one id). */
|
||||
.panel-portable .panel-header {
|
||||
cursor: grab;
|
||||
}
|
||||
.panel-portable.panel-dragging .panel-header {
|
||||
cursor: grabbing;
|
||||
}
|
||||
#right-context-rail > .panel-floating {
|
||||
position: fixed;
|
||||
inset: auto;
|
||||
max-height: calc(100vh - 12px);
|
||||
margin: 0;
|
||||
flex: none;
|
||||
pointer-events: auto;
|
||||
}
|
||||
/* A collapsed window keeps its stored height for later but shows only its
|
||||
header strip, so the resize handles have nothing to attach to. */
|
||||
#right-context-rail > .panel-floating.collapsed {
|
||||
max-height: max-content;
|
||||
}
|
||||
.panel-floating[style*='height'] > .cctv-panel-inner {
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
max-height: none;
|
||||
}
|
||||
.panel-resize-edge,
|
||||
.panel-resize-grip {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
touch-action: none;
|
||||
}
|
||||
.panel-resize-edge[data-dir='n'],
|
||||
.panel-resize-edge[data-dir='s'] {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
height: 7px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
.panel-resize-edge[data-dir='n'] {
|
||||
top: -3px;
|
||||
}
|
||||
.panel-resize-edge[data-dir='s'] {
|
||||
bottom: -3px;
|
||||
}
|
||||
.panel-resize-edge[data-dir='e'],
|
||||
.panel-resize-edge[data-dir='w'] {
|
||||
top: 10px;
|
||||
bottom: 10px;
|
||||
width: 7px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.panel-resize-edge[data-dir='e'] {
|
||||
right: -3px;
|
||||
}
|
||||
.panel-resize-edge[data-dir='w'] {
|
||||
left: -3px;
|
||||
}
|
||||
.panel-resize-edge[data-dir='ne'],
|
||||
.panel-resize-edge[data-dir='nw'],
|
||||
.panel-resize-edge[data-dir='sw'] {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.panel-resize-edge[data-dir='ne'] {
|
||||
top: -3px;
|
||||
right: -3px;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
.panel-resize-edge[data-dir='nw'] {
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
.panel-resize-edge[data-dir='sw'] {
|
||||
bottom: -3px;
|
||||
left: -3px;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
.panel-resize-grip {
|
||||
right: 3px;
|
||||
bottom: 3px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: nwse-resize;
|
||||
opacity: 0.55;
|
||||
clip-path: polygon(100% 0, 100% 100%, 0 100%);
|
||||
background: repeating-linear-gradient(
|
||||
135deg,
|
||||
transparent 0 3px,
|
||||
rgba(0, 212, 255, 0.6) 3px 4px
|
||||
);
|
||||
transition: opacity var(--transition-fast);
|
||||
}
|
||||
.panel-portable:hover .panel-resize-grip,
|
||||
.panel-resizing .panel-resize-grip {
|
||||
opacity: 1;
|
||||
}
|
||||
.panel-collapsible.collapsed > .panel-resize-edge,
|
||||
.panel-collapsible.collapsed > .panel-resize-grip {
|
||||
display: none;
|
||||
}
|
||||
.panel-resizing,
|
||||
.panel-resizing * {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 9px;
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
max-height var(--transition-fast);
|
||||
}
|
||||
#right-context-rail > #pp-toggles,
|
||||
#right-context-rail > #cctv-panel,
|
||||
#right-context-rail > #cctv-panel:not(.panel-floating),
|
||||
#right-context-rail > #global-context-panel {
|
||||
position: relative;
|
||||
inset: auto;
|
||||
@@ -173,12 +173,12 @@
|
||||
max-height: 100%;
|
||||
pointer-events: auto;
|
||||
}
|
||||
#right-context-rail #cctv-panel {
|
||||
#right-context-rail #cctv-panel:not(.panel-floating) {
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
pointer-events: auto;
|
||||
}
|
||||
#right-context-rail #cctv-panel.collapsed {
|
||||
#right-context-rail #cctv-panel.collapsed:not(.panel-floating) {
|
||||
width: var(--right-display-collapsed-width);
|
||||
margin-left: auto;
|
||||
}
|
||||
@@ -234,18 +234,22 @@
|
||||
#right-context-rail.layout-focus {
|
||||
height: var(--right-stack-max-height);
|
||||
}
|
||||
#right-context-rail.layout-exclusive > [data-panel-id].collapsed {
|
||||
#right-context-rail.layout-exclusive
|
||||
> [data-panel-id].collapsed:not(.panel-floating) {
|
||||
display: none;
|
||||
}
|
||||
#right-context-rail.layout-focus > [data-panel-id].collapsed {
|
||||
#right-context-rail.layout-focus
|
||||
> [data-panel-id].collapsed:not(.panel-floating) {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
#right-context-rail.layout-focus.layout-exclusive > [data-panel-id].collapsed {
|
||||
#right-context-rail.layout-focus.layout-exclusive
|
||||
> [data-panel-id].collapsed:not(.panel-floating) {
|
||||
display: none;
|
||||
}
|
||||
#right-context-rail.layout-focus > #pp-toggles:not(.collapsed),
|
||||
#right-context-rail.layout-focus > #cctv-panel:not(.collapsed),
|
||||
#right-context-rail.layout-focus
|
||||
> #cctv-panel:not(.collapsed):not(.panel-floating),
|
||||
#right-context-rail.layout-focus > #global-context-panel:not(.collapsed) {
|
||||
flex: 0 1 var(--right-panel-allocated-height);
|
||||
height: var(--right-panel-allocated-height);
|
||||
@@ -259,7 +263,7 @@
|
||||
scrollbar-color: rgba(0, 212, 255, 0.4) transparent;
|
||||
}
|
||||
#right-context-rail.layout-focus
|
||||
> #cctv-panel:not(.collapsed)
|
||||
> #cctv-panel:not(.collapsed):not(.panel-floating)
|
||||
.cctv-panel-inner,
|
||||
#right-context-rail.layout-focus
|
||||
> #global-context-panel:not(.collapsed)
|
||||
|
||||
@@ -202,8 +202,8 @@
|
||||
}
|
||||
|
||||
#right-context-rail #pp-toggles.collapsed .pp-header-row,
|
||||
#right-context-rail #cctv-panel,
|
||||
#right-context-rail #cctv-panel.collapsed,
|
||||
#right-context-rail #cctv-panel:not(.panel-floating),
|
||||
#right-context-rail #cctv-panel.collapsed:not(.panel-floating),
|
||||
#right-context-rail #radio-panel,
|
||||
#right-context-rail #radio-panel.collapsed,
|
||||
#right-context-rail #global-context-panel,
|
||||
|
||||
Reference in new issue
Block a user