サイドナビゲーションブロック
SideNavigationコンポーネントは、ReactとTypeScriptで構築された完全にカスタマイズ可能でアクセシブルなサイドナビゲーションメニューです。モダンなデザインパターン、モバイルレスポンシブ動作、フローティングと固定モード、柔軟なカスタマイズオプションを備えた完全なナビゲーションインターフェースを提供します。
🚀 インストール
npm install @nodeblocks/frontend-side-navigation-block@0.2.0
📖 使用法
import {SideNavigation} from '@nodeblocks/frontend-side-navigation-block';
- 基本的な使用法
- 高度な使用法
function BasicSideNavigation() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const navigationHeader = {
icon: 'home' as const,
text: 'ホーム',
href: '#home',
};
const navigationLinks = [
{icon: 'dashboard' as const, text: 'ダッシュボード', href: '#dashboard'},
{icon: 'shopping_cart' as const, text: 'プロダクト', href: '#products'},
{icon: 'reorder' as const, text: '注文', href: '#orders'},
{icon: 'settings' as const, text: '設定', href: '#settings'},
{icon: 'help' as const, text: 'サポート', href: '#support'},
];
return (
<SideNavigation
isFloating={true}
isMenuOpen={isMenuOpen}
setIsMenuOpen={setIsMenuOpen}
header={navigationHeader}
links={navigationLinks}>
<SideNavigation.Links />
</SideNavigation>
);
}
function AdvancedSideNavigation() {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const navigationHeader = {
icon: 'home' as const,
text: 'ホーム',
href: '#home',
};
const navigationLinks = [
{icon: '📊' as const, text: 'ダッシュボード', href: '#dashboard', color: '#007bff'},
{icon: '🛍️' as const, text: 'プロダクト', href: '#products', color: '#007bff'},
{icon: '📦' as const, text: '注文', href: '#orders', color: '#007bff'},
{icon: '⚙️' as const, text: '設定', href: '#settings', color: '#007bff'},
{icon: '💬' as const, text: 'サポート', href: '#support', color: '#007bff'},
];
return (
<SideNavigation
isFloating={true}
isMenuOpen={isMenuOpen}
setIsMenuOpen={setIsMenuOpen}
header={navigationHeader}
links={[]} // オーバーライド関数内で使用する
>
{({defaultBlocks, defaultBlockOrder}) => {
return {
blocks: {
...defaultBlocks,
// 🧭 完全なコンポーネントオーバーライド付きリッチナビゲーションリンク
links: {
...defaultBlocks.links,
props: {
...defaultBlocks.links.props,
className: 'custom-links',
children: (
<nav
style={{
display: 'flex',
flexDirection: 'column',
gap: '8px',
padding: '20px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
minHeight: '100vh',
width: isMenuOpen ? '280px' : '60px',
transition: 'all 0.3s ease',
position: 'relative',
overflow: 'hidden',
}}
>
{/* ヘッダーセクション */}
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '12px',
padding: '16px 12px',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderRadius: '12px',
marginBottom: '20px',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.2)',
}}
>
<div
style={{
width: '40px',
height: '40px',
backgroundColor: '#fff',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '1.2rem',
color: '#667eea',
fontWeight: 'bold',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
}}
>
🏠
</div>
{isMenuOpen && (
<div style={{color: 'white'}}>
<div style={{fontWeight: 'bold', fontSize: '1.1rem'}}>私のアプリ</div>
<div style={{fontSize: '0.8rem', opacity: 0.8}}>おかえりなさい!</div>
</div>
)}
</div>
{/* ナビゲーションリンク */}
<div style={{display: 'flex', flexDirection: 'column', gap: '6px'}}>
{navigationLinks.map((item, index) => (
<a
key={index}
href={item.href}
style={{
display: 'flex',
alignItems: 'center',
gap: '15px',
padding: '14px 16px',
borderRadius: '10px',
textDecoration: 'none',
color: 'white',
transition: 'all 0.3s ease',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
border: '1px solid rgba(255, 255, 255, 0.1)',
backdropFilter: 'blur(10px)',
}}
onMouseEnter={e => {
e.currentTarget.style.backgroundColor = item.color;
e.currentTarget.style.transform = 'translateX(8px)';
//@ts-expect-error - color is not a valid property
e.currentTarget.style.boxShadow = `0 4px 15px ${item.color}40`;
}}
onMouseLeave={e => {
e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
e.currentTarget.style.transform = 'translateX(0)';
e.currentTarget.style.boxShadow = 'none';
}}
>
<div
style={{
fontSize: '1.4rem',
minWidth: '24px',
textAlign: 'center',
}}
>
{item.icon}
</div>
{isMenuOpen && (
<div
style={{
fontSize: '0.95rem',
fontWeight: '500',
whiteSpace: 'nowrap',
}}
>
{item.text}
</div>
)}
</a>
))}
</div>
</nav>
),
},
},
},
blockOrder: defaultBlockOrder,
};
}}
</SideNavigation>
);
}
🔧 プロパティリファレンス
メインコンポーネントプロパティ
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
isFloating | boolean | undefined | ナビゲーションがコンテンツの上にフロート(モバイルスタイル)するか、固定位置にするか |
isMenuOpen | boolean | undefined | ナビゲーションメニューが開いているか閉じているかを制御 |
setIsMenuOpen | (isMenuOpen: boolean) => void | 必須 | メニューの開閉状態を制御する関数 |
header | { icon?: IconType; text?: ReactNode; href: string } | 必須 | オプションのアイコン、テキストコンテンツ、hrefを持つヘッダーリンクオブジェクト |
links | { icon?: IconType; text?: ReactNode; href: string }[] | 必須 | オプションのアイコンを持つナビゲーションリンクオブジェクトの配列 |
children | BlocksOverride | undefined | デフォルトブロックをオーバーライドするか、カスタムナビゲーションコンポーネントを追加する関数 |
サブコンポーネント
SideNavigationコンポーネントは複数のサブコンポーネントを提供します。すべてのサブコンポーネントは、メインコンポーネントのコンテキストからデフォルト値を受け取り、プロパティを通じてこれらの値をオーバーライドできます。
SideNavigation.Overlay
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
children | ReactNode | undefined | オーバーレイのカスタムコンテンツ |
setIsMenuOpen | (isMenuOpen: boolean) => void | コンテキストから | オーバーレイがクリックされたときにメニュー状態を制御する関数 |
注意: このコンポーネントはすべてのHTML div要素プロパティを継承します。
SideNavigation.MenuButton
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
children | ReactNode | <Icon icon="menu" /> | メニューボタンのカスタムコンテンツ(デフォルトはハンバーガーアイコン) |
isMenuOpen | boolean | コンテキストから | 現在のメニュー開閉状態 |
setIsMenuOpen | (isMenuOpen: boolean) => void | コンテキストから | メニュー状態を切り替える関数 |
注意: このコンポーネントはすべてのHTML button要素プロパティを継承します。
SideNavigation.Links
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
children | ReactNode | デフォルトリンクレンダリング | デフォルトリンクレンダリングをオーバーライドするカスタムコンテンツ |
header | { icon?: IconType; text?: ReactNode; href: string } | コンテキストから | 表示するヘッダーリンクオブジェクト |
links | { icon?: IconType; text?: ReactNode; href: string }[] | コンテキストから | 表示するナビゲーションリンクの配列 |
注意: このコンポーネントはすべてのHTML nav要素プロパティを継承します。
🔧 TypeScriptサポート
包括的な型定義による完全なTypeScriptサポート:
import {SideNavigation} from '@nodeblocks/frontend-side-navigation-block';
import {ComponentProps, ReactNode} from 'react';
// メインコンポーネントインターフェース
interface SideNavigationProps extends Omit<ComponentProps<'aside'>, 'children'> {
isFloating?: boolean;
isMenuOpen?: boolean;
setIsMenuOpen: (isMenuOpen: boolean) => void;
header: { icon?: string; text?: ReactNode; href: string };
links: { icon?: string; text?: ReactNode; href: string }[];
}
// 包括的な型付けでの使用例
interface CustomNavigationData {
headerItem: { icon?: string; text?: ReactNode; href: string };
menuItems: { icon?: string; text?: ReactNode; href: string }[];
isResponsive: boolean;
onMenuToggle: (isOpen: boolean) => void;
}
const NavigationComponent = ({headerItem, menuItems, isResponsive, onMenuToggle}: CustomNavigationData) => {
const [isOpen, setIsOpen] = useState(false);
const handleMenuToggle = (isMenuOpen: boolean) => {
setIsOpen(isMenuOpen);
onMenuToggle(isMenuOpen);
};
return (
<SideNavigation
isFloating={isResponsive}
isMenuOpen={isOpen}
setIsMenuOpen={handleMenuToggle}
header={headerItem}
links={menuItems}
onClick={e => console.log('ナビゲーションがクリックされました')}>
<SideNavigation.Links />
</SideNavigation>
);
};
React、TypeScript、モダンWebスタンダードを使用して❤️で構築されました。