Define semantic page action layouts
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import type { HTMLAttributes, ReactNode } from "react";
|
||||
import type { PlatformInterfaceIdentityProps } from "../types";
|
||||
import ActionToolbar, { ToolbarGroup } from "./ActionToolbar";
|
||||
|
||||
type PageActionBarCommonProps = PlatformInterfaceIdentityProps &
|
||||
Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
||||
reloadAction: ReactNode;
|
||||
contextActions?: ReactNode;
|
||||
helpAction?: ReactNode;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export type CollectionPageActionBarProps = PageActionBarCommonProps & {
|
||||
variant: "collection";
|
||||
createAction?: ReactNode;
|
||||
};
|
||||
|
||||
export type DetailPageActionBarProps = PageActionBarCommonProps & {
|
||||
variant: "detail";
|
||||
primaryActions?: ReactNode;
|
||||
consequentialActions?: ReactNode;
|
||||
};
|
||||
|
||||
export type EditorPageActionBarProps = PageActionBarCommonProps & {
|
||||
variant: "editor";
|
||||
discardAction: ReactNode;
|
||||
saveAction: ReactNode;
|
||||
};
|
||||
|
||||
export type PageActionBarProps =
|
||||
| CollectionPageActionBarProps
|
||||
| DetailPageActionBarProps
|
||||
| EditorPageActionBarProps;
|
||||
|
||||
function ActionSlot({ name, children }: { name: string; children: ReactNode }) {
|
||||
return (
|
||||
<span className={`page-action-slot page-action-slot-${name}`} data-page-action-slot={name}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const defaultLabels = {
|
||||
collection: "Collection page actions",
|
||||
detail: "Detail page actions",
|
||||
editor: "Editor page actions"
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Semantic action placement for headed pages.
|
||||
*
|
||||
* The named slots deliberately encode ordering. Product modules still own the
|
||||
* actions, wording, permissions, blockers, and consequences placed in them.
|
||||
*/
|
||||
export default function PageActionBar(props: PageActionBarProps) {
|
||||
const normalizedProps = props as PageActionBarProps & {
|
||||
createAction?: ReactNode;
|
||||
primaryActions?: ReactNode;
|
||||
consequentialActions?: ReactNode;
|
||||
discardAction?: ReactNode;
|
||||
saveAction?: ReactNode;
|
||||
};
|
||||
const {
|
||||
variant,
|
||||
reloadAction,
|
||||
contextActions,
|
||||
helpAction,
|
||||
createAction,
|
||||
primaryActions,
|
||||
consequentialActions,
|
||||
discardAction,
|
||||
saveAction,
|
||||
label,
|
||||
className = "",
|
||||
interfaceId,
|
||||
helpContextId,
|
||||
helpModuleId,
|
||||
helpTopicId,
|
||||
...toolbarProps
|
||||
} = normalizedProps;
|
||||
|
||||
let trailingActions: ReactNode;
|
||||
if (variant === "collection") {
|
||||
trailingActions = createAction ? <ActionSlot name="create">{createAction}</ActionSlot> : null;
|
||||
} else if (variant === "detail") {
|
||||
trailingActions = (
|
||||
<>
|
||||
{primaryActions ? <ActionSlot name="primary">{primaryActions}</ActionSlot> : null}
|
||||
{consequentialActions ? <ActionSlot name="consequential">{consequentialActions}</ActionSlot> : null}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
trailingActions = (
|
||||
<>
|
||||
<ActionSlot name="discard">{discardAction}</ActionSlot>
|
||||
<ActionSlot name="save">{saveAction}</ActionSlot>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ActionToolbar
|
||||
{...toolbarProps}
|
||||
className={["page-action-bar", `page-action-bar-${variant}`, className].filter(Boolean).join(" ")}
|
||||
density="compact"
|
||||
justify="between"
|
||||
wrap="responsive"
|
||||
label={label ?? defaultLabels[variant]}
|
||||
interfaceId={interfaceId}
|
||||
helpContextId={helpContextId}
|
||||
helpModuleId={helpModuleId}
|
||||
helpTopicId={helpTopicId}
|
||||
data-page-action-archetype={variant}
|
||||
>
|
||||
<ToolbarGroup className="page-action-bar-leading" data-page-action-group="leading">
|
||||
<ActionSlot name="reload">{reloadAction}</ActionSlot>
|
||||
{contextActions ? <ActionSlot name="context">{contextActions}</ActionSlot> : null}
|
||||
</ToolbarGroup>
|
||||
<ToolbarGroup className="page-action-bar-trailing" align="end" data-page-action-group="trailing">
|
||||
{helpAction ? <ActionSlot name="help">{helpAction}</ActionSlot> : null}
|
||||
{trailingActions}
|
||||
</ToolbarGroup>
|
||||
</ActionToolbar>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user