-
-
Notifications
You must be signed in to change notification settings - Fork 143
設定画面のUIを変更 #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
設定画面のUIを変更 #255
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Walkthroughこのプルリクエストでは、複数のローカライズファイルと設定コンポーネントに対して変更が加えられています。具体的には、英語、日本語、韓国語、中国語の翻訳ファイルに新しいキーの追加や既存のキーの更新が行われました。また、設定コンポーネントの構造が再編成され、特に Changes
Possibly related PRs
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (10)
src/components/settings/index.tsx (2)
6-11: コンポーネント名 'Based' の命名について'Based' というコンポーネント名は意味が曖昧かもしれません。期待される機能や役割に基づいて、'Base' や 'Basic' など、より適切な名前に変更することを検討してください。
76-91:renderTabContent関数のリファクタリング提案
switch文の代わりに、タブキーと対応するコンポーネントを持つオブジェクトを使用すると、コードが簡潔になり、可読性が向上します。例:
- const renderTabContent = () => { - switch (activeTab) { - case 'general': - return <Based /> - case 'ai': - return <AI /> - // 他のケース - } - } + const tabContents: { [key in TabKey]: JSX.Element } = { + general: <Based />, + ai: <AI />, + // 他のタブ + } + const renderTabContent = () => tabContents[activeTab]src/components/settings/advancedSettings.tsx (2)
24-38: マージン設定の最適化をお勧めします共通のマージン設定を持つコンポーネントが複数あります。スタイリングの一貫性と保守性を向上させるため、共通のクラスをコンポーネントとして抽出することを検討してください。
+const SettingsSection = ({ children }: { children: React.ReactNode }) => ( + <div className="mb-24"> + {children} + </div> +); const AdvancedSettings = () => { // ... return ( <div className="mb-40"> - <div className="mb-24 grid-cols-2"> + <SettingsSection> {/* ... */} - </div> + </SettingsSection>
39-124: 状態更新ロジックの簡素化各設定の状態更新に同様のパターンが繰り返されています。カスタムフックを作成することで、コードの重複を減らし、保守性を向上させることができます。
+const useToggleSetting = (key: keyof typeof settingsStore.getState()) => { + const value = settingsStore((s) => s[key]); + const toggle = () => settingsStore.setState((s) => ({ [key]: !s[key] })); + return [value, toggle] as const; +}; const AdvancedSettings = () => { - const showAssistantText = settingsStore((s) => s.showAssistantText); + const [showAssistantText, toggleAssistantText] = useToggleSetting('showAssistantText'); // ... 他の設定も同様に更新src/components/settings/based.tsx (1)
168-182: 背景画像変更機能の改善背景画像の変更ロジックが直接コンポーネント内に実装されています。また、エラーハンドリングが不足しています。
+const handleBackgroundImageChange = async () => { + try { + const { bgFileInput } = menuStore.getState(); + if (!bgFileInput) { + console.error('Background file input not found'); + return; + } + bgFileInput.click(); + } catch (error) { + console.error('Failed to change background image:', error); + } +}; -<TextButton - onClick={() => { - const { bgFileInput } = menuStore.getState() - bgFileInput?.click() - }} -> +<TextButton onClick={handleBackgroundImageChange}>locales/zh/translation.json (1)
139-141: 翻訳キーの一貫性について「設定重置」という翻訳が複数のキーで重複して使用されています。ユーザーの混乱を避けるため、以下の改善を提案します:
LocalStorageResetButtonの翻訳を「設定を重置する」のように動詞形に変更LocalStorageResetをより説明的な表現に変更{ - "LocalStorageReset": "設定重置", + "LocalStorageReset": "設定の初期化", "LocalStorageResetInfo": "環境変數設定時,優先使用其值。頁面將重新載入。", - "LocalStorageResetButton": "設定重置", + "LocalStorageResetButton": "設定を初期化する",src/components/settings/voice.tsx (4)
60-60: 不要なclassNameプロパティを削除することを推奨します空のclassNameプロパティは不要です。
- <div className=""> + <div>
Line range hint
734-828: Azure TTSの実装に重要な問題があります
- OpenAI TTSの音声オプションをAzure TTSに流用していますが、これは正しくありません。Azure TTSは独自の音声セットを持っています。
- OpenAIとAzureのコード間で重複が多く見られます。
以下の改善を提案します:
- Azure固有の音声オプションを実装する
- 共通のコンポーネントを抽出して再利用する
- <select - value={openaiTTSVoice} - onChange={(e) => - settingsStore.setState({ - openaiTTSVoice: e.target.value as OpenAITTSVoice, - }) - } - className="px-16 py-8 bg-surface1 hover:bg-surface1-hover rounded-8" - > - <option value="alloy">alloy</option> - <option value="echo">echo</option> - <option value="fable">fable</option> - <option value="onyx">onyx</option> - <option value="nova">nova</option> - <option value="shimmer">shimmer</option> - </select> + <select + value={azureTTSVoice} + onChange={(e) => + settingsStore.setState({ + azureTTSVoice: e.target.value as AzureTTSVoice, + }) + } + className="px-16 py-8 bg-surface1 hover:bg-surface1-hover rounded-8" + > + {azureVoiceOptions.map(voice => ( + <option key={voice.value} value={voice.value}> + {voice.label} + </option> + ))} + </select>
Line range hint
84-734: コンポーネントの分割とリファクタリングを推奨します現在のコードは以下の問題があります:
- 条件分岐が複雑で長すぎます
- 各TTSプロバイダーで類似のUIパターンが繰り返されています
- メンテナンス性が低下する可能性があります
以下のようなコンポーネントの分割を提案します:
// TTSProviderSection.tsx interface TTSProviderSectionProps { title: string; description: string; children: React.ReactNode; } // APIKeyInput.tsx interface APIKeyInputProps { value: string; onChange: (value: string) => void; label: string; } // VoiceSelector.tsx interface VoiceSelectorProps { options: Array<{value: string, label: string}>; value: string; onChange: (value: string) => void; } // SpeedControl.tsx interface SpeedControlProps { value: number; onChange: (value: number) => void; min: number; max: number; }これにより:
- コードの重複が減少
- テストが容易になる
- 機能の追加や変更が簡単になる
Line range hint
489-534: エラーハンドリングとローディング状態の実装が必要です特にAivis Speechの更新処理において:
- エラーハンドリングが不足しています
- ローディング状態の表示がありません
- ユーザーへのフィードバックが不十分です
以下のような改善を提案します:
onClick={async () => { + try { + settingsStore.setState({ isUpdating: true }); const response = await fetch('/api/update-aivis-speakers') if (response.ok) { const updatedSpeakersResponse = await fetch( '/speakers_aivis.json' ) const updatedSpeakers = await updatedSpeakersResponse.json() speakers_aivis.splice( 0, speakers_aivis.length, ...updatedSpeakers ) + toast.success(t('UpdateSuccess')); + } else { + throw new Error('Failed to update speakers'); + } + } catch (error) { + toast.error(t('UpdateError')); + console.error(error); + } finally { + settingsStore.setState({ isUpdating: false }); + } }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (19)
locales/en/translation.json(1 hunks)locales/ja/translation.json(2 hunks)locales/ko/translation.json(2 hunks)locales/zh/translation.json(2 hunks)src/components/settings/advancedSettings.tsx(1 hunks)src/components/settings/ai.tsx(1 hunks)src/components/settings/based.tsx(3 hunks)src/components/settings/environment.tsx(0 hunks)src/components/settings/externalLinkage.tsx(1 hunks)src/components/settings/index.tsx(2 hunks)src/components/settings/language.tsx(0 hunks)src/components/settings/log.tsx(1 hunks)src/components/settings/modelProvider.tsx(2 hunks)src/components/settings/other.tsx(1 hunks)src/components/settings/slide.tsx(2 hunks)src/components/settings/slideConvert.tsx(2 hunks)src/components/settings/voice.tsx(5 hunks)src/components/settings/youtube.tsx(3 hunks)tailwind.config.js(1 hunks)
💤 Files with no reviewable changes (2)
- src/components/settings/environment.tsx
- src/components/settings/language.tsx
✅ Files skipped from review due to trivial changes (6)
- src/components/settings/log.tsx
- src/components/settings/slideConvert.tsx
- src/components/settings/externalLinkage.tsx
- src/components/settings/slide.tsx
- src/components/settings/modelProvider.tsx
- src/components/settings/youtube.tsx
🔇 Additional comments (8)
src/components/settings/ai.tsx (1)
4-11: コードの実装が適切です
AI コンポーネントは適切に実装されています。
src/components/settings/other.tsx (1)
5-13: コードの実装が適切です
Other コンポーネントは適切に実装されています。
tailwind.config.js (1)
7-8: Tailwind CSS の content 設定の確認
content 配列が './src/**/*.{js,ts,jsx,tsx}' に変更されています。もし 'pages' や 'components' ディレクトリ内で Tailwind CSS のクラスを使用している場合、それらがスタイルの生成対象から外れる可能性があります。必要に応じて、これらのディレクトリを content 配列に追加することを検討してください。
locales/zh/translation.json (1)
163-163: ベータ表記の一貫性について
「測試版」という表記は適切ですが、他の箇所での表記との一貫性を確保する必要があります。
✅ Verification successful
Let's check the actual content of these files to see the beta notations used.
ベータ表記の一貫性が確認できました
各言語ファイルでベータ表記が以下のように統一されていることを確認しました:
- 英語:「(Beta)」
- 日本語:「(ベータ版)」
- 中国語:「(測試版)」
中国語での「測試版」という表記は、他の機能でも一貫して使用されており、適切です。
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 他の箇所でのベータ表記を確認
rg -l "Beta|測試版|ベータ" locales/
Length of output: 115
Script:
#!/bin/bash
# Check the actual beta notations in each language file
echo "=== English (en) ==="
rg "Beta" locales/en/translation.json
echo -e "\n=== Japanese (ja) ==="
rg "ベータ" locales/ja/translation.json
echo -e "\n=== Chinese (zh) ==="
rg "測試版" locales/zh/translation.json
Length of output: 614
locales/ja/translation.json (2)
140-142: 設定リセット機能の文言が適切に追加されています。
環境変数の優先順位と再読み込みの動作について明確に説明されており、ユーザーにとって分かりやすい表現になっています。
164-164: 「Beta」から「ベータ版」への変更は適切です。
日本語として自然な表現に修正されており、一貫性のある用語使用になっています。
locales/ko/translation.json (1)
139-141: 韓国語の翻訳が日本語版と一貫性を保っています。
設定リセット機能の説明文と「ベータ版」の表記が、意味を正確に保ちながら韓国語として自然な形で翻訳されています。
Also applies to: 163-163
locales/en/translation.json (1)
139-141: 英語の原文が明確で翻訳のベースとして適切です。
設定リセットに関する説明が簡潔かつ正確で、他言語への翻訳のソースとして優れています。環境変数の優先順位についても技術的に正確な説明になっています。
src/components/settings/index.tsx
Outdated
| <div className="text-text1 max-w-5xl mx-auto px-24 py-64"> | ||
| <div className="md:flex mt-16"> | ||
| {/* タブナビゲーション */} | ||
| <ul className="flex-column space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tailwind CSS クラス名の誤り
'flex-column' は Tailwind CSS には存在しないクラス名です。代わりに 'flex flex-col' を使用してください。
修正例:
- <ul className="flex-column space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0">
+ <ul className="flex flex-col space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <ul className="flex-column space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0"> | |
| <ul className="flex flex-col space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0"> |
| onChange={(e) => { | ||
| const newLanguage = e.target.value as Language | ||
|
|
||
| const ss = settingsStore.getState() | ||
| const jaVoiceSelected = | ||
| ss.selectVoice === 'voicevox' || | ||
| ss.selectVoice === 'koeiromap' || | ||
| ss.selectVoice === 'aivis_speech' | ||
|
|
||
| switch (newLanguage) { | ||
| case 'ja': | ||
| settingsStore.setState({ selectLanguage: 'ja' }) | ||
|
|
||
| i18n.changeLanguage('ja') | ||
| break | ||
| case 'en': | ||
| settingsStore.setState({ selectLanguage: 'en' }) | ||
|
|
||
| if (jaVoiceSelected) { | ||
| settingsStore.setState({ selectVoice: 'google' }) | ||
| } | ||
|
|
||
| i18n.changeLanguage('en') | ||
| break | ||
| case 'zh': | ||
| settingsStore.setState({ selectLanguage: 'zh' }) | ||
|
|
||
| if (jaVoiceSelected) { | ||
| settingsStore.setState({ selectVoice: 'google' }) | ||
| } | ||
|
|
||
| i18n.changeLanguage('zh-TW') | ||
| break | ||
| case 'ko': | ||
| settingsStore.setState({ selectLanguage: 'ko' }) | ||
|
|
||
| if (jaVoiceSelected) { | ||
| settingsStore.setState({ selectVoice: 'google' }) | ||
| } | ||
|
|
||
| i18n.changeLanguage('ko') | ||
| break | ||
| default: | ||
| break | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
言語切り替えロジックの改善
言語切り替えロジックが複雑で、同様のパターンが繰り返されています。また、音声選択のロジックも混在しています。以下の改善を提案します:
- 言語切り替えロジックを別の関数に抽出
- 音声選択のロジックを分離
- switch文をオブジェクトマッピングに変更
+const LANGUAGE_CONFIG = {
+ ja: { i18nCode: 'ja', keepJaVoice: true },
+ en: { i18nCode: 'en', keepJaVoice: false },
+ zh: { i18nCode: 'zh-TW', keepJaVoice: false },
+ ko: { i18nCode: 'ko', keepJaVoice: false },
+} as const;
+const handleLanguageChange = (newLanguage: Language) => {
+ const config = LANGUAGE_CONFIG[newLanguage];
+ const ss = settingsStore.getState();
+
+ settingsStore.setState({ selectLanguage: newLanguage });
+
+ if (!config.keepJaVoice) {
+ const jaVoiceSelected = ['voicevox', 'koeiromap', 'aivis_speech'].includes(ss.selectVoice);
+ if (jaVoiceSelected) {
+ settingsStore.setState({ selectVoice: 'google' });
+ }
+ }
+
+ i18n.changeLanguage(config.i18nCode);
+};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onChange={(e) => { | |
| const newLanguage = e.target.value as Language | |
| const ss = settingsStore.getState() | |
| const jaVoiceSelected = | |
| ss.selectVoice === 'voicevox' || | |
| ss.selectVoice === 'koeiromap' || | |
| ss.selectVoice === 'aivis_speech' | |
| switch (newLanguage) { | |
| case 'ja': | |
| settingsStore.setState({ selectLanguage: 'ja' }) | |
| i18n.changeLanguage('ja') | |
| break | |
| case 'en': | |
| settingsStore.setState({ selectLanguage: 'en' }) | |
| if (jaVoiceSelected) { | |
| settingsStore.setState({ selectVoice: 'google' }) | |
| } | |
| i18n.changeLanguage('en') | |
| break | |
| case 'zh': | |
| settingsStore.setState({ selectLanguage: 'zh' }) | |
| if (jaVoiceSelected) { | |
| settingsStore.setState({ selectVoice: 'google' }) | |
| } | |
| i18n.changeLanguage('zh-TW') | |
| break | |
| case 'ko': | |
| settingsStore.setState({ selectLanguage: 'ko' }) | |
| if (jaVoiceSelected) { | |
| settingsStore.setState({ selectVoice: 'google' }) | |
| } | |
| i18n.changeLanguage('ko') | |
| break | |
| default: | |
| break | |
| } | |
| onChange={(e) => { | |
| const newLanguage = e.target.value as Language | |
| handleLanguageChange(newLanguage); | |
| }} | |
| const LANGUAGE_CONFIG = { | |
| ja: { i18nCode: 'ja', keepJaVoice: true }, | |
| en: { i18nCode: 'en', keepJaVoice: false }, | |
| zh: { i18nCode: 'zh-TW', keepJaVoice: false }, | |
| ko: { i18nCode: 'ko', keepJaVoice: false }, | |
| } as const; | |
| const handleLanguageChange = (newLanguage: Language) => { | |
| const config = LANGUAGE_CONFIG[newLanguage]; | |
| const ss = settingsStore.getState(); | |
| settingsStore.setState({ selectLanguage: newLanguage }); | |
| if (!config.keepJaVoice) { | |
| const jaVoiceSelected = ['voicevox', 'koeiromap', 'aivis_speech'].includes(ss.selectVoice); | |
| if (jaVoiceSelected) { | |
| settingsStore.setState({ selectVoice: 'google' }); | |
| } | |
| } | |
| i18n.changeLanguage(config.i18nCode); | |
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (1)
src/components/settings/index.tsx (1)
42-91: 型安全性の向上を推奨しますタブの実装は適切ですが、以下の改善を提案させていただきます:
- タブの設定をより型安全にするため、定数オブジェクトとして定義することを推奨します
- renderTabContentの戻り値の型を明示的に定義することで、将来の保守性が向上します
以下のような実装を提案します:
+ const TAB_KEYS = { + GENERAL: 'general', + AI: 'ai', + YOUTUBE: 'youtube', + VOICE: 'voice', + SLIDE: 'slide', + OTHER: 'other', + } as const; + - type TabKey = 'general' | 'ai' | 'youtube' | 'voice' | 'slide' | 'other' + type TabKey = typeof TAB_KEYS[keyof typeof TAB_KEYS]; + type TabContent = React.ReactElement; - const renderTabContent = () => { + const renderTabContent = (): TabContent => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
src/components/settings/index.tsx(2 hunks)
🔇 Additional comments (2)
src/components/settings/index.tsx (2)
1-11: インポートの変更が適切に実装されています
コンポーネントの再構成に伴うインポートの変更が適切に行われており、新しいタブインターフェースの実装をサポートする構造になっています。
98-98: Tailwind CSSクラス名の誤りについて
'flex-column'はTailwind CSSに存在しないクラス名です。代わりに'flex flex-col'を使用してください。
| <div className="text-text1 max-w-5xl mx-auto px-24 py-64"> | ||
| <div className="md:flex mt-16"> | ||
| {/* タブナビゲーション */} | ||
| <ul className="flex flex-col space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0"> | ||
| {tabs.map((tab) => ( | ||
| <li key={tab.key}> | ||
| <button | ||
| className={`flex py-8 px-16 rounded-8 w-full typography-16 text-left | ||
| ${ | ||
| activeTab === tab.key | ||
| ? 'text-white bg-primary' | ||
| : 'bg-gray-50 hover:text-gray-900 hover:bg-gray-100' | ||
| }`} | ||
| onClick={() => setActiveTab(tab.key)} | ||
| > | ||
| {tab.label} | ||
| </button> | ||
| </li> | ||
| ))} | ||
| </ul> | ||
|
|
||
| {/* タブコンテンツ */} | ||
| <div className="p-24 bg-surface7-hover text-medium rounded-8 w-full"> | ||
| {renderTabContent()} | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
アクセシビリティの改善を推奨します
現在のタブ実装にアクセシビリティ対応を追加することで、スクリーンリーダーのユーザーエクスペリエンスが向上します。
以下の改善を提案します:
- <ul className="flex flex-col space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0">
+ <ul className="flex flex-col space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0"
+ role="tablist"
+ aria-orientation="vertical">
{tabs.map((tab) => (
<li key={tab.key}>
<button
className={`flex py-8 px-16 rounded-8 w-full typography-16 text-left
${activeTab === tab.key
? 'text-white bg-primary'
: 'bg-gray-50 hover:text-gray-900 hover:bg-gray-100'
}`}
onClick={() => setActiveTab(tab.key)}
+ role="tab"
+ aria-selected={activeTab === tab.key}
+ aria-controls={`${tab.key}-panel`}
>
{tab.label}
</button>
</li>
))}
</ul>
- <div className="p-24 bg-surface7-hover text-medium rounded-8 w-full">
+ <div
+ className="p-24 bg-surface7-hover text-medium rounded-8 w-full"
+ role="tabpanel"
+ id={`${activeTab}-panel`}
+ aria-labelledby={`${activeTab}-tab`}>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="text-text1 max-w-5xl mx-auto px-24 py-64"> | |
| <div className="md:flex mt-16"> | |
| {/* タブナビゲーション */} | |
| <ul className="flex flex-col space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0"> | |
| {tabs.map((tab) => ( | |
| <li key={tab.key}> | |
| <button | |
| className={`flex py-8 px-16 rounded-8 w-full typography-16 text-left | |
| ${ | |
| activeTab === tab.key | |
| ? 'text-white bg-primary' | |
| : 'bg-gray-50 hover:text-gray-900 hover:bg-gray-100' | |
| }`} | |
| onClick={() => setActiveTab(tab.key)} | |
| > | |
| {tab.label} | |
| </button> | |
| </li> | |
| ))} | |
| </ul> | |
| {/* タブコンテンツ */} | |
| <div className="p-24 bg-surface7-hover text-medium rounded-8 w-full"> | |
| {renderTabContent()} | |
| </div> | |
| <div className="text-text1 max-w-5xl mx-auto px-24 py-64"> | |
| <div className="md:flex mt-16"> | |
| {/* タブナビゲーション */} | |
| <ul className="flex flex-col space-y-4 text-sm font-medium md:w-[25%] md:me-8 mb-16 md:mb-0" | |
| role="tablist" | |
| aria-orientation="vertical"> | |
| {tabs.map((tab) => ( | |
| <li key={tab.key}> | |
| <button | |
| className={`flex py-8 px-16 rounded-8 w-full typography-16 text-left | |
| ${ | |
| activeTab === tab.key | |
| ? 'text-white bg-primary' | |
| : 'bg-gray-50 hover:text-gray-900 hover:bg-gray-100' | |
| }`} | |
| onClick={() => setActiveTab(tab.key)} | |
| role="tab" | |
| aria-selected={activeTab === tab.key} | |
| aria-controls={`${tab.key}-panel`} | |
| > | |
| {tab.label} | |
| </button> | |
| </li> | |
| ))} | |
| </ul> | |
| {/* タブコンテンツ */} | |
| <div | |
| className="p-24 bg-surface7-hover text-medium rounded-8 w-full" | |
| role="tabpanel" | |
| id={`${activeTab}-panel`} | |
| aria-labelledby={`${activeTab}-tab`}> | |
| {renderTabContent()} | |
| </div> |
Summary by CodeRabbit
新機能
バグ修正
スタイル
削除