カスタムナビゲーションの作成
フロントエンドフレームワークにおけるナビゲーションは、ナビゲーションブロックという特別なブロックによって処理され、アプリケーションの基本構造を定義します。私たちは強力なデフォルトのナビゲーションブロックを提供していますが、独自のナビゲーションコンポーネントを作成して、カスタムナビゲーションを実装することも可能です。
例えば、異なるナビゲーションライブラリを使用したり、デフォルトのサイド+トップナビゲーションとは異なるページ構造を実装したカスタムナビゲーションコンポーネントを作成できます。
このガイドでは、まずデフォルトのナビゲーションブロックの使用方法を説明し、その後、カスタムナビゲーションブロックの作成方法を示します。
デフォルトのナビゲーションブロックの使用
デフォルトのナビゲーションブロックは次のように使用します。
import { createNavigation } from '@basaldev/blocks-frontend-framework';
...
{
navigationComponent: createNavigation({
headerElements: (
isLoggedIn: boolean
): NavigationHeaderElement => {
if (isLoggedIn) {
return ...;
}
return ...;
},
headerLogoElement: (onNavigate) => (
<Link href="/" onNavigate={onNavigate}>
<img ... />
</Link>
),
menuNavigationBlocks: [
...
],
sideNavigationBlocks: [
...
],
}),
}
createNavigation
関数は、次のプロパティを持つオブジェクトを受け取ります:
headerElements
: ユーザーがログインしているかどうかに基づいてヘッダー要素を返す関数。これにより、メニューの横に表示されるボタンやリンクを返すことができます。headerLogoElement
: ロゴ要素を返す関数。これは、ページの左上にレンダリングされるReact要素です。menuNavigationBlocks
: 上部右側のドロップダウンメニューにレンダリングされるナビゲーションブロックの配列。詳細はブロックの実装と使用ガイドを参照してください。sideNavigationBlocks
: サイドナビゲーションバーにレンダリングされるナビゲーションブロックの配列。詳細はブロックの実装と使用ガイドを参照してください。
カスタムナビゲーションブロックの作成
ナビゲーションは、次のプロパティを受け取るコンポーネントを作成することで実装できます:
blockProps
: 表示されている現在のブロックのプロパティ。これを使用して、現在のブロックのnavigationOptions
に基づいてナビゲーションを変更したり、セッション状態やナビゲーションをトリガーするコールバックなどの情報を使用できます。breadcrumbs
: 現在のページのパンくずリストを表示するための配列。これは、現在のページのparentBlockPath
に基づいて、現在の階層内のすべてのページのタイトルとパスに基づいています。children
: ナビゲーションコンポーネント内でレンダリングされるブロックページ。
以下は、デフォルトのナビゲーションコンポーネントがどのように実装されているかを示します。
export type NavigationHeaderElement =
| {
badges?: Array<null | BadgeProps>;
icons: IconProps[];
links?: Array<{ to: string }>;
type: 'icons';
}
| {
buttons: ButtonProps[];
links?: Array<{ to: string }>;
type: 'buttons';
}
| {
type: 'text';
value: string;
};
export interface NavigationOptions {
footerNavigationItems?: Array<{
text: string;
to: string;
}>;
headerElements?: (isLoggedIn: boolean) => NavigationHeaderElement;
headerLogoElement?: (
onNavigate: BlockComponentProps['onNavigate']
) => React.ReactNode;
menuNavigationBlocks?: Array<React.ComponentType<BlockComponentProps>>;
sideNavigationBlocks?: Array<React.ComponentType<BlockComponentProps>>;
}
export const createNavigation = (options: NavigationOptions) => {
const {
headerElements,
headerLogoElement,
menuNavigationBlocks,
sideNavigationBlocks,
} = options;
const Navigation: React.FC<NavigationComponentProps> = ({
children,
breadcrumbs,
blockProps,
}) => {
const headerType =
blockProps.matchedBlockPage?.navigationOptions?.topBarType ?? 'standard';
const hideBorder =
blockProps.matchedBlockPage?.navigationOptions?.hideTopBarBorder ?? false;
const hideFooter =
blockProps.matchedBlockPage?.navigationOptions?.hideFooter ?? false;
const hideSideNavigation =
blockProps.matchedBlockPage?.navigationOptions?.hideSideNavigation ??
false;
const keepSearchParamsOnBack =
blockProps.matchedBlockPage?.navigationOptions?.keepSearchParamsOnBack ??
false;
const searchParams = blockProps.searchParams;
const isLoggedIn = blockProps.sessionState.isLoggedIn;
const isMobile = blockProps.screenMode === 'mobile';
const [isMenuOpen, setIsMenuOpen] = useState(false);
const onMenuOpen = useCallback(
() => setIsMenuOpen(!isMenuOpen),
[isMenuOpen, setIsMenuOpen]
);
const [leftElement, centerElement, rightElement] = useNavigationBarElements(
{
breadcrumbs,
headerElements,
headerLogoElement,
headerType,
isLoggedIn,
isMobile,
keepSearchParamsOnBack,
onMenuOpen,
onNavigate: blockProps.onNavigate,
searchParams,
urlForRoute: blockProps.urlForRoute,
}
);
const [isSidebarShrink, setShouldSidebarShrink] = useSideNavigationShrink(
blockProps.screenMode
);
const sideNavigationItems = sideNavigationBlocks?.map(
(SideBlock, index) => <SideBlock key={index} {...blockProps} />
);
const menuItems = menuNavigationBlocks?.map((MenuBlock, index) => (
<MenuBlock key={index} {...blockProps} />
));
return (
<div
className={classNames(classes.body, bodyClasses[blockProps.screenMode])}
>
<div
className={classNames(
classes.bodyColumn,
navigationColumnClasses[blockProps.screenMode]
)}
>
<NavigationBar
className={classNames(
classes.navBar,
isMobile ? classes.navBarMobile : undefined
)}
backgroundColor={
headerType === 'singleItem' ? 'secondary' : 'primary'
}
leftElement={leftElement}
centerElement={centerElement}
rightElement={rightElement}
onNavigate={blockProps.onNavigate}
hideBorder={hideBorder}
screenMode={isMobile ? 'mobile' : 'desktop'}
/>
<div className={classes.sidebarRow}>
{!hideSideNavigation &&
!isMobile &&
!!sideNavigationItems &&
sideNavigationItems.length > 0 && (
<SideNavigation
className={classes.sidebarRowSidebar}
backgroundColor="tertiary"
items={sideNavigationItems}
onShrinkToggle={() => {
setShouldSidebarShrink(
isSidebarShrink === 'shrink' ? 'expand' : 'shrink'
);
}}
isShrinkable={true}
isShrunk={isSidebarShrink === 'shrink'}
/>
)}
<main className={classes.sidebarRowMain}>{children}</main>
</div>
{blockProps.screenMode !== 'mobile' && menuItems && (
<Menu
anchorToClassName="headerLogoRightIcon"
isMenuOpen={isMenuOpen}
items={menuItems}
size="wide"
onMenuOpenUpdate={onMenuOpen}
onNavigate={blockProps.onNavigate}
/>
)}
{blockProps.screenMode === 'mobile' && !hideFooter && (
<Footer
footerPath={breadcrumbs}
onNavigate={blockProps.onNavigate}
/>
)}
{blockProps.screenMode === 'mobile' && isMenuOpen && (
<>
<div
className={classes.mobileOverlay}
onClick={() => setIsMenuOpen(false)}
/>
<Icon
className={classes.mobileOverlayClose}
iconSize="medium"
icon="close"
color="surface-primary"
onClick={() => setIsMenuOpen(false)}
/>
</>
)}
{blockProps.screenMode === 'mobile' && !hideSideNavigation && (
<SideNavigation
className={classNames(
classes.mobileSidebar,
isMenuOpen ? classes.mobileSidebarOpen : undefined
)}
backgroundColor="primary"
items={sideNavigationItems ?? []}
isShrinkable={false}
/>
)}
</div>
</div>
);
};
return Navigation;
};
useNavigationBarElements
は、ナビゲーションのオプションに基づいてJSXを生成するフックです。アプリケーション内では、これほど動的なものを作成する必要はないでしょう。そのため、ハードコーディングされたJSXを直接実装することもできます。