127 lines
3.5 KiB
TypeScript
127 lines
3.5 KiB
TypeScript
import type { ReactNode } from "react";
|
|
import { translateReactNode, usePlatformLanguage } from "../i18n/LanguageContext";
|
|
import type { PlatformInterfaceIdentityProps } from "../types";
|
|
import DismissibleAlert from "./DismissibleAlert";
|
|
import LoadingFrame from "./LoadingFrame";
|
|
import PageScrollViewport from "./PageScrollViewport";
|
|
import PageTitle from "./PageTitle";
|
|
|
|
export type PageLayoutMode = "standalone" | "embedded";
|
|
|
|
export type PageHeaderProps = {
|
|
title: ReactNode;
|
|
description?: ReactNode;
|
|
actions?: ReactNode;
|
|
loading?: boolean;
|
|
sticky?: boolean;
|
|
className?: string;
|
|
};
|
|
|
|
export function PageHeader({
|
|
title,
|
|
description,
|
|
actions,
|
|
loading = false,
|
|
sticky = true,
|
|
className = ""
|
|
}: PageHeaderProps) {
|
|
const { translateText } = usePlatformLanguage();
|
|
const classes = [
|
|
"page-heading",
|
|
"split",
|
|
sticky ? "workspace-heading" : "",
|
|
"page-layout-header",
|
|
className
|
|
].filter(Boolean).join(" ");
|
|
|
|
return (
|
|
<header className={classes}>
|
|
<div className="page-layout-heading-copy">
|
|
<PageTitle loading={loading}>{title}</PageTitle>
|
|
{description && <p>{translateReactNode(description, translateText)}</p>}
|
|
</div>
|
|
{actions && <div className="button-row compact-actions page-layout-actions">{actions}</div>}
|
|
</header>
|
|
);
|
|
}
|
|
|
|
export type PageLayoutProps = PlatformInterfaceIdentityProps & {
|
|
title: ReactNode;
|
|
description?: ReactNode;
|
|
actions?: ReactNode;
|
|
children: ReactNode;
|
|
loading?: boolean;
|
|
loadingLabel?: string;
|
|
error?: string;
|
|
success?: string;
|
|
mode?: PageLayoutMode;
|
|
scrollable?: boolean;
|
|
stickyHeader?: boolean;
|
|
documentationType?: "user" | "admin";
|
|
className?: string;
|
|
viewportClassName?: string;
|
|
headerClassName?: string;
|
|
bodyClassName?: string;
|
|
};
|
|
|
|
export default function PageLayout({
|
|
title,
|
|
description,
|
|
actions,
|
|
children,
|
|
loading = false,
|
|
loadingLabel = "i18n:govoplan-core.loading_page_data.85fe9edf",
|
|
error = "",
|
|
success = "",
|
|
mode = "standalone",
|
|
scrollable,
|
|
stickyHeader = true,
|
|
documentationType = "user",
|
|
className = "",
|
|
viewportClassName = "",
|
|
headerClassName = "",
|
|
bodyClassName = "",
|
|
interfaceId,
|
|
helpContextId,
|
|
helpModuleId,
|
|
helpTopicId
|
|
}: PageLayoutProps) {
|
|
const shouldScroll = scrollable ?? mode === "standalone";
|
|
const layoutClasses = [
|
|
"page-layout",
|
|
`page-layout-${mode}`,
|
|
mode === "standalone" ? "content-pad workspace-data-page" : "",
|
|
className
|
|
].filter(Boolean).join(" ");
|
|
|
|
const layout = (
|
|
<div
|
|
className={layoutClasses}
|
|
data-help-scope="page"
|
|
data-interface-id={interfaceId}
|
|
data-help-context-id={helpContextId}
|
|
data-help-module-id={helpModuleId}
|
|
data-help-topic-id={helpTopicId}
|
|
data-help-documentation-type={documentationType}
|
|
data-help-key={typeof title === "string" ? title : undefined}
|
|
>
|
|
<PageHeader
|
|
title={title}
|
|
description={description}
|
|
actions={actions}
|
|
loading={loading}
|
|
sticky={stickyHeader}
|
|
className={headerClassName}
|
|
/>
|
|
{error && <DismissibleAlert tone="danger" resetKey={error} floating>{error}</DismissibleAlert>}
|
|
{success && <DismissibleAlert tone="success" resetKey={success} floating>{success}</DismissibleAlert>}
|
|
<LoadingFrame loading={loading} label={loadingLabel} className={["page-layout-body", bodyClassName].filter(Boolean).join(" ")}>
|
|
{children}
|
|
</LoadingFrame>
|
|
</div>
|
|
);
|
|
|
|
if (!shouldScroll) return layout;
|
|
return <PageScrollViewport className={viewportClassName}>{layout}</PageScrollViewport>;
|
|
}
|