Adopt shared WebUI structural primitives
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
# Docs Interface Pattern Migration
|
||||||
|
|
||||||
|
The Docs route composes the platform interface language without owning a
|
||||||
|
parallel shell. Core owns its navigation/content pane geometry and page frame;
|
||||||
|
Docs owns audience filtering, version selection, topic navigation, configured
|
||||||
|
documentation projection, and evidence presentation.
|
||||||
|
|
||||||
|
## Surface contract
|
||||||
|
|
||||||
|
- `WorkspaceLayout` owns the outline/content regions, pane width, scrolling,
|
||||||
|
accessible labels, and narrow-layout navigation behavior.
|
||||||
|
- `PageLayout` owns the content inset, sticky heading, audience-aware page
|
||||||
|
identity, reload action placement, error and loading regions, and responsive
|
||||||
|
action flow.
|
||||||
|
- `ExplorerTree`, `SegmentedControl`, `DataGrid`, `DescriptionList`, dialogs,
|
||||||
|
alerts, and status badges retain their Core interaction and accessibility
|
||||||
|
contracts.
|
||||||
|
- Docs-specific CSS remains limited to the documentation tree, reading column,
|
||||||
|
topic typography, page outline, and reference content. It no longer defines
|
||||||
|
the outer workspace grid or page skeleton.
|
||||||
|
|
||||||
|
The module keeps user/admin audience boundaries, installed-version selection,
|
||||||
|
topic URLs, configured-state projection, and evidence visibility unchanged.
|
||||||
|
The platform layout checker rejects restoring a raw Docs page or workspace
|
||||||
|
frame.
|
||||||
@@ -47,6 +47,11 @@ ARCHITECTURE = ModuleArchitectureDeclaration(
|
|||||||
reference="docs/DOCUMENTATION_LAYER_CONCEPT.md",
|
reference="docs/DOCUMENTATION_LAYER_CONCEPT.md",
|
||||||
summary="Defines the manifest-driven documentation boundary.",
|
summary="Defines the manifest-driven documentation boundary.",
|
||||||
),
|
),
|
||||||
|
ModuleMaturityEvidence(
|
||||||
|
kind="documentation",
|
||||||
|
reference="docs/INTERFACE_PATTERN_MIGRATION.md",
|
||||||
|
summary="Records the Core-owned Docs workspace and page-layout boundary.",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
known_limits=(
|
known_limits=(
|
||||||
"Architecture declarations are in staged adoption, so undeclared modules remain visible as pending.",
|
"Architecture declarations are in staged adoption, so undeclared modules remain visible as pending.",
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
DismissibleAlert,
|
DismissibleAlert,
|
||||||
ExplorerTree,
|
ExplorerTree,
|
||||||
LoadingFrame,
|
PageLayout,
|
||||||
PageTitle,
|
|
||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
|
WorkspaceLayout,
|
||||||
adminErrorMessage,
|
adminErrorMessage,
|
||||||
useGuardedNavigate,
|
useGuardedNavigate,
|
||||||
usePlatformLanguage,
|
usePlatformLanguage,
|
||||||
@@ -133,92 +133,95 @@ export default function DocsPage({ settings }: { settings: ApiSettings }) {
|
|||||||
}, [treeNodes, selectedPage?.id]);
|
}, [treeNodes, selectedPage?.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="workspace module-workspace docs-workspace">
|
<WorkspaceLayout
|
||||||
<aside className="section-sidebar docs-outline" aria-label="i18n:govoplan-docs.documentation_outline.6f836b99">
|
className="module-workspace docs-workspace"
|
||||||
<div className="docs-sidebar-header">
|
primarySize="wide"
|
||||||
<div className="section-title">i18n:govoplan-docs.help_center.f3f3a34b</div>
|
primaryLabel="i18n:govoplan-docs.documentation_outline.6f836b99"
|
||||||
<AudienceToggle
|
contentLabel="i18n:govoplan-docs.help_center.f3f3a34b"
|
||||||
selected={documentationType}
|
documentationType={documentationType}
|
||||||
onSelect={selectDocumentationType}
|
contentClassName="docs-workspace-content"
|
||||||
canViewAdmin={context?.actor.available_documentation_types.includes("admin") ?? documentationType === "admin"}
|
primary={(
|
||||||
/>
|
<aside className="section-sidebar docs-outline" aria-label="i18n:govoplan-docs.documentation_outline.6f836b99">
|
||||||
<label className="docs-version-selector" title={context?.versions.fallback_policy}>
|
<div className="docs-sidebar-header">
|
||||||
<span>Version</span>
|
<div className="section-title">i18n:govoplan-docs.help_center.f3f3a34b</div>
|
||||||
<select
|
<AudienceToggle
|
||||||
value={selectedVersion ?? ""}
|
selected={documentationType}
|
||||||
onChange={(event) => selectVersion(event.target.value || null)}
|
onSelect={selectDocumentationType}
|
||||||
>
|
canViewAdmin={context?.actor.available_documentation_types.includes("admin") ?? documentationType === "admin"}
|
||||||
<option value="">Installed versions</option>
|
/>
|
||||||
{selectedVersion && !context?.versions.supported_versions.includes(selectedVersion) &&
|
<label className="docs-version-selector" title={context?.versions.fallback_policy}>
|
||||||
<option value={selectedVersion}>{selectedVersion} (unsupported)</option>
|
<span>Version</span>
|
||||||
}
|
<select
|
||||||
{(context?.versions.supported_versions ?? []).map((version) => (
|
value={selectedVersion ?? ""}
|
||||||
<option key={version} value={version}>{version}</option>
|
onChange={(event) => selectVersion(event.target.value || null)}
|
||||||
))}
|
>
|
||||||
</select>
|
<option value="">Installed versions</option>
|
||||||
</label>
|
{selectedVersion && !context?.versions.supported_versions.includes(selectedVersion) &&
|
||||||
</div>
|
<option value={selectedVersion}>{selectedVersion} (unsupported)</option>
|
||||||
<nav className="docs-tree" aria-label="i18n:govoplan-docs.documentation_outline.6f836b99">
|
}
|
||||||
<ExplorerTree
|
{(context?.versions.supported_versions ?? []).map((version) => (
|
||||||
nodes={treeNodes}
|
<option key={version} value={version}>{version}</option>
|
||||||
getNodeId={(node) => node.id}
|
))}
|
||||||
getNodeLabel={(node) => node.title}
|
</select>
|
||||||
getNodeChildren={(node) => node.children}
|
</label>
|
||||||
activeId={activeNodeIdForPage(treeNodes, selectedPage?.id ?? "")}
|
|
||||||
expandedIds={expandedNodes}
|
|
||||||
depth={0}
|
|
||||||
childrenBaseClassName="docs-tree-children"
|
|
||||||
nodeContainerClassName="docs-tree-node"
|
|
||||||
nodeWrapBaseClassName="docs-tree-row"
|
|
||||||
toggleBaseClassName="docs-tree-toggle"
|
|
||||||
nodeButtonBaseClassName="docs-tree-page"
|
|
||||||
getNodeWrapStyle={(_node, context) => ({ paddingLeft: `${context.depth * 14}px` })}
|
|
||||||
getNodeButtonClassName={(node) => selectedPage?.id === node.page.id ? "is-active" : ""}
|
|
||||||
renderToggleIcon={(_node, context) => context.hasChildren ? context.expanded ? <ChevronDown size={15} /> : <ChevronRight size={15} /> : <span className="docs-tree-toggle-placeholder" />}
|
|
||||||
renderNodeContent={(node) => node.title}
|
|
||||||
onToggle={(node) => toggleNode(node.id)}
|
|
||||||
onOpen={(node) => selectPage(node.page)}
|
|
||||||
/>
|
|
||||||
</nav>
|
|
||||||
</aside>
|
|
||||||
<section className="workspace-content docs-workspace-content">
|
|
||||||
<div className="content-pad workspace-data-page docs-page">
|
|
||||||
<div className="page-heading split workspace-heading">
|
|
||||||
<div>
|
|
||||||
<PageTitle loading={loading}>i18n:govoplan-docs.help_center.f3f3a34b</PageTitle>
|
|
||||||
<p>{adminDocs ? "i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e" : "i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad"}</p>
|
|
||||||
</div>
|
|
||||||
<div className="button-row compact-actions">
|
|
||||||
<Button onClick={() => void load()} disabled={loading}><RefreshCw size={16} /> i18n:govoplan-docs.reload.cce71553</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<nav className="docs-tree" aria-label="i18n:govoplan-docs.documentation_outline.6f836b99">
|
||||||
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
<ExplorerTree
|
||||||
|
nodes={treeNodes}
|
||||||
<LoadingFrame loading={loading} label="i18n:govoplan-docs.loading_documentation_context.1c091645">
|
getNodeId={(node) => node.id}
|
||||||
<div className="docs-content">
|
getNodeLabel={(node) => node.title}
|
||||||
<main className="docs-page-main">
|
getNodeChildren={(node) => node.children}
|
||||||
<SelectedPageContent
|
activeId={activeNodeIdForPage(treeNodes, selectedPage?.id ?? "")}
|
||||||
page={selectedPage}
|
expandedIds={expandedNodes}
|
||||||
adminDocs={adminDocs}
|
depth={0}
|
||||||
documentationType={documentationType}
|
childrenBaseClassName="docs-tree-children"
|
||||||
topicById={topicById}
|
nodeContainerClassName="docs-tree-node"
|
||||||
moduleRows={moduleRows}
|
nodeWrapBaseClassName="docs-tree-row"
|
||||||
visibleRoutes={visibleRoutes}
|
toggleBaseClassName="docs-tree-toggle"
|
||||||
availableRoutes={availableRoutes}
|
nodeButtonBaseClassName="docs-tree-page"
|
||||||
grantedPermissions={grantedPermissions}
|
getNodeWrapStyle={(_node, context) => ({ paddingLeft: `${context.depth * 14}px` })}
|
||||||
evidenceModules={context?.layers.evidence.optional_modules ?? []}
|
getNodeButtonClassName={(node) => selectedPage?.id === node.page.id ? "is-active" : ""}
|
||||||
evidenceSources={context?.layers.evidence.sources ?? []}
|
renderToggleIcon={(_node, context) => context.hasChildren ? context.expanded ? <ChevronDown size={15} /> : <ChevronRight size={15} /> : <span className="docs-tree-toggle-placeholder" />}
|
||||||
settings={settings}
|
renderNodeContent={(node) => node.title}
|
||||||
locale={locale}
|
onToggle={(node) => toggleNode(node.id)}
|
||||||
/>
|
onOpen={(node) => selectPage(node.page)}
|
||||||
</main>
|
/>
|
||||||
<PageOutline items={outlineItems} />
|
</nav>
|
||||||
</div>
|
</aside>
|
||||||
</LoadingFrame>
|
)}
|
||||||
|
>
|
||||||
|
<PageLayout
|
||||||
|
title="i18n:govoplan-docs.help_center.f3f3a34b"
|
||||||
|
description={adminDocs ? "i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e" : "i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad"}
|
||||||
|
actions={<Button onClick={() => void load()} disabled={loading}><RefreshCw size={16} /> i18n:govoplan-docs.reload.cce71553</Button>}
|
||||||
|
loading={loading}
|
||||||
|
loadingLabel="i18n:govoplan-docs.loading_documentation_context.1c091645"
|
||||||
|
error={error}
|
||||||
|
mode="workspace"
|
||||||
|
documentationType={documentationType}
|
||||||
|
className="docs-page"
|
||||||
|
>
|
||||||
|
<div className="docs-content">
|
||||||
|
<main className="docs-page-main">
|
||||||
|
<SelectedPageContent
|
||||||
|
page={selectedPage}
|
||||||
|
adminDocs={adminDocs}
|
||||||
|
documentationType={documentationType}
|
||||||
|
topicById={topicById}
|
||||||
|
moduleRows={moduleRows}
|
||||||
|
visibleRoutes={visibleRoutes}
|
||||||
|
availableRoutes={availableRoutes}
|
||||||
|
grantedPermissions={grantedPermissions}
|
||||||
|
evidenceModules={context?.layers.evidence.optional_modules ?? []}
|
||||||
|
evidenceSources={context?.layers.evidence.sources ?? []}
|
||||||
|
settings={settings}
|
||||||
|
locale={locale}
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
<PageOutline items={outlineItems} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</PageLayout>
|
||||||
</div>
|
</WorkspaceLayout>
|
||||||
);
|
);
|
||||||
|
|
||||||
function selectDocumentationType(type: DocumentationType) {
|
function selectDocumentationType(type: DocumentationType) {
|
||||||
|
|||||||
Reference in New Issue
Block a user