dramaling-vocab-learning/frontend/components/review/shared/TestContainer.tsx

234 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { ReactNode } from 'react'
import { BaseTestComponent, BaseTestComponentProps } from './BaseTestComponent'
/**
* 測驗容器元件 - 提供統一的測驗布局結構
* 整合導航控制和進度顯示
*/
export interface TestContainerProps extends Omit<BaseTestComponentProps, 'children'> {
// 測驗內容區域
contentArea: ReactNode
// 答題區域
answerArea?: ReactNode
// 結果顯示區域
resultArea?: ReactNode
// 導航控制預留給未來的NavigationController
navigationArea?: ReactNode
// 布局配置
layout?: 'standard' | 'split' | 'fullscreen'
// 自定義樣式
contentClassName?: string
answerClassName?: string
resultClassName?: string
}
export const TestContainer: React.FC<TestContainerProps> = ({
contentArea,
answerArea,
resultArea,
navigationArea,
layout = 'standard',
contentClassName = '',
answerClassName = '',
resultClassName = '',
...baseProps
}) => {
const getLayoutClasses = () => {
switch (layout) {
case 'split':
return 'lg:grid lg:grid-cols-2 lg:gap-8'
case 'fullscreen':
return 'min-h-screen flex flex-col'
default:
return 'space-y-6'
}
}
const renderContent = () => (
<div className={getLayoutClasses()}>
{/* 內容展示區域 */}
<div className={`test-content-area ${contentClassName}`}>
{contentArea}
</div>
{/* 答題互動區域 */}
{answerArea && (
<div className={`test-answer-area ${answerClassName}`}>
{answerArea}
</div>
)}
{/* 結果展示區域 */}
{resultArea && (
<div className={`test-result-area mt-6 ${resultClassName}`}>
{resultArea}
</div>
)}
{/* 導航控制區域 */}
{navigationArea && (
<div className="test-navigation-area mt-8">
{navigationArea}
</div>
)}
</div>
)
return (
<BaseTestComponent {...baseProps}>
{renderContent()}
</BaseTestComponent>
)
}
/**
* 專用於不同測驗類型的容器變體
*/
// 選擇題容器
export interface ChoiceTestContainerProps extends Omit<TestContainerProps, 'layout'> {
questionArea: ReactNode
optionsArea: ReactNode
}
export const ChoiceTestContainer: React.FC<ChoiceTestContainerProps> = ({
questionArea,
optionsArea,
resultArea,
navigationArea,
...props
}) => {
return (
<TestContainer
{...props}
layout="standard"
contentArea={
<div className="space-y-6">
<div className="question-area">
{questionArea}
</div>
<div className="options-area">
{optionsArea}
</div>
</div>
}
resultArea={resultArea}
navigationArea={navigationArea}
/>
)
}
// 填空題容器
export interface FillTestContainerProps extends Omit<TestContainerProps, 'layout'> {
sentenceArea: ReactNode
inputArea: ReactNode
}
export const FillTestContainer: React.FC<FillTestContainerProps> = ({
sentenceArea,
inputArea,
resultArea,
navigationArea,
...props
}) => {
return (
<TestContainer
{...props}
layout="standard"
contentArea={sentenceArea}
answerArea={inputArea}
resultArea={resultArea}
navigationArea={navigationArea}
/>
)
}
// 聽力測驗容器
export interface ListeningTestContainerProps extends Omit<TestContainerProps, 'layout'> {
audioArea: ReactNode
questionArea: ReactNode
answerArea: ReactNode
}
export const ListeningTestContainer: React.FC<ListeningTestContainerProps> = ({
audioArea,
questionArea,
answerArea,
resultArea,
navigationArea,
...props
}) => {
return (
<TestContainer
{...props}
layout="standard"
contentArea={
<div className="space-y-6">
<div className="audio-area text-center">
{audioArea}
</div>
<div className="question-area">
{questionArea}
</div>
</div>
}
answerArea={answerArea}
resultArea={resultArea}
navigationArea={navigationArea}
/>
)
}
// 口說測驗容器
export interface SpeakingTestContainerProps extends Omit<TestContainerProps, 'layout'> {
promptArea: ReactNode
recordingArea: ReactNode
}
export const SpeakingTestContainer: React.FC<SpeakingTestContainerProps> = ({
promptArea,
recordingArea,
resultArea,
navigationArea,
...props
}) => {
return (
<TestContainer
{...props}
layout="standard"
contentArea={promptArea}
answerArea={recordingArea}
resultArea={resultArea}
navigationArea={navigationArea}
/>
)
}
// 翻卡測驗容器
export interface FlipTestContainerProps extends Omit<TestContainerProps, 'layout'> {
cardArea: ReactNode
confidenceArea: ReactNode
}
export const FlipTestContainer: React.FC<FlipTestContainerProps> = ({
cardArea,
confidenceArea,
navigationArea,
...props
}) => {
return (
<TestContainer
{...props}
layout="standard"
contentArea={cardArea}
answerArea={confidenceArea}
navigationArea={navigationArea}
/>
)
}