フィルター検索パネルブロック
FilterSearchPanelコンポーネントは、ReactとTypeScriptで構築された、完全にカスタマイズ可能でアクセシブルなフィルタリング機能付き検索インターフェースです。モダンなデザインパターン、フォームバリデーション、柔軟なカスタマイズオプションを備えた、完全な検索・フィルター体験を提供します。
🚀 インストール
npm install @nodeblocks/frontend-filter-search-panel-block@0.2.3
📖 使用方法
import {FilterSearchPanel} from '@nodeblocks/frontend-filter-search-panel-block';
- 基本的な使用方法
- 高度な使用方法
function BasicFilterSearchPanel() {
const [filters, setFilters] = useState([
{ label: 'アクティブ', key: 'status-active', groupName: 'ステータス' },
{ label: 'ウェブ開発', key: 'category-web', groupName: 'カテゴリー' }
]);
const handleRemoveFilter = (filter: FilterChip) => {
setFilters(filters.filter(f => f.key !== filter.key));
};
const handleSearch = ({search}: {search: string}) => {
console.log('検索が実行されました:', search);
setFilters(prev => [...prev, {label: search, key: search, groupName: 'カスタム'}]);
};
return (
<FilterSearchPanel
filters={filters}
onClickRemoveFilter={handleRemoveFilter}
onClickFilterButton={() => console.log('フィルターボタンがクリックされました')}
onSearch={handleSearch}
searchPlaceholder="サービスを検索..."
noFilterText="フィルターが適用されていません"
filterLabel="フィルター設定">
<FilterSearchPanel.SearchInput />
<FilterSearchPanel.ActionGroup />
</FilterSearchPanel>
);
}
function AdvancedFilterSearchPanel() {
const [filters, setFilters] = useState([
{label: 'フロントエンド', key: 'tech-frontend', groupName: 'テクノロジー'},
{label: 'React', key: 'framework-react', groupName: 'フレームワーク'},
{label: 'シニア', key: 'level-senior', groupName: 'レベル'},
]);
const [searchHistory, setSearchHistory] = useState(['react 開発者', 'nodejs バックエンド', 'フルスタック エンジニア']);
const handleRemoveFilter = (filter: {key: string}) => {
setFilters(filters.filter(f => f.key !== filter.key));
};
const handleSearch = ({search}: {search: string}) => {
console.log('検索が実行されました:', search);
if (search.trim()) {
setSearchHistory(prev => [search, ...prev.slice(0, 4)]);
setFilters(prev => [...prev, {label: search, key: search, groupName: '検索'}]);
}
};
return (
<FilterSearchPanel
filters={filters}
onClickRemoveFilter={handleRemoveFilter}
onClickFilterButton={() => console.log('高度なフィルターパネルが開かれました')}
onSearch={handleSearch}
searchPlaceholder="開発者を検索..."
noFilterText="🔍 フィルターが適用されていません - すべての結果を表示中"
filterLabel="🎯 高度なフィルター"
style={{
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: '12px',
padding: '24px',
boxShadow: '0 8px 32px rgba(0,0,0,0.1)',
}}
>
{({defaultBlocks, defaultBlockOrder}) => {
const searchInput = (
<div style={{position: 'relative'}}>
{defaultBlocks.searchInput}
{searchHistory.length > 0 && (
<div
style={{
top: '100%',
left: 0,
right: 0,
background: 'white',
borderRadius: '8px',
marginTop: '8px',
padding: '12px',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
zIndex: 10,
}}
>
<div style={{fontSize: '12px', color: '#666', marginBottom: '8px'}}>📝 最近の検索:</div>
{searchHistory.map((term, index) => (
<div
key={index}
style={{
padding: '4px 8px',
fontSize: '14px',
color: '#333',
cursor: 'pointer',
borderRadius: '4px',
}}
>
{term}
</div>
))}
</div>
)}
</div>
);
const actionGroup = (
<div
style={{
background: 'rgba(255,255,255,0.9)',
borderRadius: '12px',
padding: '16px',
marginTop: '16px',
}}
>
{defaultBlocks.actionGroup}
</div>
);
const searchStats = (
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '12px 16px',
background: 'rgba(255,255,255,0.1)',
borderRadius: '8px',
marginTop: '12px',
color: 'white',
}}
>
<span style={{fontSize: '14px'}}>📊 {filters.length} 個のアクティブフィルター</span>
<span style={{fontSize: '14px'}}>🎯 {Math.floor(Math.random() * 150) + 50} 件の結果が見つかりました</span>
</div>
);
const quickActions = (
<div
style={{
display: 'flex',
gap: '8px',
marginTop: '16px',
flexWrap: 'wrap',
}}
>
<button
style={{
padding: '8px 16px',
background: 'rgba(255,255,255,0.2)',
border: 'none',
borderRadius: '20px',
color: 'white',
fontSize: '14px',
cursor: 'pointer',
transition: 'all 0.3s ease',
}}
>
🔄 すべてリセット
</button>
<button
style={{
padding: '8px 16px',
background: 'rgba(255,255,255,0.2)',
border: 'none',
borderRadius: '20px',
color: 'white',
fontSize: '14px',
cursor: 'pointer',
transition: 'all 0.3s ease',
}}
>
💾 検索を保存
</button>
<button
style={{
padding: '8px 16px',
background: 'rgba(255,255,255,0.2)',
border: 'none',
borderRadius: '20px',
color: 'white',
fontSize: '14px',
cursor: 'pointer',
transition: 'all 0.3s ease',
}}
>
🔔 アラートを作成
</button>
</div>
);
return {
blocks: {searchInput, actionGroup, searchStats, quickActions},
blockOrder: ['searchStats', 'quickActions', 'searchInput', 'actionGroup'],
};
}}
</FilterSearchPanel>
);
}
🔧 プロパティリファレンス
メインコンポーネントのプロパティ
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
filters | FilterChip[] | [] | 表示する現在選択されているフィルターチップの配列 |
defaultSearchValue | string | undefined | 検索入力フィールドのデフォルト値 |
onClickRemoveFilter | (filter: FilterChip) => void | undefined | フィルターチップが削除されたときにトリガーされるコールバック関数 |
onClickFilterButton | () => void | undefined | フィルターボタンがクリックされたときにトリガーされるコールバック関数 |
onSearchChange | (event: HTMLInputElement) => void | undefined | 検索入力値が変更されたときにトリガーされるコールバック関数 |
onSearch | (data: T) => void | undefined | 検索データでフォームが送信されたときにトリガーされるコールバック関数 |
searchPlaceholder | string | undefined | 検索入力フィールドのプレースホルダーテキスト |
noFilterText | string | undefined | フィルターが選択されていないときに表示されるテキスト |
filterLabel | string | undefined | フィルターボタンのラベルテキスト |
className | string | undefined | フォームコンテナのスタイリング用の追加CSSクラス名 |
children | BlocksOverride | undefined | デフォルトレンダリングをオーバーライドするカスタムブロックコンポーネント |
注意: このコンポーネントはHTML form 要素のすべてのプロパティを継承します。
サブコンポーネント
FilterSearchPanelコンポーネントは複数のサブコンポーネントを提供します。すべてのサブコンポーネントは、メインコンポーネントのコンテキストからデフォルト値を受け取り、プロパティを通じてこれらの値をオーバーライドできます。
FilterSearchPanel.SearchInput
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
searchPlaceholder | string | コンテキストから | 検索入力に表示されるプレースホルダーテキスト |
onSearchChange | (event: HTMLInputElement) => void | undefined | 入力値が変更されたときにトリガーされるコールバック関数 |
onSearch | () => void | undefined | 検索が実行されたときにトリガーされるコールバック関数 |
defaultSearchValue | string | undefined | 検索入力のデフォルト値 |
className | string | undefined | スタイリング用の追加CSSクラス名 |
注意: このコンポーネントはHTML div 要素のすべてのプロパティを継承します。
FilterSearchPanel.FilterButton
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
filterLabel | string | コンテキストから | フィルターボタンに表示されるラベルテキスト |
onClickFilterButton | () => void | コンテキストから | ボタンがクリックされたときにトリガーされるコールバック関数 |
className | string | undefined | スタイリング用の追加CSSクラス名 |
children | ReactNode | コンテキストから | デフォルトレンダリングをオーバーライドするカスタムコンテンツ |
注意: このコンポーネントはHTML button 要素のすべてのプロパティを継承します。
FilterSearchPanel.SelectedFilterList
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
filters | FilterChip[] | コンテキストから | 表示するフィルターチップの配列 |
onClickRemoveFilter | (filter: FilterChip) => void | コンテキストから | フィルターが削除されたときにトリガーされるコールバック関数 |
noFilterText | string | コンテキストから | フィルターが選択されていないときに表示されるテキスト |
className | string | undefined | スタイリング用の追加CSSクラス名 |
注意: このコンポーネントはHTML div 要素のすべてのプロパティを継承します。
FilterSearchPanel.FilterBadge
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
filter | FilterChip | 必須 | ラベル、キー、グループ名を含むフィルターチップオブジェクト |
onClickRemoveFilter | (filter: FilterChip) => void | 必須 | フィルターが削除されたときにトリガーされるコールバック関数 |
className | string | undefined | スタイリング用の追加CSSクラス名 |
注意: このコンポーネントはHTML div 要素のすべてのプロパティを継承します。
FilterSearchPanel.ActionGroup
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
className | string | undefined | スタイリング用の追加CSSクラス名 |
注意: このコンポーネントはHTML div 要素のすべてのプロパティを継承します。
🔧 TypeScript サポート
包括的な型定義による完全なTypeScriptサポート:
import FilterSearchPanel from '@nodeblocks/frontend-filter-search-panel-block';
// フィルターチップの構造
interface FilterChip {
label: string;
key: string;
groupName: string;
}
// デフォルトの検索フォームデータ構造
interface SearchFormData {
search: string;
}
// カスタムフィールドで拡張
interface CustomSearchFormData extends SearchFormData {
category?: string;
status?: string;
customField?: string;
}
const MyFilterSearchPanel = () => {
const [selectedFilters, setSelectedFilters] = useState<FilterChip[]>([
{ label: 'アクティブ', key: 'status-active', groupName: 'ステータス' },
{ label: 'プレミアム', key: 'type-premium', groupName: 'タイプ' }
]);
const handleSearch = (formData: CustomSearchFormData) => {
console.log('検索が実行されました:', formData);
// 検索送信の処理
};
const handleRemoveFilter = (filter: FilterChip) => {
setSelectedFilters(prev => prev.filter(f => f.key !== filter.key));
};
const handleFilterButtonClick = () => {
console.log('フィルターモーダルを開いています');
// フィルターボタンクリックの処理
};
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
console.log('検索値が変更されました:', event.target.value);
// 検索入力変更の処理
};
return (
<FilterSearchPanel<CustomSearchFormData>
filters={selectedFilters}
onSearch={handleSearch}
onClickRemoveFilter={handleRemoveFilter}
onClickFilterButton={handleFilterButtonClick}
onSearchChange={handleSearchChange}
searchPlaceholder="サービスを検索..."
noFilterText="フィルターが適用されていません"
filterLabel="フィルターオプション">
<FilterSearchPanel.SearchInput />
<FilterSearchPanel.ActionGroup />
</FilterSearchPanel>
);
};
React、TypeScript、モダンなウェブ標準を使用して❤️で構築されました。