@tinyweb_dev/oe-exam-sdk 0.1.6 → 0.1.8

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 (100) hide show
  1. package/dist/api/exam-entry.d.ts +1 -1
  2. package/dist/api/exam-entry.types.d.ts +14 -0
  3. package/dist/api/exam-taking.types.d.ts +2 -0
  4. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -1
  5. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +1 -1
  6. package/dist/components/exams/take/components/speaking/renderers/SpeakingConversationRenderer.js +1 -1
  7. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +1 -1
  8. package/dist/components/exams/take/components/speaking/renderers/SpeakingInfoExchangeRenderer.js +1 -1
  9. package/dist/components/exams/take/components/speaking/renderers/SpeakingReadDisplayedContentRenderer.js +1 -1
  10. package/dist/components/exams/take/exam-taking.utils.d.ts +8 -2
  11. package/dist/components/exams/take/exam-taking.utils.js +1 -1
  12. package/dist/components/exams/take/utils/question-transformers.d.ts +4 -0
  13. package/dist/components/exams/take/utils/question-transformers.js +1 -1
  14. package/dist/components/features/exam-entry/themed/CambridgeYleWaitingRoom.js +1 -1
  15. package/dist/components/features/exam-entry/themed/cambridge-yle-waiting-parts.d.ts +18 -0
  16. package/dist/components/features/exam-entry/themed/cambridge-yle-waiting-parts.js +1 -0
  17. package/dist/components/features/exam-entry/themed/index.d.ts +3 -0
  18. package/dist/components/features/exam-entry/themed/index.js +1 -1
  19. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -1
  20. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.d.ts +2 -0
  21. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -1
  22. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -1
  23. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -1
  24. package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -1
  25. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -1
  26. package/dist/components/themes/english-certification/EcWritingTask.js +1 -1
  27. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -1
  28. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -1
  29. package/dist/components/themes/english-certification/EnglishCertificationInstructionBanner.d.ts +17 -0
  30. package/dist/components/themes/english-certification/EnglishCertificationInstructionBanner.js +1 -0
  31. package/dist/components/themes/english-certification/EnglishCertificationPartHeader.d.ts +24 -0
  32. package/dist/components/themes/english-certification/EnglishCertificationPartHeader.js +1 -0
  33. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.d.ts +3 -1
  34. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -1
  35. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -1
  36. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -1
  37. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.d.ts +0 -2
  38. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -1
  39. package/dist/components/themes/english-certification/english-cert-scroll.d.ts +32 -0
  40. package/dist/components/themes/english-certification/english-cert-scroll.js +1 -0
  41. package/dist/components/themes/english-certification/index.d.ts +4 -1
  42. package/dist/components/themes/english-certification/index.js +1 -1
  43. package/dist/components/themes/index.d.ts +1 -3
  44. package/dist/components/themes/index.js +1 -1
  45. package/dist/shared/lib/html-content.d.ts +14 -0
  46. package/dist/shared/lib/html-content.js +1 -0
  47. package/dist/shared/types/common.types.d.ts +0 -1
  48. package/dist/shared/types/common.types.js +1 -1
  49. package/dist/shared/types/exam-taking.types.d.ts +2 -0
  50. package/dist/shared/types/student.types.d.ts +14 -0
  51. package/package.json +2 -2
  52. package/dist/components/themes/spaceship/SpaceshipAudioPlayer.d.ts +0 -8
  53. package/dist/components/themes/spaceship/SpaceshipAudioPlayer.js +0 -1
  54. package/dist/components/themes/spaceship/SpaceshipLayout.d.ts +0 -2
  55. package/dist/components/themes/spaceship/SpaceshipLayout.js +0 -1
  56. package/dist/components/themes/spaceship/assets/images/X-sunset.png +0 -0
  57. package/dist/components/themes/spaceship/assets/images/X.webp +0 -0
  58. package/dist/components/themes/spaceship/assets/images/audio-bar.webp +0 -0
  59. package/dist/components/themes/spaceship/assets/images/background.webp +0 -0
  60. package/dist/components/themes/spaceship/assets/images/bg_sunset.png +0 -0
  61. package/dist/components/themes/spaceship/assets/images/big-blue-badge.png +0 -0
  62. package/dist/components/themes/spaceship/assets/images/big-blue-badge.webp +0 -0
  63. package/dist/components/themes/spaceship/assets/images/bingo.webp +0 -0
  64. package/dist/components/themes/spaceship/assets/images/blue-badge.png +0 -0
  65. package/dist/components/themes/spaceship/assets/images/blue-wrapper.webp +0 -0
  66. package/dist/components/themes/spaceship/assets/images/board_red.png +0 -0
  67. package/dist/components/themes/spaceship/assets/images/button_feedback_vi.png +0 -0
  68. package/dist/components/themes/spaceship/assets/images/circle-btn-right-sunset.png +0 -0
  69. package/dist/components/themes/spaceship/assets/images/circle-btn-right.png +0 -0
  70. package/dist/components/themes/spaceship/assets/images/circle-btn-sunset.png +0 -0
  71. package/dist/components/themes/spaceship/assets/images/circle-btn.webp +0 -0
  72. package/dist/components/themes/spaceship/assets/images/focus-vector.webp +0 -0
  73. package/dist/components/themes/spaceship/assets/images/head-phone_red.png +0 -0
  74. package/dist/components/themes/spaceship/assets/images/ic-mute-sunset.png +0 -0
  75. package/dist/components/themes/spaceship/assets/images/ic-mute.png +0 -0
  76. package/dist/components/themes/spaceship/assets/images/ic-play-sunset.png +0 -0
  77. package/dist/components/themes/spaceship/assets/images/ic-play-violet.webp +0 -0
  78. package/dist/components/themes/spaceship/assets/images/ic-play.webp +0 -0
  79. package/dist/components/themes/spaceship/assets/images/micro.png +0 -0
  80. package/dist/components/themes/spaceship/assets/images/next-sunset.png +0 -0
  81. package/dist/components/themes/spaceship/assets/images/next.webp +0 -0
  82. package/dist/components/themes/spaceship/assets/images/purple-circle.webp +0 -0
  83. package/dist/components/themes/spaceship/assets/images/question-badge-sunset.png +0 -0
  84. package/dist/components/themes/spaceship/assets/images/question-badge.webp +0 -0
  85. package/dist/components/themes/spaceship/assets/images/red-badge.webp +0 -0
  86. package/dist/components/themes/spaceship/assets/images/red-wrapper.webp +0 -0
  87. package/dist/components/themes/spaceship/assets/images/sound_red.png +0 -0
  88. package/dist/components/themes/spaceship/assets/images/student-avatar.png +0 -0
  89. package/dist/components/themes/spaceship/assets/images/teacher-play.webp +0 -0
  90. package/dist/components/themes/spaceship/assets/images/timer-badge.png +0 -0
  91. package/dist/components/themes/spaceship/assets/styles/SpaceshipLayout.module.css +0 -347
  92. package/dist/components/themes/spaceship/assets/styles/SpaceshipWaitingRoom.module.css +0 -395
  93. /package/dist/components/themes/{spaceship → summer-sky}/assets/images/big-blue-badge-sunset.png +0 -0
  94. /package/dist/components/themes/{spaceship → summer-sky}/assets/images/blue-badge-sunset.png +0 -0
  95. /package/dist/components/themes/{spaceship → summer-sky}/assets/images/blue-wrapper-sunset.png +0 -0
  96. /package/dist/components/themes/{spaceship → summer-sky}/assets/images/next-small.webp +0 -0
  97. /package/dist/components/themes/{spaceship → summer-sky}/assets/images/red-badge-sunset.png +0 -0
  98. /package/dist/components/themes/{spaceship → summer-sky}/assets/images/red-wrapper-sunset.png +0 -0
  99. /package/dist/components/themes/{spaceship → summer-sky}/assets/images/robot-avatar.png +0 -0
  100. /package/dist/components/themes/{spaceship → summer-sky}/assets/images/teacher-play-sunset.png +0 -0
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import{EnglishCertificationAudioPlayer as n}from"./EnglishCertificationAudioPlayer";import{EnglishCertificationQuestionItem as t}from"./EnglishCertificationQuestionItem";import{ENGLISH_CERT_CONTENT_WIDTH_CLASS as o}from"./english-certification-theme";export function englishCertQuestionDomId(e){return`ec-question-${e}`}export function EnglishCertificationSingleQuestionList({part:r,partConfig:s,answers:a,flaggedQuestions:u,onAnswerChange:l,onToggleFlag:g,isReviewMode:d=!1,isListeningPart:m=!1,partAudioUrl:f}){const c=s.startIndex??0,p=Array.isArray(s.questionConfig)?s.questionConfig:[],h=s.defaultQuestion,C=m&&Boolean(f);return i("div",{className:"flex flex-col items-center gap-5 pb-4",children:[C&&e("div",{className:o,children:e(n,{audioUrl:f,partNumber:r.partNo})}),r.questions.map((i,n)=>{const o=i.audio_url?Array.from(new Set([...p,"audioUrl"])):p;return e(t,{domId:englishCertQuestionDomId(i.id),question:i,questionNumber:c+n+1,questionConfig:o,defaultQuestion:h,isListeningPart:m,answer:a[i.id],isFlagged:u.has(i.id),onAnswerChange:l,onToggleFlag:g,isReviewMode:d},i.id)})]})}
1
+ "use client";import{jsx as i,jsxs as e}from"react/jsx-runtime";import{EnglishCertificationAudioPlayer as t}from"./EnglishCertificationAudioPlayer";import{EnglishCertificationQuestionItem as o}from"./EnglishCertificationQuestionItem";import{ENGLISH_CERT_CONTENT_WIDTH_CLASS as n}from"./english-certification-theme";import{ENGLISH_CERT_STICKY_AUDIO_ATTR as r,englishCertQuestionDomId as s}from"./english-cert-scroll";export function EnglishCertificationSingleQuestionList({part:a,partConfig:l,answers:u,flaggedQuestions:g,onAnswerChange:d,onToggleFlag:m,isReviewMode:f=!1,isListeningPart:c=!1,partAudioUrl:p}){const h=l.startIndex??0,C=Array.isArray(l.questionConfig)?l.questionConfig:[],w=l.defaultQuestion,x=c&&Boolean(p);return e("div",{className:"flex flex-col items-center gap-5 pb-4",children:[x&&i("div",{[r]:"",className:`sticky top-0 z-20 ${n}`,children:i(t,{audioUrl:p,partNumber:a.partNo})}),a.questions.map((e,t)=>{const n=e.audio_url?Array.from(new Set([...C,"audioUrl"])):C;return i(o,{domId:s(e.id),question:e,questionNumber:h+t+1,questionConfig:n,defaultQuestion:w,isListeningPart:c,answer:u[e.id],isFlagged:g.has(e.id),onAnswerChange:d,onToggleFlag:m,isReviewMode:f},e.id)})]})}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Scroll helpers for English Certification Answer Sheet navigation.
3
+ *
4
+ * Question cards use a stable DOM id (`ec-question-{id}`) so the sidebar can
5
+ * jump to the chosen question after `goToQuestion` re-renders the active part.
6
+ *
7
+ * Listening parts pin the audio bar with `position: sticky`. Manual scroll must
8
+ * subtract that bar's height so the target card is not hidden underneath it.
9
+ */
10
+ /** Stable DOM id for a question card / row, used for Answer-Sheet scroll-to. */
11
+ export declare function englishCertQuestionDomId(questionId: string): string;
12
+ /** Marker on sticky audio wrappers so scroll offset can be measured at runtime. */
13
+ export declare const ENGLISH_CERT_STICKY_AUDIO_ATTR = "data-ec-sticky-audio";
14
+ /** Nearest ancestor that actually scrolls (falls back to document scrolling element). */
15
+ export declare function findScrollableParent(el: HTMLElement): HTMLElement;
16
+ /**
17
+ * Scroll `el` into view inside its nearest scrollable ancestor.
18
+ * Prefer parent-relative scroll so nested columns (Reading split-view) work.
19
+ * When a sticky Listening audio bar is present, leave room under it.
20
+ */
21
+ export declare function scrollElementIntoView(el: HTMLElement, options?: {
22
+ behavior?: ScrollBehavior;
23
+ block?: ScrollLogicalPosition;
24
+ }): void;
25
+ /**
26
+ * Find the question anchor by id and scroll it into view.
27
+ * Retries briefly so cross-part navigation can wait for React to mount the card.
28
+ */
29
+ export declare function scrollToEnglishCertQuestion(questionId: string, options?: {
30
+ behavior?: ScrollBehavior;
31
+ block?: ScrollLogicalPosition;
32
+ }): void;
@@ -0,0 +1 @@
1
+ export function englishCertQuestionDomId(t){return`ec-question-${t}`}export const ENGLISH_CERT_STICKY_AUDIO_ATTR="data-ec-sticky-audio";const t=[0,50,120,250,400];function e(t){const e=window.getComputedStyle(t).overflowY;return("auto"===e||"scroll"===e||"overlay"===e)&&t.scrollHeight>t.clientHeight+1}export function findScrollableParent(t){let n=t.parentElement;for(;n;){if(e(n))return n;n=n.parentElement}return document.scrollingElement??document.documentElement}export function scrollElementIntoView(t,e){const n=e?.behavior??"smooth",o=e?.block??"start",i=findScrollableParent(t),l=function(t,e){const n=t.querySelectorAll("[data-ec-sticky-audio]");if(!n.length)return 0;let o=0;return n.forEach(n=>{if(n===e||n.contains(e))return;if(!t.contains(n))return;const i=n.getBoundingClientRect().height;i>o&&(o=i)}),o>0?o+12:0}(i,t),r=i.getBoundingClientRect(),c=t.getBoundingClientRect(),s=i.scrollTop,u=c.top-r.top+s;let a=u;if("center"===o)a=u-i.clientHeight/2+c.height/2;else if("end"===o)a=u-i.clientHeight+c.height;else if("nearest"===o){const t=s+l,e=s+i.clientHeight,n=u+c.height;if(u>=t&&n<=e)return;a=u<t?u-l:n-i.clientHeight}else a=u-l;"start"===o&&0===l&&(a=Math.max(0,a-12)),i.scrollTo({top:Math.max(0,a),behavior:n})}export function scrollToEnglishCertQuestion(e,n){if("undefined"==typeof document||!e)return;const o=englishCertQuestionDomId(e);let i=0;const l=()=>{const e=document.getElementById(o);e?scrollElementIntoView(e,n):(i+=1,i<t.length&&window.setTimeout(l,t[i]))};window.setTimeout(l,t[0])}
@@ -6,11 +6,14 @@ export { EnglishCertificationFooterBar } from './EnglishCertificationFooterBar';
6
6
  export { EnglishCertificationOptionSelector } from './EnglishCertificationOptionSelector';
7
7
  export { EnglishCertificationAudioPlayer } from './EnglishCertificationAudioPlayer';
8
8
  export { EnglishCertificationSingleQuestion } from './EnglishCertificationSingleQuestion';
9
- export { EnglishCertificationSingleQuestionList, englishCertQuestionDomId, } from './EnglishCertificationSingleQuestionList';
9
+ export { EnglishCertificationSingleQuestionList, } from './EnglishCertificationSingleQuestionList';
10
+ export { ENGLISH_CERT_STICKY_AUDIO_ATTR, englishCertQuestionDomId, scrollToEnglishCertQuestion, } from './english-cert-scroll';
10
11
  export { EnglishCertificationGroupedQuestion } from './EnglishCertificationGroupedQuestion';
11
12
  export { EnglishCertificationReadingSection } from './EnglishCertificationReadingSection';
12
13
  export { EnglishCertificationFlagButton } from './EnglishCertificationFlagButton';
13
14
  export { EnglishCertificationBrand } from './EnglishCertificationBrand';
15
+ export { EnglishCertificationPartHeader } from './EnglishCertificationPartHeader';
16
+ export { EnglishCertificationInstructionBanner } from './EnglishCertificationInstructionBanner';
14
17
  export { EcWordFillStructuredForm } from './EcWordFillStructuredForm';
15
18
  export { EcMatchByWritingAnswer } from './EcMatchByWritingAnswer';
16
19
  export { EcAnswerTheQuestion } from './EcAnswerTheQuestion';
@@ -1 +1 @@
1
- export{EnglishCertificationExamLayout}from"./EnglishCertificationExamLayout";export{EnglishCertificationQuestionRenderer}from"./EnglishCertificationQuestionRenderer";export{EnglishCertificationHeader}from"./EnglishCertificationHeader";export{EnglishCertificationNavigationPanel}from"./EnglishCertificationNavigationPanel";export{EnglishCertificationFooterBar}from"./EnglishCertificationFooterBar";export{EnglishCertificationOptionSelector}from"./EnglishCertificationOptionSelector";export{EnglishCertificationAudioPlayer}from"./EnglishCertificationAudioPlayer";export{EnglishCertificationSingleQuestion}from"./EnglishCertificationSingleQuestion";export{EnglishCertificationSingleQuestionList,englishCertQuestionDomId}from"./EnglishCertificationSingleQuestionList";export{EnglishCertificationGroupedQuestion}from"./EnglishCertificationGroupedQuestion";export{EnglishCertificationReadingSection}from"./EnglishCertificationReadingSection";export{EnglishCertificationFlagButton}from"./EnglishCertificationFlagButton";export{EnglishCertificationBrand}from"./EnglishCertificationBrand";export{EcWordFillStructuredForm}from"./EcWordFillStructuredForm";export{EcMatchByWritingAnswer}from"./EcMatchByWritingAnswer";export{EcAnswerTheQuestion}from"./EcAnswerTheQuestion";export{EcWordFillParagraph}from"./EcWordFillParagraph";export{EcWritingTask}from"./EcWritingTask";
1
+ export{EnglishCertificationExamLayout}from"./EnglishCertificationExamLayout";export{EnglishCertificationQuestionRenderer}from"./EnglishCertificationQuestionRenderer";export{EnglishCertificationHeader}from"./EnglishCertificationHeader";export{EnglishCertificationNavigationPanel}from"./EnglishCertificationNavigationPanel";export{EnglishCertificationFooterBar}from"./EnglishCertificationFooterBar";export{EnglishCertificationOptionSelector}from"./EnglishCertificationOptionSelector";export{EnglishCertificationAudioPlayer}from"./EnglishCertificationAudioPlayer";export{EnglishCertificationSingleQuestion}from"./EnglishCertificationSingleQuestion";export{EnglishCertificationSingleQuestionList}from"./EnglishCertificationSingleQuestionList";export{ENGLISH_CERT_STICKY_AUDIO_ATTR,englishCertQuestionDomId,scrollToEnglishCertQuestion}from"./english-cert-scroll";export{EnglishCertificationGroupedQuestion}from"./EnglishCertificationGroupedQuestion";export{EnglishCertificationReadingSection}from"./EnglishCertificationReadingSection";export{EnglishCertificationFlagButton}from"./EnglishCertificationFlagButton";export{EnglishCertificationBrand}from"./EnglishCertificationBrand";export{EnglishCertificationPartHeader}from"./EnglishCertificationPartHeader";export{EnglishCertificationInstructionBanner}from"./EnglishCertificationInstructionBanner";export{EcWordFillStructuredForm}from"./EcWordFillStructuredForm";export{EcMatchByWritingAnswer}from"./EcMatchByWritingAnswer";export{EcAnswerTheQuestion}from"./EcAnswerTheQuestion";export{EcWordFillParagraph}from"./EcWordFillParagraph";export{EcWritingTask}from"./EcWritingTask";
@@ -2,14 +2,12 @@ import type { ComponentType } from 'react';
2
2
  import type { SpeakingLayoutProps, StandardExamLayoutProps, StandardQuestionCardProps } from './types';
3
3
  export * from './cambridge-yle';
4
4
  export * from './english-certification';
5
- export * from './spaceship/SpaceshipLayout';
6
- export * from './spaceship/SpaceshipAudioPlayer';
7
5
  export * from './summer-sky/SummerSkyAudioPlayer';
8
6
  export * from './summer-sky/SummerSkyLayout';
9
7
  export * from './summer-sky/SummerSkySubmitModal';
10
8
  export * from './summer-sky/SummerSkyWaitingRoom';
11
9
  export type * from './types';
12
- export type SpeakingThemeId = 'SPACESHIP' | 'SUMMER_SKY' | string;
10
+ export type SpeakingThemeId = 'SUMMER_SKY' | string;
13
11
  export declare function getSpeakingLayout(themeId?: SpeakingThemeId): ComponentType<SpeakingLayoutProps>;
14
12
  export interface StandardThemeEntry {
15
13
  Layout: ComponentType<StandardExamLayoutProps>;
@@ -1 +1 @@
1
- import{SpaceshipLayout as r}from"./spaceship/SpaceshipLayout";import{SummerSkyLayout as e}from"./summer-sky/SummerSkyLayout";import{CambridgeYleExamLayout as m}from"./cambridge-yle/CambridgeYleExamLayout";import o from"./cambridge-yle/CambridgeYleQuestionCard";export*from"./cambridge-yle";export*from"./english-certification";export*from"./spaceship/SpaceshipLayout";export*from"./spaceship/SpaceshipAudioPlayer";export*from"./summer-sky/SummerSkyAudioPlayer";export*from"./summer-sky/SummerSkyLayout";export*from"./summer-sky/SummerSkySubmitModal";export*from"./summer-sky/SummerSkyWaitingRoom";const t={SPACESHIP:r,SUMMER_SKY:e},a="SUMMER_SKY";export function getSpeakingLayout(r){return t[r||a]||t[a]}const i={CAMBRIDGE_YLE:{Layout:m,QuestionCard:o}},p="CAMBRIDGE_YLE";export function getStandardExamTheme(r){return i[r||p]||i[p]}
1
+ import{SummerSkyLayout as r}from"./summer-sky/SummerSkyLayout";import{CambridgeYleExamLayout as m}from"./cambridge-yle/CambridgeYleExamLayout";import e from"./cambridge-yle/CambridgeYleQuestionCard";export*from"./cambridge-yle";export*from"./english-certification";export*from"./summer-sky/SummerSkyAudioPlayer";export*from"./summer-sky/SummerSkyLayout";export*from"./summer-sky/SummerSkySubmitModal";export*from"./summer-sky/SummerSkyWaitingRoom";const o={SUMMER_SKY:r},t="SUMMER_SKY";export function getSpeakingLayout(r){return o[r||t]||o[t]}const u={CAMBRIDGE_YLE:{Layout:m,QuestionCard:e}},a="CAMBRIDGE_YLE";export function getStandardExamTheme(r){return u[r||a]||u[a]}
@@ -0,0 +1,14 @@
1
+ /** Detects whether a CMS string carries HTML markup vs plain text. */
2
+ export declare function isHtmlContent(value: string): boolean;
3
+ interface HtmlOrTextProps {
4
+ value: string;
5
+ className?: string;
6
+ /** Element used for plain-text fallback. Default: `p`. */
7
+ as?: 'p' | 'div' | 'span';
8
+ }
9
+ /**
10
+ * Renders CMS-authored content as HTML when it contains tags, otherwise as
11
+ * plain text. Content comes from the exam CMS (not end-user input).
12
+ */
13
+ export declare function HtmlOrText({ value, className, as }: HtmlOrTextProps): import("react").JSX.Element;
14
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as t}from"react/jsx-runtime";import{cn as l}from"../lib/utils";export function isHtmlContent(t){return/<[a-z][\s\S]*>/i.test(t)}export function HtmlOrText({value:e,className:s,as:i="p"}){if(!e)return null;if(isHtmlContent(e))return t("div",{className:l("[&_p]:mb-2 [&_p:last-child]:mb-0 [&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-5 [&_strong]:font-semibold",s),dangerouslySetInnerHTML:{__html:e}});return t(i,{className:s,children:e})}
@@ -42,7 +42,6 @@ export declare enum SkillEnum {
42
42
  export declare enum ExamTemplateTheme {
43
43
  CAMBRIDGE_YLE = "CAMBRIDGE_YLE",
44
44
  FLYER_WOODEN = "FLYER_WOODEN",
45
- SPACESHIP = "SPACESHIP",
46
45
  SUMMER_SKY = "SUMMER_SKY",
47
46
  IELTS_LRW = "IELTS_LRW"
48
47
  }
@@ -1 +1 @@
1
- export var UserRole;!function(E){E.SYSTEM_ADMIN="SYSTEM_ADMIN",E.ADMIN="ADMIN",E.TEACHER="TEACHER",E.STUDENT="STUDENT"}(UserRole||(UserRole={}));export var Gender;!function(E){E.MALE="male",E.FEMALE="female",E.OTHER="other"}(Gender||(Gender={}));export var RoomType;!function(E){E.CONTEST="CONTEST",E.PRACTICE_TEST="PRACTICE_TEST"}(RoomType||(RoomType={}));export var ExamRoomStatus;!function(E){E.SCHEDULED="SCHEDULED",E.IN_PROGRESS="IN_PROGRESS",E.COMPLETED="COMPLETED"}(ExamRoomStatus||(ExamRoomStatus={}));export var StudentSelectionType;!function(E){E.ALL="ALL",E.SPECIFIC="SPECIFIC"}(StudentSelectionType||(StudentSelectionType={}));export var SkillEnum;!function(E){E.LISTENING="LISTENING",E.SPEAKING="SPEAKING",E.READING="READING",E.WRITING="WRITING"}(SkillEnum||(SkillEnum={}));export var ExamTemplateTheme;!function(E){E.CAMBRIDGE_YLE="CAMBRIDGE_YLE",E.FLYER_WOODEN="FLYER_WOODEN",E.SPACESHIP="SPACESHIP",E.SUMMER_SKY="SUMMER_SKY",E.IELTS_LRW="IELTS_LRW"}(ExamTemplateTheme||(ExamTemplateTheme={}));
1
+ export var UserRole;!function(E){E.SYSTEM_ADMIN="SYSTEM_ADMIN",E.ADMIN="ADMIN",E.TEACHER="TEACHER",E.STUDENT="STUDENT"}(UserRole||(UserRole={}));export var Gender;!function(E){E.MALE="male",E.FEMALE="female",E.OTHER="other"}(Gender||(Gender={}));export var RoomType;!function(E){E.CONTEST="CONTEST",E.PRACTICE_TEST="PRACTICE_TEST"}(RoomType||(RoomType={}));export var ExamRoomStatus;!function(E){E.SCHEDULED="SCHEDULED",E.IN_PROGRESS="IN_PROGRESS",E.COMPLETED="COMPLETED"}(ExamRoomStatus||(ExamRoomStatus={}));export var StudentSelectionType;!function(E){E.ALL="ALL",E.SPECIFIC="SPECIFIC"}(StudentSelectionType||(StudentSelectionType={}));export var SkillEnum;!function(E){E.LISTENING="LISTENING",E.SPEAKING="SPEAKING",E.READING="READING",E.WRITING="WRITING"}(SkillEnum||(SkillEnum={}));export var ExamTemplateTheme;!function(E){E.CAMBRIDGE_YLE="CAMBRIDGE_YLE",E.FLYER_WOODEN="FLYER_WOODEN",E.SUMMER_SKY="SUMMER_SKY",E.IELTS_LRW="IELTS_LRW"}(ExamTemplateTheme||(ExamTemplateTheme={}));
@@ -16,6 +16,8 @@ export interface StoreTemplatePart {
16
16
  id: string | number;
17
17
  name: string;
18
18
  category?: string;
19
+ /** Max points for this part from exams.template.parts */
20
+ points?: number;
19
21
  startIndex?: number;
20
22
  endIndex?: number;
21
23
  questionCount?: number;
@@ -102,6 +102,17 @@ export interface AttemptQuestionsResponse {
102
102
  } | null;
103
103
  questions: any[];
104
104
  }
105
+ /** Summary of one exam part for student waiting room / room detail */
106
+ export interface StudentExamPartSummary {
107
+ id: string;
108
+ name: string;
109
+ skill?: string;
110
+ category?: string;
111
+ points: number;
112
+ questionCount: number;
113
+ instructions?: string;
114
+ questionType?: string;
115
+ }
105
116
  export interface StudentRoom {
106
117
  id: string;
107
118
  name: string;
@@ -127,9 +138,12 @@ export interface StudentRoom {
127
138
  passScore: number;
128
139
  numberOfParts: number | null;
129
140
  level?: string;
141
+ theme?: string;
130
142
  template?: {
131
143
  theme?: string;
132
144
  };
145
+ /** Summary parts from exams.template.parts */
146
+ parts?: StudentExamPartSummary[];
133
147
  };
134
148
  contest?: {
135
149
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyweb_dev/oe-exam-sdk",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Reusable OceanEdu question components.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "build:compile": "tsc -p tsconfig.build.json",
22
22
  "build:alias": "tsc-alias -p tsconfig.build.json",
23
23
  "build:ts": "npm run build:compile && npm run build:assets && npm run build:alias",
24
- "build:assets": "for d in cambridge-yle english-certification summer-sky spaceship; do [ -d src/components/themes/$d/assets ] && cp -R src/components/themes/$d/assets dist/components/themes/$d/ || true; done; [ -d src/components/shared/audio/assets ] && cp -R src/components/shared/audio/assets dist/components/shared/audio/ || true; [ -d src/components/results/report/assets ] && mkdir -p dist/components/results/report && cp -R src/components/results/report/assets dist/components/results/report/ || true; (cd src && find . -name '*.module.css' -exec cp --parents {} ../dist/ \\;); cp src/global.d.ts dist/global.d.ts",
24
+ "build:assets": "for d in cambridge-yle english-certification summer-sky; do [ -d src/components/themes/$d/assets ] && cp -R src/components/themes/$d/assets dist/components/themes/$d/ || true; done; [ -d src/components/shared/audio/assets ] && cp -R src/components/shared/audio/assets dist/components/shared/audio/ || true; [ -d src/components/results/report/assets ] && mkdir -p dist/components/results/report && cp -R src/components/results/report/assets dist/components/results/report/ || true; (cd src && find . -name '*.module.css' -exec cp --parents {} ../dist/ \\;); cp src/global.d.ts dist/global.d.ts",
25
25
  "minify:js": "find dist -name '*.js' -type f -exec sh -c 'for f do terser \"$f\" --compress directives=false --mangle --module --comments false --output \"$f\"; done' sh {} +",
26
26
  "build": "npm run clean && npm run build:ts && npm run minify:js",
27
27
  "pack:local": "npm pack",
@@ -1,8 +0,0 @@
1
- interface SpaceshipAudioPlayerProps {
2
- audioUrl: string;
3
- partName?: string;
4
- onEnded?: () => void;
5
- onPlayingChange?: (isPlaying: boolean) => void;
6
- }
7
- export declare function SpaceshipAudioPlayer({ audioUrl, partName, onEnded, onPlayingChange }: SpaceshipAudioPlayerProps): import("react").JSX.Element;
8
- export {};
@@ -1 +0,0 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as a,useState as r,useEffect as n}from"react";import s from"next/image";import{Pause as i,Play as l,VolumeX as o}from"lucide-react";import{usePresignedFileUrl as d}from"../../../shared/lib/hooks/usePresignedFileUrl";import c from"./assets/styles/SpaceshipLayout.module.css";import u from"./assets/images/big-blue-badge-sunset.png";import m from"./assets/images/teacher-play-sunset.png";import p from"./assets/images/ic-mute-sunset.png";function v(e){if(isNaN(e)||!isFinite(e))return"0:00";const t=Math.floor(e/60),a=Math.floor(e%60);return`${t}:${String(a).padStart(2,"0")}`}export function SpaceshipAudioPlayer({audioUrl:h,partName:f="Part 1 - Phase 2",onEnded:g,onPlayingChange:N}){const w=a(null),[y,b]=r(!1),[L,E]=r(0),[x,k]=r(0),[C,B]=r(100),{previewUrl:P}=d(h||void 0);n(()=>{const e=w.current;if(!e)return;const t=()=>{e.duration&&!isNaN(e.duration)&&isFinite(e.duration)&&k(e.duration)},a=()=>{E(e.currentTime),t()},r=()=>{b(!0),N?.(!0)},n=()=>{b(!1),N?.(!1)},s=()=>{b(!1),E(0),N?.(!1),g?.()};return t(),e.addEventListener("loadedmetadata",t),e.addEventListener("durationchange",t),e.addEventListener("canplay",t),e.addEventListener("timeupdate",a),e.addEventListener("play",r),e.addEventListener("pause",n),e.addEventListener("ended",s),()=>{e.removeEventListener("loadedmetadata",t),e.removeEventListener("durationchange",t),e.removeEventListener("canplay",t),e.removeEventListener("timeupdate",a),e.removeEventListener("play",r),e.removeEventListener("pause",n),e.removeEventListener("ended",s)}},[P,g]),n(()=>{b(!1),E(0),k(0)},[P]),n(()=>{const e=w.current;if(!e||!P)return;let t=!1;const a=()=>{t||e.play().catch(()=>{if(t)return;const a=()=>{t||(e.play().catch(()=>{}),document.removeEventListener("click",a),document.removeEventListener("keydown",a))};document.addEventListener("click",a,{once:!0}),document.addEventListener("keydown",a,{once:!0})})};return e.readyState>=3?a():e.addEventListener("canplay",a,{once:!0}),()=>{t=!0,e.removeEventListener("canplay",a)}},[P]);const F=x>0?L/x*100:0;return t("div",{className:c.examAudioContainer,children:[e("audio",{ref:w,...P?{src:P}:{},preload:"metadata"}),t("div",{className:c.bigBlueBadge,children:[e(s,{src:u,alt:"Audio Badge",className:c.bigBlueBadgeBg,priority:!0}),e("div",{className:c.audioBarContent,children:t("div",{className:c.audioBarInner,children:[e("span",{className:c.partLabel,children:f}),t("div",{className:c.teacherAvatarContainer,onClick:P?()=>{const e=w.current;e&&(e.paused?e.play().catch(()=>{}):e.pause())}:void 0,children:[e("img",{src:m.src,alt:"Play",className:c.teacherPlayIcon}),e("div",{className:"absolute inset-0 flex items-center justify-center",children:y?e(i,{className:"h-5 w-5 text-white drop-shadow-md",fill:"currentColor"}):e(l,{className:"h-5 w-5 text-white drop-shadow-md ml-1",fill:"currentColor"})})]}),t("div",{className:c.audioControlsWrapper,children:[t("span",{className:c.audioTime,children:[v(L)," / ",v(x)]}),t("div",{className:c.sliderTrack,children:[e("div",{className:c.sliderFill,style:{width:`${F}%`}}),e("input",{type:"range",min:"0",max:x||0,step:"0.1",value:L,onChange:e=>{const t=w.current;if(!t)return;const a=parseFloat(e.target.value);t.currentTime=a,E(a)},className:"absolute inset-0 w-full h-full opacity-0 cursor-pointer disabled:cursor-not-allowed",disabled:!P})]}),t("div",{className:c.volumeContainer,children:[e("div",{className:c.volumeIcon,onClick:()=>{const e=w.current;e&&(C>0?(e.volume=0,B(0)):(e.volume=1,B(100)))},children:0===C?e(o,{className:"text-white w-full h-full"}):e(s,{src:p,alt:"volume",className:"w-full h-full"})}),e("div",{className:"flex-grow items-center flex",style:{width:"60px"},children:e("input",{type:"range",min:"0",max:"100",value:C,onChange:e=>{const t=w.current;if(!t)return;const a=parseFloat(e.target.value);t.volume=a/100,B(a)},className:"w-full h-[3px] appearance-none bg-white/50 rounded cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-2.5 [&::-webkit-slider-thumb]:h-2.5 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white"})})]})]})]})})]})]})}
@@ -1,2 +0,0 @@
1
- import type { SpeakingLayoutProps } from '../types';
2
- export declare function SpaceshipLayout({ onExit, partName, headerRight, onPrev, onNext, canGoPrev, canGoNext, audioUrl, onAudioEnded, recorder, children, contentClassName, hideAudioBar, onAudioPlayingChange, }: SpeakingLayoutProps): import("react").JSX.Element;
@@ -1 +0,0 @@
1
- "use client";import{jsx as e,jsxs as t,Fragment as s}from"react/jsx-runtime";import a from"next/image";import r from"./assets/styles/SpaceshipLayout.module.css";import{SpaceshipAudioPlayer as i}from"./SpaceshipAudioPlayer";import n from"./assets/images/circle-btn-right-sunset.png";import l from"./assets/images/circle-btn-sunset.png";import c from"./assets/images/next-sunset.png";import o from"./assets/images/X-sunset.png";import m from"./assets/images/button_feedback_vi.png";import d from"./assets/images/blue-wrapper-sunset.png";import p from"./assets/images/timer-badge.png";import h from"./assets/images/micro.png";export function SpaceshipLayout({onExit:g,partName:u,headerRight:f,onPrev:x,onNext:v,canGoPrev:N,canGoNext:b,audioUrl:y,onAudioEnded:B,recorder:w,children:C,contentClassName:S="",hideAudioBar:k,onAudioPlayingChange:I}){return t("div",{id:"exam-main-scroll-area",className:r.pageContainer,children:[e("div",{className:r.examContainer,children:t("div",{className:r.examMainBoard,children:[e("div",{className:r.headerTitleWrapper,children:e("div",{className:r.exitButtonWrapper,children:t("div",{className:r.circleBtn,tabIndex:0,onClick:g,children:[e(a,{src:n,alt:"btn",className:r.circleBtnBg}),e("div",{className:r.circleBtnInner,children:e(a,{src:o,alt:"X",className:r.exitIcon})})]})})}),N&&e("div",{className:r.controlLeft,children:t("div",{className:r.circleBtn,onClick:x,children:[e(a,{src:n,alt:"btn",className:r.circleBtnBg}),e("div",{className:r.circleBtnInner,children:e(a,{src:c,alt:"prev",className:`${r.nextPrevIcon} ${r.rotate}`})})]})}),t("div",{className:r.examContentMainContainer,children:[!k&&e(i,{audioUrl:y,partName:u,onEnded:B,onPlayingChange:I}),t("div",{className:r.examContentSpace,children:[f&&e("div",{className:"absolute top-2 right-6 z-50",children:f}),e("div",{className:`w-full h-full flex flex-col items-center justify-center ${S}`,children:C})]}),w&&t(s,{children:[e("p",{className:"absolute left-1/2 -translate-x-1/2 text-center whitespace-nowrap text-black font-bold m-0",style:{bottom:"115px",fontSize:"16px",fontFamily:'"Open Sans", sans-serif'},children:"uploaded"===w.status?"Đã ghi nhận câu trả lời":"Ấn biểu tượng micro để trả lời"}),e("div",{className:`absolute left-1/2 -translate-x-1/2 flex flex-col items-center z-10 transition-transform ${"recording"===w.status?"scale-[1.05]":"hover:scale-[1.02] active:scale-[0.98]"} ${"uploaded"===w.status?"opacity-80 grayscale-[20%] pointer-events-none":""}`,style:{bottom:"-40px",overflow:"visible"},children:t("div",{className:"block relative",style:{overflow:"visible"},children:[e(a,{src:d,alt:"wrapper",className:"z-0 "+("recording"===w.status?"animate-pulse":""),style:{width:"158px",height:"116px"}}),"uploaded"!==w.status&&t("div",{className:"absolute left-1/2 -translate-x-1/2 z-20",style:{top:"-32px"},children:[e(a,{src:p,alt:"timer",style:{width:"104px",height:"45px"}}),e("div",{className:"absolute inset-0 flex items-center justify-center",children:t("p",{className:"text-white text-center text-[20px] font-normal m-0",style:{margin:0},children:[Math.floor(w.remainingSeconds/60),":",(w.remainingSeconds%60).toString().padStart(2,"0")]})})]}),e("div",{className:"absolute inset-0 flex justify-center items-center w-full h-full",children:e(a,{src:h,alt:"micro",className:"cursor-pointer "+("recording"===w.status?"drop-shadow-[0_0_15px_rgba(0,255,255,0.8)]":""),style:{width:"51px",height:"51px"},onClick:"recording"===w.status?w.stopRecording:"idle"===w.status||"error"===w.status?w.startRecording:void 0})})]})})]})]}),b&&e("div",{className:r.controlRight,children:t("div",{className:r.circleBtn,onClick:v,children:[e(a,{src:l,alt:"btn",className:r.circleBtnBg}),e("div",{className:r.circleBtnInner,children:e(a,{src:c,alt:"next",className:r.nextPrevIcon})})]})})]})}),e("div",{className:r.feedbackButton,children:e("a",{target:"_blank",href:"https://forms.gle/DxdZqfgSirYzf1vW8",rel:"noopener noreferrer",children:e(a,{alt:"feedback",src:m})})})]})}