feat(ui): add bounded workspace windows
This commit is contained in:
@@ -43,9 +43,11 @@ import {
|
||||
useApplicationWorkspace,
|
||||
Window,
|
||||
WindowFooterActions,
|
||||
WorkspaceWindow,
|
||||
type IconName,
|
||||
type ShareAccessMember,
|
||||
type ToolbarPlacement,
|
||||
type WorkspaceWindowRect,
|
||||
} from "@nodedc/ui-react";
|
||||
import { applyFaviconAssets, createIcoBlob, generateFavicons, type FaviconAssetUrls, type GeneratedFavicon } from "./favicon.js";
|
||||
import {
|
||||
@@ -371,6 +373,10 @@ export function CatalogApp() {
|
||||
const [strokeColor, setStrokeColor] = useState("#2b2b36");
|
||||
const [strokeOpacity, setStrokeOpacity] = useState(100);
|
||||
const [inspectorOpen, setInspectorOpen] = useState(false);
|
||||
const workspaceWindowDemoRef = useRef<HTMLDivElement>(null);
|
||||
const [workspaceWindowDemoOpen, setWorkspaceWindowDemoOpen] = useState(true);
|
||||
const [workspaceWindowDemoMaximized, setWorkspaceWindowDemoMaximized] = useState(false);
|
||||
const [workspaceWindowDemoRect, setWorkspaceWindowDemoRect] = useState<WorkspaceWindowRect>({ x: 24, y: 24, width: 340, height: 230 });
|
||||
const [createModalOpen, setCreateModalOpen] = useState(false);
|
||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||
const [shareAccessOpen, setShareAccessOpen] = useState(false);
|
||||
@@ -1296,6 +1302,44 @@ export function CatalogApp() {
|
||||
<IconButton label="Закрыть"><Icon name="close" /></IconButton>
|
||||
</div>
|
||||
</Preview>
|
||||
<Preview title="Workspace window" note="inline / bounded / controlled" className="catalog-preview--wide catalog-workspace-window-preview">
|
||||
<div ref={workspaceWindowDemoRef} className="catalog-workspace-window-demo">
|
||||
{workspaceWindowDemoOpen ? (
|
||||
<WorkspaceWindow
|
||||
boundsRef={workspaceWindowDemoRef}
|
||||
rect={workspaceWindowDemoRect}
|
||||
onRectChange={setWorkspaceWindowDemoRect}
|
||||
maximized={workspaceWindowDemoMaximized}
|
||||
onMaximizedChange={setWorkspaceWindowDemoMaximized}
|
||||
onActivate={() => undefined}
|
||||
onClose={() => {
|
||||
setWorkspaceWindowDemoOpen(false);
|
||||
setWorkspaceWindowDemoMaximized(false);
|
||||
}}
|
||||
title="Визуальный канал"
|
||||
subtitle="WORKSPACE / AUXILIARY VIEW"
|
||||
status="LIVE"
|
||||
footer={<span className="catalog-workspace-window-demo__hint">Стрелки — позиция · Shift + стрелки — 10 px</span>}
|
||||
active
|
||||
zIndex={2}
|
||||
>
|
||||
<div className="catalog-workspace-window-demo__media">
|
||||
<Icon name="video" size={24} />
|
||||
<strong>Рабочая поверхность</strong>
|
||||
<span>Перемещается, изменяет размер и разворачивается только внутри сцены.</span>
|
||||
</div>
|
||||
</WorkspaceWindow>
|
||||
) : (
|
||||
<Button
|
||||
className="catalog-workspace-window-demo__reopen"
|
||||
shape="pill"
|
||||
icon={<Icon name="plus" />}
|
||||
onClick={() => setWorkspaceWindowDemoOpen(true)}
|
||||
>Открыть окно</Button>
|
||||
)}
|
||||
</div>
|
||||
<p className="catalog-preview__explanation">Rectangle, maximize, close и stacking остаются состоянием приложения; дизайн-система владеет одинаковой bounded-геометрией и доступным управлением.</p>
|
||||
</Preview>
|
||||
<Preview title="Inspector" note="modeless / draggable" className="catalog-preview--wide catalog-preview--compact catalog-inspector-launcher">
|
||||
<p className="catalog-preview__explanation">Настройки приложения и Toolbar открываются в отдельном перемещаемом Inspector и сохраняются одним серверным layout.</p>
|
||||
<Button variant="primary" shape="pill" icon={<Icon name="panel" />} onClick={() => setInspectorOpen(true)}>Открыть Inspector</Button>
|
||||
|
||||
@@ -897,6 +897,65 @@ textarea {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.catalog-workspace-window-preview .catalog-preview__body {
|
||||
align-content: stretch;
|
||||
}
|
||||
|
||||
.catalog-workspace-window-demo {
|
||||
position: relative;
|
||||
min-height: 24rem;
|
||||
overflow: hidden;
|
||||
border-radius: var(--nodedc-radius-card);
|
||||
background:
|
||||
linear-gradient(color-mix(in srgb, var(--nodedc-text-primary) 5%, transparent) 1px, transparent 1px),
|
||||
linear-gradient(90deg, color-mix(in srgb, var(--nodedc-text-primary) 5%, transparent) 1px, transparent 1px),
|
||||
color-mix(in srgb, var(--nodedc-canvas) 88%, var(--nodedc-glass-panel-bg-soft));
|
||||
background-size: 2.5rem 2.5rem;
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--nodedc-text-primary) 8%, transparent);
|
||||
}
|
||||
|
||||
.catalog-workspace-window-demo .nodedc-workspace-window__body {
|
||||
display: grid;
|
||||
padding: 0.25rem 0.75rem 0.5rem;
|
||||
}
|
||||
|
||||
.catalog-workspace-window-demo__media {
|
||||
display: grid;
|
||||
min-height: 0;
|
||||
place-content: center;
|
||||
justify-items: center;
|
||||
gap: 0.45rem;
|
||||
border-radius: calc(var(--nodedc-radius-card) * 0.72);
|
||||
background:
|
||||
radial-gradient(circle at 62% 30%, color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 18%, transparent), transparent 38%),
|
||||
color-mix(in srgb, var(--nodedc-canvas) 72%, transparent);
|
||||
color: var(--nodedc-text-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.catalog-workspace-window-demo__media strong {
|
||||
color: var(--nodedc-text-primary);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.catalog-workspace-window-demo__media span,
|
||||
.catalog-workspace-window-demo__hint {
|
||||
color: var(--nodedc-text-muted);
|
||||
font-size: 0.66rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.catalog-workspace-window-demo__media span {
|
||||
max-width: 16rem;
|
||||
}
|
||||
|
||||
.catalog-workspace-window-demo__reopen {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.catalog-inspector-launcher .nodedc-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user