@hydralms/components 0.1.3 → 0.2.0

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 (199) hide show
  1. package/dist/ForumBoard-CHXU3mjC.js +2207 -0
  2. package/dist/ForumBoard-d1w5-r6n.cjs +1 -0
  3. package/dist/assessment-toolbar/assessment-toolbar.d.ts +1 -1
  4. package/dist/assessment-toolbar/index.d.ts +5 -1
  5. package/dist/assessment-toolbar/question-header-bar.d.ts +2 -0
  6. package/dist/assessment-toolbar/question-materials-drawer.d.ts +2 -0
  7. package/dist/assessment-toolbar/question-navigator.d.ts +1 -1
  8. package/dist/assessment-toolbar/types.d.ts +52 -4
  9. package/dist/assessment-toolbar/use-countdown.d.ts +43 -0
  10. package/dist/common/index.d.ts +2 -1
  11. package/dist/common/stepper.d.ts +6 -0
  12. package/dist/common/types.d.ts +37 -0
  13. package/dist/components.css +1 -1
  14. package/dist/content/attachment-list.d.ts +6 -0
  15. package/dist/content/content-block.d.ts +1 -1
  16. package/dist/content/index.d.ts +2 -1
  17. package/dist/content/types.d.ts +39 -0
  18. package/dist/curriculum/curriculum-item.d.ts +1 -1
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.js +551 -312
  21. package/dist/modules/AssignmentModule/AssignmentModule.d.ts +8 -0
  22. package/dist/modules/AssignmentModule/types.d.ts +65 -0
  23. package/dist/modules/CertificateModule/CertificateModule.d.ts +9 -0
  24. package/dist/modules/CertificateModule/types.d.ts +49 -0
  25. package/dist/modules/DiscussionModule/DiscussionModule.d.ts +8 -0
  26. package/dist/modules/DiscussionModule/types.d.ts +47 -0
  27. package/dist/modules/ExamModule/ExamModule.d.ts +8 -0
  28. package/dist/modules/ExamModule/types.d.ts +64 -0
  29. package/dist/modules/GradeCenterModule/GradeCenterModule.d.ts +9 -0
  30. package/dist/modules/GradeCenterModule/types.d.ts +54 -0
  31. package/dist/modules/QuizModule/QuizModule.d.ts +1 -1
  32. package/dist/modules/QuizModule/types.d.ts +6 -1
  33. package/dist/modules/SurveyModule/SurveyModule.d.ts +7 -0
  34. package/dist/modules/SurveyModule/types.d.ts +49 -0
  35. package/dist/modules/index.d.ts +12 -0
  36. package/dist/modules.cjs +1 -0
  37. package/dist/modules.js +1422 -0
  38. package/dist/progress/achievement-badge.d.ts +6 -0
  39. package/dist/progress/activity-timeline.d.ts +6 -0
  40. package/dist/progress/index.d.ts +4 -1
  41. package/dist/progress/stat-card.d.ts +1 -1
  42. package/dist/progress/streak-badge.d.ts +6 -0
  43. package/dist/progress/types.d.ts +97 -0
  44. package/dist/questions/essay.d.ts +1 -1
  45. package/dist/questions/hotspot.d.ts +21 -0
  46. package/dist/questions/index.d.ts +9 -1
  47. package/dist/questions/inline-choice.d.ts +21 -0
  48. package/dist/questions/matching.d.ts +22 -0
  49. package/dist/questions/numeric.d.ts +11 -0
  50. package/dist/questions/ordering.d.ts +12 -0
  51. package/dist/questions/scenario.d.ts +23 -0
  52. package/dist/questions/scoring.d.ts +22 -0
  53. package/dist/questions/spreadsheet.d.ts +29 -0
  54. package/dist/questions/types.d.ts +106 -1
  55. package/dist/questions/use-drag-reorder.d.ts +17 -0
  56. package/dist/sections/CertificateViewer/types.d.ts +7 -5
  57. package/dist/sections/ExamSession/ExamSession.d.ts +1 -1
  58. package/dist/sections/ExamSession/types.d.ts +6 -1
  59. package/dist/sections/ForumBoard/ForumBoard.d.ts +8 -0
  60. package/dist/sections/ForumBoard/types.d.ts +64 -0
  61. package/dist/sections/QuizSession/QuizSession.d.ts +1 -1
  62. package/dist/sections/QuizSession/types.d.ts +6 -1
  63. package/dist/sections/RequirementsChecklist/RequirementsChecklist.d.ts +8 -0
  64. package/dist/sections/RequirementsChecklist/types.d.ts +37 -0
  65. package/dist/sections/RubricView/RubricView.d.ts +9 -0
  66. package/dist/sections/RubricView/types.d.ts +50 -0
  67. package/dist/sections/index.d.ts +7 -1
  68. package/dist/sections.cjs +1 -1
  69. package/dist/sections.js +250 -1715
  70. package/dist/social/post-card.d.ts +1 -1
  71. package/dist/tabs-DRM2Iq_J.cjs +172 -0
  72. package/dist/tabs-Wf3h_Cx3.js +21580 -0
  73. package/dist/ui/alert.d.ts +1 -1
  74. package/dist/ui/badge.d.ts +1 -1
  75. package/dist/ui/button.d.ts +1 -1
  76. package/dist/ui/drawer.d.ts +84 -0
  77. package/dist/ui/index.d.ts +3 -0
  78. package/dist/ui/progress.d.ts +1 -1
  79. package/dist/ui/rich-text-editor.d.ts +30 -0
  80. package/dist/ui/rich-text-toolbar.d.ts +8 -0
  81. package/dist/utils/array-utils.d.ts +4 -0
  82. package/dist/utils/flatten-leaves.d.ts +6 -0
  83. package/dist/utils/format-file-size.d.ts +1 -0
  84. package/dist/utils/format-timestamp.d.ts +1 -0
  85. package/dist/utils/is-empty-html.d.ts +5 -0
  86. package/dist/utils/shuffle.d.ts +1 -0
  87. package/dist/utils/string-utils.d.ts +12 -0
  88. package/dist/video/video-bookmark.d.ts +1 -1
  89. package/dist/video/video-playlist-item.d.ts +1 -1
  90. package/package.json +92 -3
  91. package/src/assessment-toolbar/assessment-toolbar.tsx +54 -49
  92. package/src/assessment-toolbar/index.ts +6 -0
  93. package/src/assessment-toolbar/question-header-bar.tsx +61 -0
  94. package/src/assessment-toolbar/question-materials-drawer.tsx +55 -0
  95. package/src/assessment-toolbar/question-navigator.tsx +3 -31
  96. package/src/assessment-toolbar/timer-display.tsx +2 -2
  97. package/src/assessment-toolbar/types.ts +54 -4
  98. package/src/assessment-toolbar/use-countdown.ts +153 -0
  99. package/src/common/index.ts +3 -0
  100. package/src/common/search-input.tsx +7 -6
  101. package/src/common/stepper.tsx +100 -0
  102. package/src/common/types.ts +39 -0
  103. package/src/content/attachment-list.tsx +90 -0
  104. package/src/content/content-block.tsx +4 -2
  105. package/src/content/file-upload-zone.tsx +1 -6
  106. package/src/content/index.ts +3 -0
  107. package/src/content/types.ts +41 -0
  108. package/src/curriculum/curriculum-item.tsx +7 -3
  109. package/src/feedback/feedback-banner.tsx +12 -14
  110. package/src/flashcards/flashcard-deck.tsx +1 -9
  111. package/src/flashcards/flashcard.tsx +1 -1
  112. package/src/modules/AssignmentModule/AssignmentModule.tsx +305 -0
  113. package/src/modules/AssignmentModule/types.ts +73 -0
  114. package/src/modules/CertificateModule/CertificateModule.tsx +161 -0
  115. package/src/modules/CertificateModule/types.ts +47 -0
  116. package/src/modules/CoursePlayer/CoursePlayer.tsx +44 -48
  117. package/src/modules/DiscussionModule/DiscussionModule.tsx +110 -0
  118. package/src/modules/DiscussionModule/types.ts +54 -0
  119. package/src/modules/ExamModule/ExamModule.tsx +285 -0
  120. package/src/modules/ExamModule/types.ts +66 -0
  121. package/src/modules/FlashcardLab/FlashcardLab.tsx +29 -16
  122. package/src/modules/GradeCenterModule/GradeCenterModule.tsx +169 -0
  123. package/src/modules/GradeCenterModule/types.ts +63 -0
  124. package/src/modules/QuizModule/QuizModule.tsx +88 -88
  125. package/src/modules/QuizModule/types.ts +6 -1
  126. package/src/modules/SurveyModule/SurveyModule.tsx +180 -0
  127. package/src/modules/SurveyModule/types.ts +51 -0
  128. package/src/modules/index.ts +24 -0
  129. package/src/progress/achievement-badge.tsx +52 -0
  130. package/src/progress/activity-timeline.tsx +84 -0
  131. package/src/progress/index.ts +7 -0
  132. package/src/progress/stat-card.tsx +30 -18
  133. package/src/progress/streak-badge.tsx +35 -0
  134. package/src/progress/types.ts +101 -0
  135. package/src/questions/choice.tsx +7 -9
  136. package/src/questions/essay.tsx +23 -25
  137. package/src/questions/fill-in-the-blank.tsx +13 -16
  138. package/src/questions/hotspot.tsx +154 -0
  139. package/src/questions/index.ts +16 -0
  140. package/src/questions/inline-choice.tsx +151 -0
  141. package/src/questions/matching.tsx +228 -0
  142. package/src/questions/multiple-choice.tsx +7 -9
  143. package/src/questions/numeric.tsx +102 -0
  144. package/src/questions/ordering.tsx +159 -0
  145. package/src/questions/question-renderer.tsx +21 -0
  146. package/src/questions/scenario.tsx +140 -0
  147. package/src/questions/scoring.ts +201 -0
  148. package/src/questions/spreadsheet.tsx +259 -0
  149. package/src/questions/true-false.tsx +7 -9
  150. package/src/questions/types.ts +123 -1
  151. package/src/questions/use-drag-reorder.ts +80 -0
  152. package/src/sections/AnnouncementFeed/AnnouncementFeed.tsx +2 -15
  153. package/src/sections/AssessmentReview/AssessmentReview.tsx +13 -2
  154. package/src/sections/AssignmentSubmission/AssignmentSubmission.tsx +7 -5
  155. package/src/sections/CertificateViewer/CertificateViewer.tsx +409 -56
  156. package/src/sections/CertificateViewer/types.ts +13 -5
  157. package/src/sections/CourseOutline/CourseOutline.tsx +4 -14
  158. package/src/sections/DiscussionThread/DiscussionThread.tsx +13 -10
  159. package/src/sections/ExamSession/ExamSession.tsx +44 -7
  160. package/src/sections/ExamSession/types.ts +6 -1
  161. package/src/sections/ForumBoard/ForumBoard.tsx +284 -0
  162. package/src/sections/ForumBoard/types.ts +67 -0
  163. package/src/sections/GradebookTable/GradebookTable.tsx +1 -1
  164. package/src/sections/LecturePlayer/LecturePlayer.tsx +1 -1
  165. package/src/sections/LessonPage/LessonPage.tsx +5 -9
  166. package/src/sections/PracticeQuiz/PracticeQuiz.tsx +15 -26
  167. package/src/sections/ProgressDashboard/ProgressDashboard.tsx +65 -65
  168. package/src/sections/QuizSession/QuizSession.tsx +67 -8
  169. package/src/sections/QuizSession/types.ts +6 -1
  170. package/src/sections/RequirementsChecklist/RequirementsChecklist.tsx +107 -0
  171. package/src/sections/RequirementsChecklist/types.ts +38 -0
  172. package/src/sections/ResourceLibrary/ResourceLibrary.tsx +4 -9
  173. package/src/sections/RubricView/RubricView.tsx +138 -0
  174. package/src/sections/RubricView/types.ts +52 -0
  175. package/src/sections/ScrollableQuiz/ScrollableQuiz.tsx +23 -9
  176. package/src/sections/SurveyForm/SurveyForm.tsx +8 -5
  177. package/src/sections/index.ts +20 -1
  178. package/src/social/post-card.tsx +8 -19
  179. package/src/social/user-avatar.tsx +1 -0
  180. package/src/styles/globals.css +13 -0
  181. package/src/ui/drawer.tsx +600 -0
  182. package/src/ui/index.ts +19 -0
  183. package/src/ui/rich-text-editor.tsx +109 -0
  184. package/src/ui/rich-text-toolbar.tsx +156 -0
  185. package/src/utils/array-utils.ts +17 -0
  186. package/src/utils/flatten-leaves.ts +17 -0
  187. package/src/utils/format-file-size.ts +5 -0
  188. package/src/utils/format-timestamp.ts +13 -0
  189. package/src/utils/is-empty-html.ts +7 -0
  190. package/src/utils/shuffle.ts +8 -0
  191. package/src/utils/string-utils.ts +30 -0
  192. package/src/video/video-bookmark.tsx +4 -3
  193. package/src/video/video-chapter-list.tsx +9 -4
  194. package/src/video/video-player.tsx +11 -4
  195. package/src/video/video-playlist-item.tsx +8 -3
  196. package/src/video/video-thumbnail-card.tsx +4 -0
  197. package/src/video/video-transcript.tsx +8 -5
  198. package/dist/table-BrS5cDQu.js +0 -2510
  199. package/dist/table-D6AkBBEo.cjs +0 -1
@@ -1,7 +1,7 @@
1
1
  import { VariantProps } from 'class-variance-authority';
2
2
  import * as React from "react";
3
3
  declare const alertVariants: (props?: ({
4
- variant?: "default" | "destructive" | "success" | "warning" | "info" | null | undefined;
4
+ variant?: "info" | "warning" | "default" | "destructive" | "success" | null | undefined;
5
5
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
6
6
  declare function Alert({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>): import("react/jsx-runtime").JSX.Element;
7
7
  declare function AlertTitle({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { VariantProps } from 'class-variance-authority';
2
2
  import * as React from "react";
3
3
  declare const badgeVariants: (props?: ({
4
- variant?: "default" | "destructive" | "success" | "warning" | "info" | "outline" | "secondary" | "muted" | null | undefined;
4
+ variant?: "info" | "warning" | "default" | "destructive" | "success" | "muted" | "outline" | "secondary" | null | undefined;
5
5
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
6
6
  declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
7
7
  asChild?: boolean;
@@ -2,7 +2,7 @@ import { VariantProps } from 'class-variance-authority';
2
2
  import * as React from "react";
3
3
  declare const buttonVariants: (props?: ({
4
4
  variant?: "default" | "destructive" | "link" | "outline" | "secondary" | "ghost" | null | undefined;
5
- size?: "default" | "icon" | "xs" | "sm" | "lg" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
5
+ size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
6
6
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
7
  declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
8
8
  asChild?: boolean;
@@ -0,0 +1,84 @@
1
+ import * as React from "react";
2
+ declare const drawerContentVariants: (props?: ({
3
+ side?: "left" | "right" | null | undefined;
4
+ size?: "default" | "full" | "sm" | "lg" | "xl" | null | undefined;
5
+ } & import('class-variance-authority/types').ClassProp) | undefined) => string;
6
+ /**
7
+ * A slide-in side panel for contextual content like navigation, settings, or detail views.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <Drawer open={open} onOpenChange={setOpen} side="right">
12
+ * <DrawerContent>
13
+ * <DrawerHeader><DrawerTitle>Panel</DrawerTitle></DrawerHeader>
14
+ * <DrawerBody>Content</DrawerBody>
15
+ * </DrawerContent>
16
+ * </Drawer>
17
+ * ```
18
+ */
19
+ interface DrawerProps {
20
+ /** Compound component children (DrawerContent, DrawerTrigger, etc.) */
21
+ children: React.ReactNode;
22
+ /** Whether the drawer is open. */
23
+ open: boolean;
24
+ /** Callback fired when the drawer should open or close. */
25
+ onOpenChange: (open: boolean) => void;
26
+ /** Which edge of the viewport the drawer slides in from. */
27
+ side?: "left" | "right";
28
+ }
29
+ declare function Drawer({ children, open, onOpenChange, side }: DrawerProps): import("react/jsx-runtime").JSX.Element;
30
+ declare function DrawerTrigger({ className, onClick, ...props }: React.ComponentProps<"button">): import("react/jsx-runtime").JSX.Element;
31
+ declare function DrawerPortal({ children }: {
32
+ children: React.ReactNode;
33
+ }): React.ReactPortal;
34
+ declare function DrawerBackdrop({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
35
+ interface DrawerContentProps extends Omit<React.ComponentProps<"div">, "size"> {
36
+ /** Width preset for the drawer panel. */
37
+ size?: "sm" | "default" | "lg" | "xl" | "full";
38
+ /** Whether to lock body scroll when the drawer is open. */
39
+ scrollLock?: boolean;
40
+ }
41
+ declare function DrawerContent({ className, children, size, scrollLock: scrollLockProp, ...props }: DrawerContentProps): import("react/jsx-runtime").JSX.Element | null;
42
+ declare function DrawerHeader({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
43
+ declare function DrawerBody({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
44
+ declare function DrawerFooter({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
45
+ declare function DrawerTitle({ className, ...props }: React.ComponentProps<"h2">): import("react/jsx-runtime").JSX.Element;
46
+ declare function DrawerDescription({ className, ...props }: React.ComponentProps<"p">): import("react/jsx-runtime").JSX.Element;
47
+ declare function DrawerClose({ className, children, onClick, ...props }: React.ComponentProps<"button">): import("react/jsx-runtime").JSX.Element;
48
+ /**
49
+ * A vertical icon rail for switching between views inside a Drawer.
50
+ * Provides controlled navigation state to DrawerNavItem children.
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * <DrawerNav value={activeView} onValueChange={setActiveView}>
55
+ * <DrawerNavItem value="notes" icon={<FileText />} label="Notes" />
56
+ * <DrawerNavItem value="bookmarks" icon={<Bookmark />} label="Bookmarks" />
57
+ * </DrawerNav>
58
+ * ```
59
+ */
60
+ interface DrawerNavProps extends React.ComponentProps<"nav"> {
61
+ /** The value of the currently active nav item. */
62
+ value: string;
63
+ /** Callback fired when the active nav item changes. */
64
+ onValueChange: (value: string) => void;
65
+ }
66
+ declare function DrawerNav({ value, onValueChange, className, children, ...props }: DrawerNavProps): import("react/jsx-runtime").JSX.Element;
67
+ /**
68
+ * An icon button inside a DrawerNav rail. Renders a tooltip on hover.
69
+ */
70
+ interface DrawerNavItemProps extends Omit<React.ComponentProps<"button">, "value"> {
71
+ /** Unique value identifying this view. Must match a DrawerViewport's conditional content. */
72
+ value: string;
73
+ /** Icon element to render (e.g., `<FileText />`). */
74
+ icon: React.ReactNode;
75
+ /** Accessible label shown in the tooltip. */
76
+ label: string;
77
+ }
78
+ declare function DrawerNavItem({ value, icon, label, className, ...props }: DrawerNavItemProps): import("react/jsx-runtime").JSX.Element;
79
+ /**
80
+ * Content panel paired with a DrawerNav rail. Wraps header/body/footer in the
81
+ * remaining space beside the icon rail.
82
+ */
83
+ declare function DrawerViewport({ className, ...props }: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
84
+ export { Drawer, DrawerTrigger, DrawerPortal, DrawerBackdrop, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, DrawerTitle, DrawerDescription, DrawerClose, DrawerNav, DrawerNavItem, DrawerViewport, drawerContentVariants, };
@@ -6,6 +6,7 @@ export { Alert, AlertTitle, AlertDescription, alertVariants } from './alert';
6
6
  export { Separator } from './separator';
7
7
  export { AlertDialog, AlertDialogTrigger, AlertDialogPortal, AlertDialogBackdrop, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, } from './alert-dialog';
8
8
  export { Avatar, AvatarImage, AvatarFallback } from './avatar';
9
+ export { Drawer, DrawerTrigger, DrawerPortal, DrawerBackdrop, DrawerContent, DrawerHeader, DrawerBody, DrawerFooter, DrawerTitle, DrawerDescription, DrawerClose, DrawerNav, DrawerNavItem, DrawerViewport, drawerContentVariants, } from './drawer';
9
10
  export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from './card';
10
11
  export { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs';
11
12
  export { Progress, progressVariants } from './progress';
@@ -13,3 +14,5 @@ export type { ProgressProps } from './progress';
13
14
  export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption, } from './table';
14
15
  export { Skeleton } from './skeleton';
15
16
  export { Tooltip, TooltipTrigger, TooltipContent } from './tooltip';
17
+ export { RichTextEditor } from './rich-text-editor';
18
+ export type { RichTextEditorProps, RichTextEditorVariant } from './rich-text-editor';
@@ -1,7 +1,7 @@
1
1
  import { VariantProps } from 'class-variance-authority';
2
2
  import * as React from "react";
3
3
  declare const progressVariants: (props?: ({
4
- variant?: "default" | "success" | "warning" | "info" | null | undefined;
4
+ variant?: "info" | "warning" | "default" | "success" | null | undefined;
5
5
  size?: "default" | "sm" | null | undefined;
6
6
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
7
  interface ProgressProps extends Omit<React.ComponentProps<"div">, "children">, VariantProps<typeof progressVariants> {
@@ -0,0 +1,30 @@
1
+ export type RichTextEditorVariant = "default" | "minimal";
2
+ export interface RichTextEditorProps {
3
+ /** HTML string value */
4
+ value?: string;
5
+ /** Called with the HTML string on every content change */
6
+ onChange?: (html: string) => void;
7
+ /** Placeholder text shown when the editor is empty */
8
+ placeholder?: string;
9
+ /** Makes the editor non-editable (shows content without toolbar) */
10
+ readOnly?: boolean;
11
+ /** Visually disables the editor */
12
+ disabled?: boolean;
13
+ /** `"default"` shows full toolbar; `"minimal"` shows only basic formatting */
14
+ variant?: RichTextEditorVariant;
15
+ className?: string;
16
+ style?: React.CSSProperties;
17
+ }
18
+ /**
19
+ * RichTextEditor wraps Tiptap to provide a rich text editing experience
20
+ * styled to match the HydraLMS design system.
21
+ *
22
+ * @example
23
+ * <RichTextEditor
24
+ * value={html}
25
+ * onChange={setHtml}
26
+ * placeholder="Write your response..."
27
+ * variant="default"
28
+ * />
29
+ */
30
+ export declare function RichTextEditor({ value, onChange, placeholder, readOnly, disabled, variant, className, style, }: RichTextEditorProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,8 @@
1
+ import { Editor } from '@tiptap/react';
2
+ type RichTextToolbarVariant = "default" | "minimal";
3
+ interface RichTextToolbarProps {
4
+ editor: Editor;
5
+ variant?: RichTextToolbarVariant;
6
+ }
7
+ export declare function RichTextToolbar({ editor, variant, }: RichTextToolbarProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Group an array of items by a key derived from each item.
3
+ */
4
+ export declare function groupBy<T>(arr: T[], keyFn: (item: T) => string): Record<string, T[]>;
@@ -0,0 +1,6 @@
1
+ import { CurriculumItem } from '../curriculum/types';
2
+ /**
3
+ * Recursively collects the UIDs of all leaf nodes (items with no children)
4
+ * in a curriculum tree.
5
+ */
6
+ export declare function flattenLeaves(items: CurriculumItem[]): string[];
@@ -0,0 +1 @@
1
+ export declare function formatFileSize(bytes: number): string;
@@ -0,0 +1 @@
1
+ export declare function formatTimestamp(iso: string): string;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Returns true if an HTML string contains no meaningful text content.
3
+ * Tiptap returns `"<p></p>"` for an empty editor, not an empty string.
4
+ */
5
+ export declare function isEmptyHtml(html: string): boolean;
@@ -0,0 +1 @@
1
+ export declare function shuffle<T>(array: T[]): T[];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Truncate text to a maximum length, appending a suffix if truncated.
3
+ */
4
+ export declare function truncateText(text: string, maxLength: number, suffix?: string): string;
5
+ /**
6
+ * Format a decimal value as a percentage string.
7
+ */
8
+ export declare function formatPercentage(value: number, decimals?: number): string;
9
+ /**
10
+ * Return the singular or plural form of a word based on count.
11
+ */
12
+ export declare function pluralize(count: number, singular: string, plural?: string): string;
@@ -1,2 +1,2 @@
1
1
  import { VideoBookmarkProps } from './types';
2
- export declare const VideoBookmark: ({ bookmark, onSeek, onDelete, onEdit, className, style, }: VideoBookmarkProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const VideoBookmark: import('react').NamedExoticComponent<VideoBookmarkProps>;
@@ -1,2 +1,2 @@
1
1
  import { VideoPlaylistItemProps } from './types';
2
- export declare const VideoPlaylistItem: ({ thumbnail, title, duration, status, isActive, index, onClick, className, style, }: VideoPlaylistItemProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const VideoPlaylistItem: import('react').NamedExoticComponent<VideoPlaylistItemProps>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydralms/components",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "React component library for LMS platforms",
5
5
  "license": "MIT",
6
6
  "author": "HydraLMS",
@@ -115,6 +115,18 @@
115
115
  "types": "./src/sections/CertificateViewer/CertificateViewer.tsx",
116
116
  "default": "./src/sections/CertificateViewer/CertificateViewer.tsx"
117
117
  },
118
+ "./sections/RubricView": {
119
+ "types": "./src/sections/RubricView/RubricView.tsx",
120
+ "default": "./src/sections/RubricView/RubricView.tsx"
121
+ },
122
+ "./sections/RequirementsChecklist": {
123
+ "types": "./src/sections/RequirementsChecklist/RequirementsChecklist.tsx",
124
+ "default": "./src/sections/RequirementsChecklist/RequirementsChecklist.tsx"
125
+ },
126
+ "./sections/ForumBoard": {
127
+ "types": "./src/sections/ForumBoard/ForumBoard.tsx",
128
+ "default": "./src/sections/ForumBoard/ForumBoard.tsx"
129
+ },
118
130
  "./modules": {
119
131
  "types": "./src/modules/index.ts",
120
132
  "default": "./src/modules/index.ts"
@@ -131,6 +143,30 @@
131
143
  "types": "./src/modules/CoursePlayer/CoursePlayer.tsx",
132
144
  "default": "./src/modules/CoursePlayer/CoursePlayer.tsx"
133
145
  },
146
+ "./modules/ExamModule": {
147
+ "types": "./src/modules/ExamModule/ExamModule.tsx",
148
+ "default": "./src/modules/ExamModule/ExamModule.tsx"
149
+ },
150
+ "./modules/SurveyModule": {
151
+ "types": "./src/modules/SurveyModule/SurveyModule.tsx",
152
+ "default": "./src/modules/SurveyModule/SurveyModule.tsx"
153
+ },
154
+ "./modules/GradeCenterModule": {
155
+ "types": "./src/modules/GradeCenterModule/GradeCenterModule.tsx",
156
+ "default": "./src/modules/GradeCenterModule/GradeCenterModule.tsx"
157
+ },
158
+ "./modules/AssignmentModule": {
159
+ "types": "./src/modules/AssignmentModule/AssignmentModule.tsx",
160
+ "default": "./src/modules/AssignmentModule/AssignmentModule.tsx"
161
+ },
162
+ "./modules/CertificateModule": {
163
+ "types": "./src/modules/CertificateModule/CertificateModule.tsx",
164
+ "default": "./src/modules/CertificateModule/CertificateModule.tsx"
165
+ },
166
+ "./modules/DiscussionModule": {
167
+ "types": "./src/modules/DiscussionModule/DiscussionModule.tsx",
168
+ "default": "./src/modules/DiscussionModule/DiscussionModule.tsx"
169
+ },
134
170
  "./provider": {
135
171
  "types": "./src/provider/index.ts",
136
172
  "default": "./src/provider/index.ts"
@@ -183,7 +219,6 @@
183
219
  "types": "./src/provider/HydraProvider.tsx",
184
220
  "default": "./src/provider/HydraProvider.tsx"
185
221
  },
186
- "./*": "./src/*",
187
222
  "./styles.css": "./src/styles/globals.css",
188
223
  "./styles/globals.css": "./src/styles/globals.css",
189
224
  "./package.json": "./package.json"
@@ -203,17 +238,65 @@
203
238
  "import": "./dist/sections.js",
204
239
  "require": "./dist/sections.cjs"
205
240
  },
241
+ "./sections/*": {
242
+ "types": "./src/sections/*/index.ts",
243
+ "default": "./src/sections/*/index.ts"
244
+ },
206
245
  "./modules": {
207
246
  "types": "./dist/modules/index.d.ts",
208
247
  "import": "./dist/modules.js",
209
248
  "require": "./dist/modules.cjs"
210
249
  },
250
+ "./modules/*": {
251
+ "types": "./src/modules/*/index.ts",
252
+ "default": "./src/modules/*/index.ts"
253
+ },
254
+ "./provider": {
255
+ "types": "./src/provider/index.ts",
256
+ "default": "./src/provider/index.ts"
257
+ },
258
+ "./questions": {
259
+ "types": "./src/questions/index.ts",
260
+ "default": "./src/questions/index.ts"
261
+ },
262
+ "./assessment-toolbar": {
263
+ "types": "./src/assessment-toolbar/index.ts",
264
+ "default": "./src/assessment-toolbar/index.ts"
265
+ },
266
+ "./flashcards": {
267
+ "types": "./src/flashcards/index.ts",
268
+ "default": "./src/flashcards/index.ts"
269
+ },
270
+ "./curriculum": {
271
+ "types": "./src/curriculum/index.ts",
272
+ "default": "./src/curriculum/index.ts"
273
+ },
274
+ "./video": {
275
+ "types": "./src/video/index.ts",
276
+ "default": "./src/video/index.ts"
277
+ },
278
+ "./progress": {
279
+ "types": "./src/progress/index.ts",
280
+ "default": "./src/progress/index.ts"
281
+ },
282
+ "./feedback": {
283
+ "types": "./src/feedback/index.ts",
284
+ "default": "./src/feedback/index.ts"
285
+ },
286
+ "./content": {
287
+ "types": "./src/content/index.ts",
288
+ "default": "./src/content/index.ts"
289
+ },
290
+ "./social": {
291
+ "types": "./src/social/index.ts",
292
+ "default": "./src/social/index.ts"
293
+ },
211
294
  "./styles.css": "./dist/components.css",
212
295
  "./package.json": "./package.json"
213
296
  }
214
297
  },
215
298
  "scripts": {
216
- "build": "tsc -b && vite build",
299
+ "build": "vite build",
217
300
  "dev": "vite build --watch",
218
301
  "generate-docs": "tsx scripts/generate-docs.ts",
219
302
  "check-docs": "tsx scripts/generate-docs.ts --check",
@@ -228,6 +311,11 @@
228
311
  "tailwindcss": "^4.0.0"
229
312
  },
230
313
  "dependencies": {
314
+ "@tiptap/extension-link": "^3.20.1",
315
+ "@tiptap/extension-placeholder": "^3.20.1",
316
+ "@tiptap/extension-underline": "^3.20.1",
317
+ "@tiptap/react": "^3.20.1",
318
+ "@tiptap/starter-kit": "^3.20.1",
231
319
  "class-variance-authority": "^0.7.1",
232
320
  "clsx": "^2.1.1",
233
321
  "lucide-react": "^0.475.0",
@@ -244,6 +332,7 @@
244
332
  "jsdom": "^28.1.0",
245
333
  "react": "^19.0.0",
246
334
  "react-docgen-typescript": "^2.3.0",
335
+ "react-dom": "^19.0.0",
247
336
  "tailwindcss": "^4.0.0",
248
337
  "tsx": "^4.19.0",
249
338
  "typescript": "~5.7.0",
@@ -2,6 +2,7 @@ import { ChevronLeft, ChevronRight, Send } from "lucide-react";
2
2
  import { TimerDisplay } from "./timer-display";
3
3
  import { QuestionNavigator } from "./question-navigator";
4
4
  import { Button } from "../ui/button";
5
+ import { cn } from "../lib/utils";
5
6
  import type { AssessmentToolbarProps } from "./types";
6
7
 
7
8
  export const AssessmentToolbar = ({
@@ -16,7 +17,6 @@ export const AssessmentToolbar = ({
16
17
  timeLimitSeconds,
17
18
  questions,
18
19
  onNavigateToQuestion,
19
- onToggleFlag,
20
20
  currentQuestionUid,
21
21
  isCompleted = false,
22
22
  isSubmitting = false,
@@ -27,70 +27,75 @@ export const AssessmentToolbar = ({
27
27
  const showNavigator = questions && questions.length > 0;
28
28
 
29
29
  return (
30
- <div className="rounded-lg border border-border overflow-hidden shadow-sm">
31
- {/* Header */}
32
- <div className="flex items-center justify-between px-4 py-2.5 bg-muted border-b border-border">
33
- <span className="text-sm font-medium text-foreground">
34
- Question{" "}
35
- <span className="font-bold">{currentQuestionIndex + 1}</span>
36
- {" "}of {totalQuestions}
37
- </span>
30
+ <div className="flex items-center gap-2 rounded-lg border border-border bg-muted px-2 py-1.5 shadow-sm">
31
+ {/* Previous */}
32
+ <Button
33
+ variant="ghost"
34
+ size="icon"
35
+ className="size-8 shrink-0 rounded-md"
36
+ disabled={!hasPrevious || readOnly}
37
+ onClick={onPrevious}
38
+ >
39
+ <ChevronLeft size={16} />
40
+ <span className="sr-only">Previous</span>
41
+ </Button>
38
42
 
39
- {showTimer && (
40
- <TimerDisplay
41
- timeElapsedSeconds={timeElapsedSeconds}
42
- timeLimitSeconds={timeLimitSeconds}
43
- />
44
- )}
45
- </div>
43
+ {/* Question indicator (shown when no navigator) */}
44
+ {!showNavigator && (
45
+ <span className="text-xs font-medium text-muted-foreground whitespace-nowrap">
46
+ <span className="font-bold text-foreground">{currentQuestionIndex + 1}</span>
47
+ <span className="mx-0.5">/</span>
48
+ {totalQuestions}
49
+ </span>
50
+ )}
46
51
 
47
- {/* Navigator */}
52
+ {/* Navigator chips */}
48
53
  {showNavigator && (
49
- <div className="px-4 py-3 border-b border-border">
54
+ <div className="min-w-0 flex-1 overflow-x-auto [scrollbar-width:none]">
50
55
  <QuestionNavigator
51
56
  questions={questions}
52
57
  currentQuestionUid={currentQuestionUid}
53
58
  onNavigate={onNavigateToQuestion}
54
- onToggleFlag={onToggleFlag}
55
59
  readOnly={readOnly}
56
60
  />
57
61
  </div>
58
62
  )}
59
63
 
60
- {/* Navigation buttons */}
61
- <div className="flex items-center justify-between gap-2 px-4 py-3">
64
+ {/* Spacer when no navigator */}
65
+ {!showNavigator && <div className="flex-1" />}
66
+
67
+ {/* Timer */}
68
+ {showTimer && (
69
+ <TimerDisplay
70
+ timeElapsedSeconds={timeElapsedSeconds}
71
+ timeLimitSeconds={timeLimitSeconds}
72
+ />
73
+ )}
74
+
75
+ {/* Next / Submit */}
76
+ {!hasNext || isCompleted ? (
62
77
  <Button
63
- variant="outline"
78
+ variant={isCompleted ? "secondary" : "default"}
64
79
  size="sm"
65
- className="rounded-lg"
66
- disabled={!hasPrevious || readOnly}
67
- onClick={onPrevious}
80
+ className={cn("shrink-0 rounded-md", isSubmitting && "gap-0")}
81
+ onClick={onSubmit}
82
+ disabled={readOnly || isSubmitting}
68
83
  >
69
- <ChevronLeft size={16} /> Previous
84
+ {isSubmitting ? "..." : isCompleted ? "Review" : "Submit"}
85
+ {!isSubmitting && <Send size={14} />}
70
86
  </Button>
71
-
72
- {!hasNext || isCompleted ? (
73
- <Button
74
- variant={isCompleted ? "secondary" : "default"}
75
- size="sm"
76
- className="rounded-lg"
77
- onClick={onSubmit}
78
- disabled={readOnly || isSubmitting}
79
- >
80
- {isSubmitting ? "Submitting..." : isCompleted ? "Review" : "Submit"}
81
- {!isSubmitting && <Send size={14} />}
82
- </Button>
83
- ) : (
84
- <Button
85
- size="sm"
86
- className="rounded-lg"
87
- disabled={!hasNext || readOnly}
88
- onClick={onNext}
89
- >
90
- Next <ChevronRight size={16} />
91
- </Button>
92
- )}
93
- </div>
87
+ ) : (
88
+ <Button
89
+ variant="ghost"
90
+ size="icon"
91
+ className="size-8 shrink-0 rounded-md"
92
+ disabled={!hasNext || readOnly}
93
+ onClick={onNext}
94
+ >
95
+ <ChevronRight size={16} />
96
+ <span className="sr-only">Next</span>
97
+ </Button>
98
+ )}
94
99
  </div>
95
100
  );
96
101
  };
@@ -1,10 +1,16 @@
1
1
  export { AssessmentToolbar } from "./assessment-toolbar";
2
2
  export { TimerDisplay } from "./timer-display";
3
3
  export { QuestionNavigator } from "./question-navigator";
4
+ export { QuestionHeaderBar } from "./question-header-bar";
5
+ export { QuestionMaterialsDrawer } from "./question-materials-drawer";
6
+ export { useCountdown } from "./use-countdown";
4
7
 
5
8
  export type {
6
9
  AssessmentToolbarProps,
7
10
  TimerDisplayProps,
8
11
  QuestionNavigatorProps,
9
12
  QuestionNavigatorItem,
13
+ QuestionHeaderBarProps,
14
+ QuestionMaterialsDrawerProps,
10
15
  } from "./types";
16
+ export type { UseCountdownOptions, UseCountdownReturn } from "./use-countdown";
@@ -0,0 +1,61 @@
1
+ import { BookOpen, Flag } from "lucide-react";
2
+ import { Button } from "../ui/button";
3
+ import { cn } from "../lib/utils";
4
+ import type { QuestionHeaderBarProps } from "./types";
5
+
6
+ export const QuestionHeaderBar = ({
7
+ questionNumber,
8
+ totalQuestions,
9
+ isFlagged,
10
+ onToggleFlag,
11
+ hasMaterials = false,
12
+ onOpenMaterials,
13
+ readOnly = false,
14
+ }: QuestionHeaderBarProps) => {
15
+ return (
16
+ <div className="flex items-center justify-between pb-3 border-b border-border">
17
+ <span className="text-sm font-semibold text-foreground">
18
+ Question {questionNumber}{" "}
19
+ <span className="font-normal text-muted-foreground">
20
+ of {totalQuestions}
21
+ </span>
22
+ </span>
23
+
24
+ {!readOnly && (
25
+ <div className="flex items-center gap-1">
26
+ {hasMaterials && onOpenMaterials && (
27
+ <Button
28
+ variant="ghost"
29
+ size="sm"
30
+ className="gap-1.5 text-muted-foreground hover:text-foreground"
31
+ onClick={onOpenMaterials}
32
+ >
33
+ <BookOpen size={14} />
34
+ Related Material
35
+ </Button>
36
+ )}
37
+
38
+ {onToggleFlag && (
39
+ <Button
40
+ variant="ghost"
41
+ size="icon"
42
+ className={cn(
43
+ "size-8",
44
+ isFlagged
45
+ ? "text-warning hover:text-warning/80"
46
+ : "text-muted-foreground hover:text-warning",
47
+ )}
48
+ onClick={onToggleFlag}
49
+ aria-label={isFlagged ? "Unflag question" : "Flag question"}
50
+ >
51
+ <Flag
52
+ size={14}
53
+ fill={isFlagged ? "currentColor" : "none"}
54
+ />
55
+ </Button>
56
+ )}
57
+ </div>
58
+ )}
59
+ </div>
60
+ );
61
+ };