feat(foundry): refine map application controls

This commit is contained in:
Codex
2026-08-09 14:24:18 +03:00
parent 7025fd20b5
commit beb84f6de6
18 changed files with 633 additions and 186 deletions
+38 -1
View File
@@ -6,6 +6,7 @@ import {
AppHeader,
ApplicationPanel,
ApplicationShell,
ApplicationSidePanel,
Button,
Checker,
ColorField,
@@ -318,6 +319,9 @@ export function CatalogApp() {
const [mapTemplateSaveState, setMapTemplateSaveState] = useState<"idle" | "loading" | "saving" | "saved" | "error">("loading");
const mapTemplatePreviewRef = useRef<MapFixturePreviewHandle>(null);
const applicationMapPreviewRef = useRef<MapFixturePreviewHandle>(null);
const [mapSettingsPanelHost, setMapSettingsPanelHost] = useState<HTMLDivElement | null>(null);
const [mapHeaderActionsHost, setMapHeaderActionsHost] = useState<HTMLDivElement | null>(null);
const [mapSettingsPanelOpen, setMapSettingsPanelOpen] = useState(false);
const [createModuleOpen, setCreateModuleOpen] = useState(false);
const [createModuleName, setCreateModuleName] = useState("");
const [createModuleSlug, setCreateModuleSlug] = useState("");
@@ -355,6 +359,7 @@ export function CatalogApp() {
const [workspaceWindowDemoOpen, setWorkspaceWindowDemoOpen] = useState(true);
const [workspaceWindowDemoMaximized, setWorkspaceWindowDemoMaximized] = useState(false);
const [workspaceWindowDemoRect, setWorkspaceWindowDemoRect] = useState<WorkspaceWindowRect>({ x: 24, y: 24, width: 340, height: 230 });
const [sidePanelDemoOpen, setSidePanelDemoOpen] = useState(true);
const [createModalOpen, setCreateModalOpen] = useState(false);
const [confirmOpen, setConfirmOpen] = useState(false);
const [shareAccessOpen, setShareAccessOpen] = useState(false);
@@ -1413,6 +1418,23 @@ export function CatalogApp() {
</div>
<p className="catalog-preview__explanation">Rectangle, maximize, close и stacking остаются состоянием приложения; дизайн-система владеет одинаковой bounded-геометрией и доступным управлением.</p>
</Preview>
<Preview title="Application side panel" note="end / push / controlled" className="catalog-preview--wide catalog-side-panel-preview">
<div className="catalog-side-panel-demo">
{sidePanelDemoOpen ? (
<ApplicationSidePanel
eyebrow="MAP / SETTINGS"
title="Настройки карты"
description="Theme-aware application panel"
onClose={() => setSidePanelDemoOpen(false)}
>
<Inspector variant="panel" sections={environmentSections.slice(0, 3)} defaultOpen={[environmentSections[0]?.id ?? ""]} singleOpen />
</ApplicationSidePanel>
) : (
<Button shape="pill" icon={<Icon name="panel" />} onClick={() => setSidePanelDemoOpen(true)}>Открыть правую панель</Button>
)}
</div>
<p className="catalog-preview__explanation">Панель занимает отдельную end-колонку ApplicationShell, сдвигает рабочую область, наследует тему и не входит в z-index stack окон карты.</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>
@@ -1794,6 +1816,9 @@ export function CatalogApp() {
expanded
initialLayout={mapDesignLayout}
features={Object.fromEntries(template.features.map((feature) => [feature.id, feature.required ? true : feature.defaultVisible]))}
settingsPanelHost={mapSettingsPanelHost}
headerActionsHost={mapHeaderActionsHost}
onSettingsPanelOpenChange={setMapSettingsPanelOpen}
/>
<div className="catalog-page-template__actions">
<Button variant="primary" shape="pill" icon={<Icon name="plus" />} onClick={() => { void createApplicationDraft(template); }}>Создать Application Draft</Button>
@@ -1807,7 +1832,12 @@ export function CatalogApp() {
title={template.title}
description={template.description}
>
<MapFixturePreview features={Object.fromEntries(template.features.map((feature) => [feature.id, feature.required ? true : feature.defaultVisible]))} />
<MapFixturePreview
features={Object.fromEntries(template.features.map((feature) => [feature.id, feature.required ? true : feature.defaultVisible]))}
settingsPanelHost={mapSettingsPanelHost}
headerActionsHost={mapHeaderActionsHost}
onSettingsPanelOpenChange={setMapSettingsPanelOpen}
/>
<div className="catalog-page-template__actions">
<Button variant="primary" shape="pill" icon={<Icon name="plus" />} onClick={() => { void createApplicationDraft(template); }}>Создать Application Draft</Button>
<span>Только утверждённые features и slots без свободного canvas.</span>
@@ -1860,6 +1890,9 @@ export function CatalogApp() {
initialLayout={resolvedMapLayout}
features={page.features}
expanded
settingsPanelHost={mapSettingsPanelHost}
headerActionsHost={mapHeaderActionsHost}
onSettingsPanelOpenChange={setMapSettingsPanelOpen}
/>
{applicationMode === "edit" ? (
<SettingsCard eyebrow="PAGE SETTINGS" title={page.title} description={`${template.title} · ${template.version}`}>
@@ -1888,6 +1921,8 @@ export function CatalogApp() {
navigationOpen={guidelineOpen}
contentOpen={workspace.contentOpen}
contentExpanded={panelExpanded}
endPanelOpen={mapSettingsPanelOpen}
endPanel={<div ref={setMapSettingsPanelHost} className="catalog-map-settings-panel-host" />}
header={
<AppHeader
brand={<img src="/nodedc-logo.svg" alt="NODE.DC" />}
@@ -2035,6 +2070,7 @@ export function CatalogApp() {
description={`${activePageTemplate.category} · contract ${activePageTemplate.schemaVersion} · template ${activePageTemplate.version}`}
expanded={panelExpanded}
onExpandedChange={workspace.setContentExpanded}
headerTools={activePageTemplate.id === "map" ? <div ref={setMapHeaderActionsHost} className="catalog-map-header-actions-host" /> : undefined}
utilityActions={activePageTemplate.id === "map" ? [{
label: mapTemplateSaveState === "saving" ? "Сохраняем Map-фрагмент в Design Profile" : mapTemplateSaveState === "error" ? "Повторить сохранение Map-фрагмента" : "Сохранить дизайн Map в Design Profile",
icon: "save",
@@ -2053,6 +2089,7 @@ export function CatalogApp() {
description={activeApplicationPage ? `${activeApplication.metadata.name} · ${activeApplicationPage.template.id}@${activeApplicationPage.template.version}` : `/${activeApplication.metadata.slug} · manifest ${activeApplication.schemaVersion}`}
expanded={panelExpanded}
onExpandedChange={workspace.setContentExpanded}
headerTools={activeApplicationPage?.template.id === "map" ? <div ref={setMapHeaderActionsHost} className="catalog-map-header-actions-host" /> : undefined}
utilityActions={[{
label: applicationSaveState === "saving" ? "Сохранение…" : applicationSaveState === "error" ? "Повторить сохранение" : "Сохранить",
icon: "save",