@tinyweb_dev/oe-exam-sdk 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/components/exams/take/components/QuestionRenderer.js +10 -2
  2. package/dist/components/exams/take/components/question-renderers/FindWordsInMatrixRenderer.d.ts +4 -0
  3. package/dist/components/exams/take/components/question-renderers/FindWordsInMatrixRenderer.js +389 -0
  4. package/dist/components/exams/take/components/question-renderers/MoversCrosswordPuzzleRenderer.d.ts +1 -1
  5. package/dist/components/exams/take/components/question-renderers/MoversCrosswordPuzzleRenderer.js +33 -7
  6. package/dist/components/exams/take/components/question-renderers/index.d.ts +1 -0
  7. package/dist/components/exams/take/components/question-renderers/index.js +1 -0
  8. package/dist/components/exams/take/types.d.ts +50 -0
  9. package/dist/components/exams/take/utils/question-transformers.d.ts +41 -1
  10. package/dist/components/exams/take/utils/question-transformers.js +24 -0
  11. package/dist/components/questions/_shared/config/question-types.config.js +6 -0
  12. package/dist/components/questions/_shared/types/find-words-in-matrix.type.d.ts +18 -0
  13. package/dist/components/questions/_shared/types/find-words-in-matrix.type.js +1 -0
  14. package/dist/components/questions/creator/question-type-registry.js +2 -0
  15. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +2 -1
  16. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +16 -0
  17. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +10 -6
  18. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.d.ts +9 -0
  19. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +69 -0
  20. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.d.ts +8 -0
  21. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +302 -0
  22. package/dist/components/questions/types/find-words-in-matrix/index.d.ts +4 -0
  23. package/dist/components/questions/types/find-words-in-matrix/index.js +4 -0
  24. package/dist/components/questions/types/find-words-in-matrix/register.d.ts +2 -0
  25. package/dist/components/questions/types/find-words-in-matrix/register.js +29 -0
  26. package/dist/components/questions/types/find-words-in-matrix/transform.d.ts +2 -0
  27. package/dist/components/questions/types/find-words-in-matrix/transform.js +89 -0
  28. package/dist/shared/constants/question-skills.js +2 -0
  29. package/dist/shared/lib/utils/question-reverse-transform.js +63 -0
  30. package/dist/shared/lib/utils/question-transform.js +2 -0
  31. package/dist/shared/types/common.types.d.ts +1 -1
  32. package/dist/shared/types/questions/find-words-in-matrix.d.ts +71 -0
  33. package/dist/shared/types/questions/find-words-in-matrix.js +8 -0
  34. package/dist/shared/types/questions/index.d.ts +1 -0
  35. package/dist/shared/types/questions/index.js +2 -0
  36. package/package.json +1 -1
@@ -5,7 +5,7 @@
5
5
  * Each transformer handles specific question type data structure.
6
6
  */
7
7
  import type { FillBlankQuestion, ChooseBestAnswerQuestion, PictureChooseSubQuestion, PictureFillBlankSubQuestion, ReadingPassageQuestion, LabelThePictureLabel, FillWordHintSubQuestion, ColoringHintItem, ColoringRegion } from '../types';
8
- export type QuestionType = 'FILL_IN_BLANK' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'CHOOSE_CORRECT_ADJECTIVE' | 'WRITE_A_SHORT_LETTER' | 'READ_AND_COLOR_OBJECTS' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'LABEL_THE_PICTURE' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'FILL_MISSING_WORDS_IN_GRID' | 'IMAGE_OBJECT_MATCHING' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'TRUE_FALSE' | 'TRUE_FALSE_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'WRITE_SENTENCES' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'WORD_ORDER_AND_MATCH_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPONTANEOUS_QA_GROUP' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD';
8
+ export type QuestionType = 'FILL_IN_BLANK' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'CHOOSE_CORRECT_ADJECTIVE' | 'WRITE_A_SHORT_LETTER' | 'READ_AND_COLOR_OBJECTS' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'LABEL_THE_PICTURE' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'FILL_MISSING_WORDS_IN_GRID' | 'IMAGE_OBJECT_MATCHING' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'TRUE_FALSE' | 'TRUE_FALSE_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'WRITE_SENTENCES' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'FIND_WORDS_IN_MATRIX' | 'WORD_ORDER_AND_MATCH_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPONTANEOUS_QA_GROUP' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD';
9
9
  export interface ApiQuestion {
10
10
  id: string;
11
11
  content: any;
@@ -437,3 +437,43 @@ export interface CrosswordPuzzlePartData {
437
437
  explanation?: string;
438
438
  }
439
439
  export declare function transformCrosswordPuzzle(questions: ApiQuestion[]): CrosswordPuzzlePartData;
440
+ export interface FindWordsInMatrixPartData {
441
+ type?: 'WORD_BANK' | 'CATEGORIZE';
442
+ title?: string;
443
+ instruction: string;
444
+ gridSize: {
445
+ rows: number;
446
+ cols: number;
447
+ };
448
+ grid: string[][];
449
+ categories?: Array<{
450
+ id: string;
451
+ name: string;
452
+ color?: string;
453
+ }>;
454
+ wordBank: Array<{
455
+ id: string;
456
+ word: string;
457
+ isExample?: boolean;
458
+ categoryId?: string;
459
+ }>;
460
+ exampleSelection?: {
461
+ wordId: string;
462
+ categoryId?: string;
463
+ path: Array<{
464
+ row: number;
465
+ col: number;
466
+ }>;
467
+ };
468
+ correctWords: Array<{
469
+ wordId: string;
470
+ word?: string;
471
+ categoryId?: string;
472
+ path: Array<{
473
+ row: number;
474
+ col: number;
475
+ }>;
476
+ }>;
477
+ explanation?: string;
478
+ }
479
+ export declare function transformFindWordsInMatrix(questions: ApiQuestion[]): FindWordsInMatrixPartData;
@@ -1204,3 +1204,27 @@ export function transformCrosswordPuzzle(questions) {
1204
1204
  explanation: question?.explanation,
1205
1205
  };
1206
1206
  }
1207
+ export function transformFindWordsInMatrix(questions) {
1208
+ const question = questions[0];
1209
+ const content = (question?.content || {});
1210
+ const correctAnswer = (question?.correct_answer || question?.correctAnswer || {});
1211
+ const type = content?.type || (Array.isArray(content?.categories) && content.categories.length > 0 ? 'CATEGORIZE' : 'WORD_BANK');
1212
+ const rows = Number(content?.gridSize?.rows) || (Array.isArray(content?.grid) ? content.grid.length : 14);
1213
+ const cols = Number(content?.gridSize?.cols) || (Array.isArray(content?.grid?.[0]) ? content.grid[0].length : 14);
1214
+ const grid = Array.isArray(content?.grid) ? content.grid : [];
1215
+ const categories = Array.isArray(content?.categories) ? content.categories : undefined;
1216
+ const wordBank = Array.isArray(content?.wordBank) ? content.wordBank : [];
1217
+ const correctWords = Array.isArray(correctAnswer?.words) ? correctAnswer.words : [];
1218
+ return {
1219
+ type,
1220
+ title: content?.title || '',
1221
+ instruction: question?.instruction || content?.instruction || 'Find these words in the wordsearch box.',
1222
+ gridSize: { rows, cols },
1223
+ grid,
1224
+ ...(categories ? { categories } : {}),
1225
+ wordBank,
1226
+ ...(content?.exampleSelection ? { exampleSelection: content.exampleSelection } : {}),
1227
+ correctWords,
1228
+ explanation: question?.explanation,
1229
+ };
1230
+ }
@@ -322,6 +322,11 @@ export const QUESTION_TYPE_CONFIG = {
322
322
  requiresContent: true,
323
323
  hasDedicatedComponent: true,
324
324
  },
325
+ FIND_WORDS_IN_MATRIX: {
326
+ label: 'Tìm từ trong ma trận (Word Search)',
327
+ requiresContent: true,
328
+ hasDedicatedComponent: true,
329
+ },
325
330
  };
326
331
  export const QUESTION_TYPES_WITH_DEDICATED_COMPONENTS = [
327
332
  'FILL_IN_BLANK',
@@ -368,6 +373,7 @@ export const QUESTION_TYPES_WITH_DEDICATED_COMPONENTS = [
368
373
  'SPEAKING_CUE_CARD',
369
374
  'WORD_FILL_STRUCTURED_FORM',
370
375
  'CROSSWORD_PUZZLE',
376
+ 'FIND_WORDS_IN_MATRIX',
371
377
  ];
372
378
  export function getQuestionTypeLabel(type) {
373
379
  return QUESTION_TYPE_CONFIG[type]?.label || type;
@@ -0,0 +1,18 @@
1
+ import type { FindWordsInMatrixType, MatrixCellCoord, CategoryHeadingItem, WordBankItem, ExampleSelection, CorrectWordItem, FindWordsInMatrixContent, FindWordsInMatrixCorrectAnswer, StudentWordSelectionItem } from '../../../../shared/types/questions/find-words-in-matrix';
2
+ export interface WordBankFormItem extends WordBankItem {
3
+ path?: MatrixCellCoord[];
4
+ }
5
+ export interface FindWordsInMatrixFormState {
6
+ type: FindWordsInMatrixType;
7
+ title?: string;
8
+ instruction?: string;
9
+ rows: number;
10
+ cols: number;
11
+ grid: string[][];
12
+ categories: CategoryHeadingItem[];
13
+ words: WordBankFormItem[];
14
+ caseSensitive?: boolean;
15
+ points: number;
16
+ explanation?: string;
17
+ }
18
+ export type { FindWordsInMatrixType, MatrixCellCoord, CategoryHeadingItem, WordBankItem, ExampleSelection, CorrectWordItem, FindWordsInMatrixContent, FindWordsInMatrixCorrectAnswer, StudentWordSelectionItem, };
@@ -44,6 +44,7 @@ import { speakingCueCardRegistration } from '../types/speaking-cue-card/register
44
44
  import { wordFillStructuredFormRegistration } from '../types/word-fill-structured-form/register';
45
45
  import { wordOrderAndMatchGroupRegistration } from '../types/word-order-and-match-group/register';
46
46
  import { crosswordPuzzleRegistration } from '../types/crossword-puzzle/register';
47
+ import { findWordsInMatrixRegistration } from '../types/find-words-in-matrix/register';
47
48
  // ─── Registry map ───────────────────────────────────────────────────────────
48
49
  export const questionTypeRegistry = {
49
50
  CHOOSE_THE_CORRECT_ANSWER: chooseTheCorrectAnswerRegistration,
@@ -91,4 +92,5 @@ export const questionTypeRegistry = {
91
92
  SPEAKING_CUE_CARD: speakingCueCardRegistration,
92
93
  WORD_FILL_STRUCTURED_FORM: wordFillStructuredFormRegistration,
93
94
  CROSSWORD_PUZZLE: crosswordPuzzleRegistration,
95
+ FIND_WORDS_IN_MATRIX: findWordsInMatrixRegistration,
94
96
  };
@@ -15,7 +15,8 @@ export declare enum QuestionBankEditorType {
15
15
  WRITE_CORRECT_VERB_FORM = "WRITE_CORRECT_VERB_FORM",
16
16
  FILL_IN_BLANK = "FILL_IN_BLANK",
17
17
  CHOOSE_CORRECT_ADJECTIVE = "CHOOSE_CORRECT_ADJECTIVE",
18
- CROSSWORD_PUZZLE = "CROSSWORD_PUZZLE"
18
+ CROSSWORD_PUZZLE = "CROSSWORD_PUZZLE",
19
+ FIND_WORDS_IN_MATRIX = "FIND_WORDS_IN_MATRIX"
19
20
  }
20
21
  /**
21
22
  * Renders the appropriate Creator for a question-bank item.
@@ -4,6 +4,7 @@ import { useEffect, useRef, useState } from 'react';
4
4
  import { t } from '../../../shared/lib/i18n';
5
5
  import { renderChooseCorrectAdjectiveEditor, renderChooseTheCorrectAnswerEditor, renderFillInBlankEditor, renderFillMissingWordsInGridEditor, renderImageObjectMatchingEditor, renderLabelThePictureEditor, renderLookPictureChooseCorrectAnswerEditor, renderLookPictureFillBlankChooseAnswerEditor, renderLookPictureFillWordHintEditor, renderReadAndColorObjectsEditor, renderReadPassageAndAnswerQuestionsEditor, renderWriteAShortLetterEditor, renderWriteCorrectVerbFormEditor, UnsupportedEditorFallback, } from './editor-adapters';
6
6
  import { CrosswordPuzzleCreator } from '../types/crossword-puzzle/CrosswordPuzzleCreator';
7
+ import { FindWordsInMatrixCreator } from '../types/find-words-in-matrix/FindWordsInMatrixCreator';
7
8
  /** Marketing question types supported by the question-bank editor. */
8
9
  export var QuestionBankEditorType;
9
10
  (function (QuestionBankEditorType) {
@@ -21,6 +22,7 @@ export var QuestionBankEditorType;
21
22
  QuestionBankEditorType["FILL_IN_BLANK"] = "FILL_IN_BLANK";
22
23
  QuestionBankEditorType["CHOOSE_CORRECT_ADJECTIVE"] = "CHOOSE_CORRECT_ADJECTIVE";
23
24
  QuestionBankEditorType["CROSSWORD_PUZZLE"] = "CROSSWORD_PUZZLE";
25
+ QuestionBankEditorType["FIND_WORDS_IN_MATRIX"] = "FIND_WORDS_IN_MATRIX";
24
26
  })(QuestionBankEditorType || (QuestionBankEditorType = {}));
25
27
  /**
26
28
  * Renders the appropriate Creator for a question-bank item.
@@ -85,6 +87,20 @@ export function QuestionEditorRenderer({ questionType, content, correctAnswer, e
85
87
  points: data.points ?? points,
86
88
  });
87
89
  } }));
90
+ case QuestionBankEditorType.FIND_WORDS_IN_MATRIX:
91
+ return (_jsx(FindWordsInMatrixCreator, { initialData: {
92
+ content,
93
+ answer: correctAnswer,
94
+ explanation,
95
+ points,
96
+ }, onChange: (data) => {
97
+ onChange?.({
98
+ content: data.content,
99
+ answer: data.answer,
100
+ explanation: data.explanation || (explanation ?? undefined),
101
+ points: data.points ?? points,
102
+ });
103
+ } }));
88
104
  default:
89
105
  return (_jsx(UnsupportedEditorFallback, { questionType: questionType, content: content, correctAnswer: correctAnswer, explanation: explanation }));
90
106
  }
@@ -4,8 +4,8 @@ import { useMemo } from 'react';
4
4
  export function CrosswordPuzzleClient({ content, correctAnswer, isReviewMode = false, userAnswers = {}, }) {
5
5
  const rows = content?.gridSize?.rows || 12;
6
6
  const cols = content?.gridSize?.cols || 12;
7
- const acrossClues = content?.clues?.across || [];
8
- const downClues = content?.clues?.down || [];
7
+ const acrossClues = useMemo(() => content?.clues?.across || [], [content?.clues?.across]);
8
+ const downClues = useMemo(() => content?.clues?.down || [], [content?.clues?.down]);
9
9
  // Build grid cell mapping
10
10
  const { cellMap, clueNumberMap } = useMemo(() => {
11
11
  const cMap = new Map();
@@ -69,12 +69,16 @@ export function CrosswordPuzzleClient({ content, correctAnswer, isReviewMode = f
69
69
  else if (userAnswers?.[key]) {
70
70
  previewLetter = userAnswers[key];
71
71
  }
72
- let cellBg = isMarked ? 'bg-slate-200' : 'bg-white';
73
- let textColor = isExample ? 'text-orange-600' : 'text-gray-800';
74
- if (isReviewMode && previewLetter) {
72
+ const cellBg = isMarked ? 'bg-gray-400 border-gray-400 shadow-sm ring-1 ring-gray-400/50' : 'bg-gray-100 border-gray-300';
73
+ let textColor = isExample
74
+ ? 'text-orange-600 font-bold italic'
75
+ : isMarked
76
+ ? 'text-gray-950 font-bold'
77
+ : 'text-gray-800 font-bold';
78
+ if (isReviewMode && previewLetter && !isExample) {
75
79
  textColor = 'text-green-700 font-extrabold';
76
80
  }
77
- return (_jsxs("div", { className: `relative flex items-center justify-center rounded border-2 border-gray-400 font-bold shadow-sm h-8 w-8 sm:h-9 sm:w-9 md:h-10 md:w-10 text-sm sm:text-base md:text-lg ${cellBg} ${textColor}`, children: [clueNum && (_jsx("span", { className: "absolute top-0.5 left-1 text-[9px] font-bold text-gray-500 leading-none", children: clueNum })), _jsx("span", { children: previewLetter })] }, key));
81
+ return (_jsxs("div", { className: `relative flex items-center justify-center rounded border-2 font-bold shadow-sm h-8 w-8 sm:h-9 sm:w-9 md:h-10 md:w-10 text-sm sm:text-base md:text-lg ${cellBg} ${textColor}`, children: [clueNum && (_jsx("span", { className: "absolute top-0.5 left-1 text-[9px] font-bold text-gray-500 leading-none", children: clueNum })), _jsx("span", { children: previewLetter })] }, key));
78
82
  });
79
83
  }) }) })] })] }));
80
84
  }
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { FindWordsInMatrixContent, FindWordsInMatrixCorrectAnswer } from '../../../../shared/types/questions/find-words-in-matrix';
3
+ interface FindWordsInMatrixClientProps {
4
+ content: FindWordsInMatrixContent;
5
+ correctAnswer?: FindWordsInMatrixCorrectAnswer;
6
+ showSolution?: boolean;
7
+ }
8
+ export declare function FindWordsInMatrixClient({ content, correctAnswer, showSolution, }: FindWordsInMatrixClientProps): React.JSX.Element;
9
+ export {};
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Card } from '../../../../components/ui/card';
4
+ import { Badge } from '../../../../components/ui/badge';
5
+ // 10 Distinct pastel highlighter colors for word selections
6
+ const WORD_COLORS = [
7
+ { bg: 'bg-amber-100 text-amber-900 border-amber-400', badge: 'bg-amber-500 text-white', stroke: '#f59e0b' },
8
+ { bg: 'bg-emerald-100 text-emerald-900 border-emerald-400', badge: 'bg-emerald-500 text-white', stroke: '#10b981' },
9
+ { bg: 'bg-sky-100 text-sky-900 border-sky-400', badge: 'bg-sky-500 text-white', stroke: '#0ea5e9' },
10
+ { bg: 'bg-purple-100 text-purple-900 border-purple-400', badge: 'bg-purple-500 text-white', stroke: '#a855f7' },
11
+ { bg: 'bg-rose-100 text-rose-900 border-rose-400', badge: 'bg-rose-500 text-white', stroke: '#f43f5e' },
12
+ { bg: 'bg-indigo-100 text-indigo-900 border-indigo-400', badge: 'bg-indigo-500 text-white', stroke: '#6366f1' },
13
+ { bg: 'bg-teal-100 text-teal-900 border-teal-400', badge: 'bg-teal-500 text-white', stroke: '#14b8a6' },
14
+ { bg: 'bg-yellow-100 text-yellow-900 border-yellow-400', badge: 'bg-yellow-500 text-white', stroke: '#eab308' },
15
+ { bg: 'bg-pink-100 text-pink-900 border-pink-400', badge: 'bg-pink-500 text-white', stroke: '#ec4899' },
16
+ { bg: 'bg-cyan-100 text-cyan-900 border-cyan-400', badge: 'bg-cyan-500 text-white', stroke: '#06b6d4' },
17
+ ];
18
+ export function FindWordsInMatrixClient({ content, correctAnswer, showSolution = false, }) {
19
+ const rows = content.gridSize?.rows || content.grid?.length || 14;
20
+ const cols = content.gridSize?.cols || content.grid?.[0]?.length || 14;
21
+ const grid = content.grid || [];
22
+ const wordBank = content.wordBank || [];
23
+ const isCategorize = content.type === 'CATEGORIZE' && Array.isArray(content.categories) && content.categories.length > 0;
24
+ const categories = content.categories || [];
25
+ // Build cell highlight map
26
+ const cellColorMap = {};
27
+ // Example selection
28
+ if (content.exampleSelection?.path) {
29
+ for (const c of content.exampleSelection.path) {
30
+ cellColorMap[`${c.row}-${c.col}`] = { colorIdx: 0, word: 'Example' };
31
+ }
32
+ }
33
+ // Solution paths if visible
34
+ if (showSolution && correctAnswer?.words) {
35
+ correctAnswer.words.forEach((item, idx) => {
36
+ const colorIdx = (idx + 1) % WORD_COLORS.length;
37
+ item.path?.forEach((c) => {
38
+ cellColorMap[`${c.row}-${c.col}`] = { colorIdx, word: item.word || item.wordId };
39
+ });
40
+ });
41
+ }
42
+ return (_jsxs("div", { className: "space-y-6", children: [content.title && (_jsx("h3", { className: "text-lg font-bold text-gray-800 dark:text-gray-100", children: content.title })), isCategorize ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4", children: categories.map((cat) => {
43
+ const wordsInCat = wordBank.filter((w) => w.categoryId === cat.id);
44
+ return (_jsxs(Card, { className: "p-3.5 bg-gray-50/70 dark:bg-gray-800/40 border", children: [_jsxs("div", { className: "font-bold text-sm text-gray-800 dark:text-gray-200 border-b pb-1.5 mb-2.5 flex items-center justify-between", children: [_jsx("span", { children: cat.name }), _jsx(Badge, { variant: "outline", className: "text-xs", children: wordsInCat.length })] }), _jsx("div", { className: "flex flex-wrap gap-1.5", children: wordsInCat.map((w, idx) => {
45
+ const isExample = Boolean(w.isExample);
46
+ return (_jsxs(Badge, { variant: "outline", className: `px-2.5 py-1 text-xs font-medium ${isExample
47
+ ? 'line-through bg-amber-50 text-amber-800 border-amber-300'
48
+ : 'bg-white dark:bg-gray-800'}`, children: [w.word, isExample && _jsx("span", { className: "ml-1 text-amber-600 font-bold", children: "(Ex)" })] }, w.id || idx));
49
+ }) })] }, cat.id));
50
+ }) })) : (_jsxs(Card, { className: "p-4 bg-gray-50 dark:bg-gray-800/50 border-dashed border-2", children: [_jsx("div", { className: "text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 mb-3 flex items-center gap-2", children: _jsxs("span", { children: ["Word Bank (", wordBank.length, " words)"] }) }), _jsx("div", { className: "flex flex-wrap gap-2", children: wordBank.map((w, idx) => {
51
+ const isExample = Boolean(w.isExample);
52
+ return (_jsxs(Badge, { variant: "outline", className: `px-3 py-1.5 text-sm font-medium transition-all ${isExample
53
+ ? 'line-through bg-amber-50 text-amber-800 border-amber-300'
54
+ : 'bg-white dark:bg-gray-800 hover:shadow-sm'}`, children: [w.word, isExample && (_jsx("span", { className: "ml-1.5 text-xs text-amber-600 font-bold no-underline", children: "(Example)" }))] }, w.id || idx));
55
+ }) })] })), _jsx("div", { className: "overflow-x-auto pb-2 flex justify-center", children: _jsx("div", { className: "inline-grid gap-1 p-3 bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-800", style: {
56
+ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
57
+ }, children: Array.from({ length: rows }).map((_, rIdx) => {
58
+ const r = rIdx + 1;
59
+ return Array.from({ length: cols }).map((_, cIdx) => {
60
+ const c = cIdx + 1;
61
+ const char = grid[rIdx]?.[cIdx] || '';
62
+ const highlight = cellColorMap[`${r}-${c}`];
63
+ const colorConfig = highlight ? WORD_COLORS[highlight.colorIdx] : null;
64
+ return (_jsx("div", { className: `h-9 w-9 sm:h-11 sm:w-11 flex items-center justify-center font-bold text-sm sm:text-base select-none rounded-md transition-colors border ${colorConfig
65
+ ? `${colorConfig.bg} font-extrabold shadow-sm scale-95`
66
+ : 'border-gray-100 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-800/30 text-gray-700 dark:text-gray-300'}`, title: highlight ? `${highlight.word} (${r},${c})` : `(${r},${c})`, children: char || '·' }, `${r}-${c}`));
67
+ });
68
+ }) }) })] }));
69
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ interface FindWordsInMatrixCreatorProps {
3
+ initialData?: any;
4
+ onChange?: (data: any) => void;
5
+ onUnsavedChangesChange?: (hasChanges: boolean) => void;
6
+ }
7
+ export declare function FindWordsInMatrixCreator({ initialData, onChange, onUnsavedChangesChange, }: FindWordsInMatrixCreatorProps): React.JSX.Element;
8
+ export {};