102 lines
4.5 KiB
TypeScript
Executable File
102 lines
4.5 KiB
TypeScript
Executable File
import React, { useCallback } from 'react';
|
|
import clsx from 'clsx';
|
|
import { toast } from 'react-toastify';
|
|
import ReadingContext from './SectionContext/reading';
|
|
import SectionExercises from './SectionExercises';
|
|
import useExamEditorStore from '@/stores/examEditor';
|
|
import { ModuleState } from '@/stores/examEditor/types';
|
|
import ListeningContext from './SectionContext/listening';
|
|
import SectionDropdown from '../Shared/SectionDropdown';
|
|
import LevelContext from './SectionContext/level';
|
|
import { Module } from '@/interfaces';
|
|
|
|
|
|
const SectionRenderer: React.FC = () => {
|
|
const { currentModule, dispatch } = useExamEditorStore();
|
|
const {
|
|
focusedSection,
|
|
expandedSections,
|
|
sections,
|
|
sectionLabels,
|
|
edit,
|
|
} = useExamEditorStore(state => state.modules[currentModule]);
|
|
|
|
const updateModule = useCallback((updates: Partial<ModuleState>) => {
|
|
dispatch({ type: 'UPDATE_MODULE', payload: { updates } });
|
|
}, [dispatch]);
|
|
|
|
const toggleSection = (sectionId: number) => {
|
|
if (edit.includes(sectionId)) {
|
|
toast.info(`Save or discard your changes first!`);
|
|
} else {
|
|
if (!expandedSections.includes(sectionId)) {
|
|
updateModule({ focusedSection: sectionId });
|
|
}
|
|
updateModule({
|
|
expandedSections:
|
|
expandedSections.includes(sectionId) ?
|
|
expandedSections.filter(index => index !== sectionId) :
|
|
[...expandedSections, sectionId]
|
|
})
|
|
}
|
|
};
|
|
|
|
const ContextMap: Record<string, React.ComponentType<{ sectionId: number; module: Module }>> = {
|
|
reading: ReadingContext,
|
|
listening: ListeningContext,
|
|
level: LevelContext,
|
|
};
|
|
|
|
const SectionContext = ContextMap[currentModule];
|
|
|
|
return (
|
|
<>
|
|
<div className='flex flex-row'>
|
|
<div className={clsx(
|
|
"p-4 rounded-xl w-full",
|
|
currentModule && `bg-ielts-${currentModule}/20`
|
|
)}>
|
|
|
|
{sections.map((state, sectionIndex) => {
|
|
const id = state.sectionId;
|
|
const label = sectionLabels.find((sl) => sl.id == id)?.label;
|
|
|
|
return (
|
|
<div key={id}
|
|
className={
|
|
clsx("rounded-xl shadow",
|
|
sectionIndex !== sections.length - 1 && "mb-4"
|
|
)}>
|
|
<SectionDropdown
|
|
toggleOpen={() => toggleSection(id)}
|
|
open={expandedSections.includes(id)}
|
|
title={label}
|
|
className={clsx(
|
|
"w-full py-4 px-8 text-lg font-semibold leading-6 text-white",
|
|
"shadow-lg transform transition-all duration-300 hover:scale-102 hover:rounded-lg",
|
|
expandedSections.includes(id) ? "rounded-t-lg" : "rounded-lg",
|
|
focusedSection !== id ?
|
|
`bg-gradient-to-r from-ielts-${currentModule}/30 to-ielts-${currentModule}/60 hover:from-ielts-${currentModule}/60 hover:to-ielts-${currentModule}` :
|
|
`bg-ielts-${currentModule}`
|
|
)}
|
|
>
|
|
{expandedSections.includes(id) && (
|
|
<div
|
|
className="p-6 bg-white rounded-b-xl shadow-inner border-b"
|
|
onFocus={() => updateModule({ focusedSection: id })}
|
|
tabIndex={id + 1}
|
|
>
|
|
{currentModule in ContextMap && <SectionContext sectionId={id} module={currentModule} />}
|
|
<SectionExercises sectionId={id} />
|
|
</div>
|
|
)}
|
|
</SectionDropdown>
|
|
</div>);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default SectionRenderer; |