@hydralms/components 0.1.3 → 0.3.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 (344) hide show
  1. package/dist/StudentProfile-BVfZMbnV.cjs +1 -0
  2. package/dist/StudentProfile-DeMxdrL3.js +3275 -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/timer-display.d.ts +1 -1
  9. package/dist/assessment-toolbar/types.d.ts +52 -4
  10. package/dist/assessment-toolbar/use-countdown.d.ts +43 -0
  11. package/dist/common/index.d.ts +3 -1
  12. package/dist/common/pagination.d.ts +26 -0
  13. package/dist/common/stepper.d.ts +6 -0
  14. package/dist/common/types.d.ts +38 -0
  15. package/dist/components.css +1 -1
  16. package/dist/content/attachment-list.d.ts +6 -0
  17. package/dist/content/audio-player.d.ts +22 -0
  18. package/dist/content/code-block.d.ts +30 -0
  19. package/dist/content/content-block.d.ts +1 -1
  20. package/dist/content/embed-block.d.ts +28 -0
  21. package/dist/content/index.d.ts +8 -1
  22. package/dist/content/types.d.ts +63 -0
  23. package/dist/curriculum/course-card.d.ts +51 -0
  24. package/dist/curriculum/curriculum-item.d.ts +1 -1
  25. package/dist/curriculum/index.d.ts +2 -0
  26. package/dist/curriculum/types.d.ts +2 -2
  27. package/dist/index.cjs +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +597 -308
  30. package/dist/license/HydraContext.d.ts +16 -0
  31. package/dist/license/ProBadge.d.ts +6 -0
  32. package/dist/license/index.d.ts +7 -0
  33. package/dist/license/tiers.d.ts +3 -0
  34. package/dist/license/useHydraLicense.d.ts +6 -0
  35. package/dist/license/validate.d.ts +13 -0
  36. package/dist/license/withProGate.d.ts +6 -0
  37. package/dist/modules/AssignmentModule/AssignmentModule.d.ts +5 -0
  38. package/dist/modules/AssignmentModule/types.d.ts +69 -0
  39. package/dist/modules/CertificateModule/CertificateModule.d.ts +5 -0
  40. package/dist/modules/CertificateModule/types.d.ts +51 -0
  41. package/dist/modules/CourseCatalogModule/CourseCatalogModule.d.ts +5 -0
  42. package/dist/modules/CourseCatalogModule/types.d.ts +43 -0
  43. package/dist/modules/CoursePlayer/CoursePlayer.d.ts +4 -1
  44. package/dist/modules/DiscussionModule/DiscussionModule.d.ts +5 -0
  45. package/dist/modules/DiscussionModule/types.d.ts +47 -0
  46. package/dist/modules/ExamModule/ExamModule.d.ts +5 -0
  47. package/dist/modules/ExamModule/types.d.ts +55 -0
  48. package/dist/modules/FlashcardLab/FlashcardLab.d.ts +4 -1
  49. package/dist/modules/FlashcardLab/types.d.ts +2 -0
  50. package/dist/modules/GradeCenterModule/GradeCenterModule.d.ts +5 -0
  51. package/dist/modules/GradeCenterModule/types.d.ts +56 -0
  52. package/dist/modules/QuizModule/QuizModule.d.ts +4 -1
  53. package/dist/modules/QuizModule/types.d.ts +10 -14
  54. package/dist/modules/StudentDashboardModule/StudentDashboardModule.d.ts +5 -0
  55. package/dist/modules/StudentDashboardModule/types.d.ts +54 -0
  56. package/dist/modules/StudentProfileModule/StudentProfileModule.d.ts +5 -0
  57. package/dist/modules/StudentProfileModule/types.d.ts +43 -0
  58. package/dist/modules/SurveyModule/SurveyModule.d.ts +5 -0
  59. package/dist/modules/SurveyModule/types.d.ts +51 -0
  60. package/dist/modules/_shared/assessment-intro.d.ts +16 -0
  61. package/dist/modules/_shared/assessment-results.d.ts +23 -0
  62. package/dist/modules/_shared/types.d.ts +10 -0
  63. package/dist/modules/_shared/use-timer.d.ts +9 -0
  64. package/dist/modules/index.d.ts +18 -0
  65. package/dist/modules.cjs +1 -0
  66. package/dist/modules.js +1834 -0
  67. package/dist/progress/achievement-badge.d.ts +6 -0
  68. package/dist/progress/activity-timeline.d.ts +6 -0
  69. package/dist/progress/index.d.ts +4 -1
  70. package/dist/progress/stat-card.d.ts +1 -1
  71. package/dist/progress/streak-badge.d.ts +6 -0
  72. package/dist/progress/types.d.ts +99 -0
  73. package/dist/provider/HydraProvider.d.ts +5 -1
  74. package/dist/questions/choice.d.ts +1 -1
  75. package/dist/questions/confidence-indicator.d.ts +37 -0
  76. package/dist/questions/essay.d.ts +2 -2
  77. package/dist/questions/fill-in-the-blank.d.ts +1 -1
  78. package/dist/questions/hotspot.d.ts +21 -0
  79. package/dist/questions/index.d.ts +11 -1
  80. package/dist/questions/inline-choice.d.ts +21 -0
  81. package/dist/questions/matching.d.ts +22 -0
  82. package/dist/questions/multiple-choice.d.ts +1 -1
  83. package/dist/questions/numeric.d.ts +11 -0
  84. package/dist/questions/ordering.d.ts +12 -0
  85. package/dist/questions/question-renderer.d.ts +1 -1
  86. package/dist/questions/scenario.d.ts +23 -0
  87. package/dist/questions/scoring.d.ts +22 -0
  88. package/dist/questions/spreadsheet.d.ts +29 -0
  89. package/dist/questions/true-false.d.ts +1 -1
  90. package/dist/questions/types.d.ts +106 -1
  91. package/dist/questions/use-drag-reorder.d.ts +17 -0
  92. package/dist/sections/AnnouncementFeed/AnnouncementFeed.d.ts +1 -1
  93. package/dist/sections/AnnouncementFeed/types.d.ts +15 -1
  94. package/dist/sections/AssessmentReview/AssessmentReview.d.ts +1 -1
  95. package/dist/sections/AssessmentReview/types.d.ts +6 -0
  96. package/dist/sections/AssignmentSubmission/AssignmentSubmission.d.ts +1 -1
  97. package/dist/sections/AssignmentSubmission/types.d.ts +6 -0
  98. package/dist/sections/CertificateViewer/CertificateViewer.d.ts +1 -1
  99. package/dist/sections/CertificateViewer/certificate-variants.d.ts +42 -0
  100. package/dist/sections/CertificateViewer/types.d.ts +13 -5
  101. package/dist/sections/CourseCatalog/CourseCatalog.d.ts +2 -0
  102. package/dist/sections/CourseCatalog/types.d.ts +80 -0
  103. package/dist/sections/CourseOutline/CourseOutline.d.ts +1 -1
  104. package/dist/sections/CourseOutline/types.d.ts +6 -0
  105. package/dist/sections/DiscussionThread/DiscussionThread.d.ts +1 -1
  106. package/dist/sections/DiscussionThread/types.d.ts +6 -0
  107. package/dist/sections/EnrollmentWizard/EnrollmentWizard.d.ts +2 -0
  108. package/dist/sections/EnrollmentWizard/types.d.ts +66 -0
  109. package/dist/sections/ExamSession/ExamSession.d.ts +1 -1
  110. package/dist/sections/ExamSession/types.d.ts +12 -1
  111. package/dist/sections/FlashcardStudySession/FlashcardStudySession.d.ts +1 -1
  112. package/dist/sections/FlashcardStudySession/types.d.ts +6 -0
  113. package/dist/sections/ForumBoard/ForumBoard.d.ts +8 -0
  114. package/dist/sections/ForumBoard/types.d.ts +78 -0
  115. package/dist/sections/GradebookTable/GradebookTable.d.ts +1 -1
  116. package/dist/sections/GradebookTable/types.d.ts +14 -0
  117. package/dist/sections/LecturePlayer/LecturePlayer.d.ts +1 -1
  118. package/dist/sections/LecturePlayer/types.d.ts +8 -0
  119. package/dist/sections/LessonPage/LessonPage.d.ts +1 -1
  120. package/dist/sections/LessonPage/types.d.ts +6 -0
  121. package/dist/sections/PracticeQuiz/PracticeQuiz.d.ts +1 -1
  122. package/dist/sections/PracticeQuiz/types.d.ts +6 -0
  123. package/dist/sections/ProgressDashboard/ProgressDashboard.d.ts +1 -1
  124. package/dist/sections/ProgressDashboard/types.d.ts +6 -0
  125. package/dist/sections/QuizSession/QuizSession.d.ts +1 -1
  126. package/dist/sections/QuizSession/types.d.ts +12 -1
  127. package/dist/sections/RequirementsChecklist/RequirementsChecklist.d.ts +8 -0
  128. package/dist/sections/RequirementsChecklist/types.d.ts +43 -0
  129. package/dist/sections/ResourceLibrary/ResourceLibrary.d.ts +1 -1
  130. package/dist/sections/ResourceLibrary/types.d.ts +15 -1
  131. package/dist/sections/RubricView/RubricView.d.ts +9 -0
  132. package/dist/sections/RubricView/types.d.ts +56 -0
  133. package/dist/sections/ScrollableQuiz/ScrollableQuiz.d.ts +1 -1
  134. package/dist/sections/ScrollableQuiz/types.d.ts +6 -0
  135. package/dist/sections/StudentProfile/StudentProfile.d.ts +2 -0
  136. package/dist/sections/StudentProfile/types.d.ts +98 -0
  137. package/dist/sections/SurveyForm/SurveyForm.d.ts +1 -1
  138. package/dist/sections/SurveyForm/types.d.ts +6 -0
  139. package/dist/sections/_shared/merge-answers.d.ts +9 -0
  140. package/dist/sections/_shared/section-shell.d.ts +20 -0
  141. package/dist/sections/_shared/use-assessment-session.d.ts +30 -0
  142. package/dist/sections/index.d.ts +13 -1
  143. package/dist/sections.cjs +1 -1
  144. package/dist/sections.js +282 -1786
  145. package/dist/social/post-card.d.ts +1 -1
  146. package/dist/tabs-BsfVo2Bl.cjs +173 -0
  147. package/dist/tabs-BuY1iNJE.js +22305 -0
  148. package/dist/ui/alert.d.ts +1 -1
  149. package/dist/ui/badge.d.ts +1 -1
  150. package/dist/ui/button.d.ts +1 -1
  151. package/dist/ui/drawer.d.ts +84 -0
  152. package/dist/ui/index.d.ts +5 -0
  153. package/dist/ui/progress.d.ts +1 -1
  154. package/dist/ui/rich-text-editor.d.ts +32 -0
  155. package/dist/ui/rich-text-toolbar.d.ts +8 -0
  156. package/dist/ui/toast.d.ts +43 -0
  157. package/dist/utils/array-utils.d.ts +4 -0
  158. package/dist/utils/debounce.d.ts +5 -1
  159. package/dist/utils/flatten-leaves.d.ts +6 -0
  160. package/dist/utils/format-file-size.d.ts +1 -0
  161. package/dist/utils/format-timestamp.d.ts +1 -0
  162. package/dist/utils/is-empty-html.d.ts +5 -0
  163. package/dist/utils/pick-palette-color.d.ts +19 -0
  164. package/dist/utils/shuffle.d.ts +1 -0
  165. package/dist/utils/string-utils.d.ts +12 -0
  166. package/dist/video/types.d.ts +15 -0
  167. package/dist/video/video-bookmark.d.ts +1 -1
  168. package/dist/video/video-player.d.ts +1 -1
  169. package/dist/video/video-playlist-item.d.ts +1 -1
  170. package/dist/withProGate-BWqcKdPM.js +137 -0
  171. package/dist/withProGate-DX6XqKLp.cjs +1 -0
  172. package/package.json +40 -137
  173. package/src/assessment-toolbar/assessment-toolbar.tsx +54 -49
  174. package/src/assessment-toolbar/index.ts +6 -0
  175. package/src/assessment-toolbar/question-header-bar.tsx +61 -0
  176. package/src/assessment-toolbar/question-materials-drawer.tsx +55 -0
  177. package/src/assessment-toolbar/question-navigator.tsx +13 -36
  178. package/src/assessment-toolbar/timer-display.tsx +6 -5
  179. package/src/assessment-toolbar/types.ts +54 -4
  180. package/src/assessment-toolbar/use-countdown.ts +153 -0
  181. package/src/common/empty-state.tsx +1 -0
  182. package/src/common/index.ts +5 -0
  183. package/src/common/pagination.tsx +135 -0
  184. package/src/common/search-input.tsx +8 -6
  185. package/src/common/stepper.tsx +100 -0
  186. package/src/common/types.ts +41 -0
  187. package/src/content/attachment-list.tsx +92 -0
  188. package/src/content/audio-player.tsx +196 -0
  189. package/src/content/code-block.tsx +113 -0
  190. package/src/content/content-block.tsx +68 -2
  191. package/src/content/embed-block.tsx +78 -0
  192. package/src/content/file-upload-zone.tsx +11 -6
  193. package/src/content/index.ts +9 -0
  194. package/src/content/types.ts +46 -0
  195. package/src/curriculum/course-card.tsx +199 -0
  196. package/src/curriculum/curriculum-item.tsx +9 -5
  197. package/src/curriculum/curriculum-tree.tsx +20 -13
  198. package/src/curriculum/index.ts +2 -0
  199. package/src/curriculum/types.ts +2 -2
  200. package/src/feedback/feedback-banner.tsx +12 -14
  201. package/src/flashcards/flashcard-deck.tsx +1 -9
  202. package/src/flashcards/flashcard.tsx +29 -9
  203. package/src/index.ts +3 -0
  204. package/src/license/HydraContext.tsx +62 -0
  205. package/src/license/ProBadge.tsx +43 -0
  206. package/src/license/index.ts +7 -0
  207. package/src/license/tiers.ts +24 -0
  208. package/src/license/useHydraLicense.ts +10 -0
  209. package/src/license/validate.ts +90 -0
  210. package/src/license/withProGate.tsx +21 -0
  211. package/src/modules/AssignmentModule/AssignmentModule.tsx +314 -0
  212. package/src/modules/AssignmentModule/types.ts +77 -0
  213. package/src/modules/CertificateModule/CertificateModule.tsx +173 -0
  214. package/src/modules/CertificateModule/types.ts +49 -0
  215. package/src/modules/CourseCatalogModule/CourseCatalogModule.tsx +126 -0
  216. package/src/modules/CourseCatalogModule/types.ts +47 -0
  217. package/src/modules/CoursePlayer/CoursePlayer.tsx +80 -69
  218. package/src/modules/DiscussionModule/DiscussionModule.tsx +145 -0
  219. package/src/modules/DiscussionModule/types.ts +54 -0
  220. package/src/modules/ExamModule/ExamModule.tsx +151 -0
  221. package/src/modules/ExamModule/types.ts +57 -0
  222. package/src/modules/FlashcardLab/FlashcardLab.tsx +39 -21
  223. package/src/modules/FlashcardLab/types.ts +2 -0
  224. package/src/modules/GradeCenterModule/GradeCenterModule.tsx +174 -0
  225. package/src/modules/GradeCenterModule/types.ts +65 -0
  226. package/src/modules/QuizModule/QuizModule.tsx +58 -178
  227. package/src/modules/QuizModule/types.ts +10 -15
  228. package/src/modules/StudentDashboardModule/StudentDashboardModule.tsx +117 -0
  229. package/src/modules/StudentDashboardModule/types.ts +56 -0
  230. package/src/modules/StudentProfileModule/StudentProfileModule.tsx +289 -0
  231. package/src/modules/StudentProfileModule/types.ts +45 -0
  232. package/src/modules/SurveyModule/SurveyModule.tsx +185 -0
  233. package/src/modules/SurveyModule/types.ts +53 -0
  234. package/src/modules/_shared/assessment-intro.tsx +75 -0
  235. package/src/modules/_shared/assessment-results.tsx +133 -0
  236. package/src/modules/_shared/types.ts +11 -0
  237. package/src/modules/_shared/use-timer.ts +49 -0
  238. package/src/modules/index.ts +33 -0
  239. package/src/progress/achievement-badge.tsx +52 -0
  240. package/src/progress/activity-timeline.tsx +84 -0
  241. package/src/progress/grade-indicator.tsx +9 -1
  242. package/src/progress/index.ts +7 -0
  243. package/src/progress/progress-ring.tsx +2 -1
  244. package/src/progress/stat-card.tsx +37 -18
  245. package/src/progress/streak-badge.tsx +35 -0
  246. package/src/progress/types.ts +103 -0
  247. package/src/provider/HydraProvider.tsx +15 -6
  248. package/src/questions/choice.tsx +19 -14
  249. package/src/questions/confidence-indicator.tsx +107 -0
  250. package/src/questions/essay.tsx +28 -28
  251. package/src/questions/fill-in-the-blank.tsx +20 -19
  252. package/src/questions/hotspot.tsx +154 -0
  253. package/src/questions/index.ts +18 -0
  254. package/src/questions/inline-choice.tsx +152 -0
  255. package/src/questions/matching.tsx +229 -0
  256. package/src/questions/multiple-choice.tsx +19 -14
  257. package/src/questions/numeric.tsx +106 -0
  258. package/src/questions/ordering.tsx +167 -0
  259. package/src/questions/question-renderer.tsx +24 -2
  260. package/src/questions/scenario.tsx +140 -0
  261. package/src/questions/scoring.ts +201 -0
  262. package/src/questions/spreadsheet.tsx +260 -0
  263. package/src/questions/true-false.tsx +19 -14
  264. package/src/questions/types.ts +123 -1
  265. package/src/questions/use-drag-reorder.ts +80 -0
  266. package/src/sections/AnnouncementFeed/AnnouncementFeed.tsx +66 -23
  267. package/src/sections/AnnouncementFeed/types.ts +15 -1
  268. package/src/sections/AssessmentReview/AssessmentReview.tsx +50 -2
  269. package/src/sections/AssessmentReview/types.ts +6 -0
  270. package/src/sections/AssignmentSubmission/AssignmentSubmission.tsx +44 -6
  271. package/src/sections/AssignmentSubmission/types.ts +6 -0
  272. package/src/sections/CertificateViewer/CertificateViewer.tsx +215 -60
  273. package/src/sections/CertificateViewer/certificate-variants.tsx +170 -0
  274. package/src/sections/CertificateViewer/types.ts +19 -5
  275. package/src/sections/CourseCatalog/CourseCatalog.tsx +220 -0
  276. package/src/sections/CourseCatalog/types.ts +76 -0
  277. package/src/sections/CourseOutline/CourseOutline.tsx +45 -14
  278. package/src/sections/CourseOutline/types.ts +6 -0
  279. package/src/sections/DiscussionThread/DiscussionThread.tsx +55 -11
  280. package/src/sections/DiscussionThread/types.ts +6 -0
  281. package/src/sections/EnrollmentWizard/EnrollmentWizard.tsx +343 -0
  282. package/src/sections/EnrollmentWizard/types.ts +65 -0
  283. package/src/sections/ExamSession/ExamSession.tsx +125 -82
  284. package/src/sections/ExamSession/types.ts +12 -1
  285. package/src/sections/FlashcardStudySession/FlashcardStudySession.tsx +53 -36
  286. package/src/sections/FlashcardStudySession/types.ts +6 -0
  287. package/src/sections/ForumBoard/ForumBoard.tsx +342 -0
  288. package/src/sections/ForumBoard/types.ts +81 -0
  289. package/src/sections/GradebookTable/GradebookTable.tsx +55 -2
  290. package/src/sections/GradebookTable/types.ts +14 -0
  291. package/src/sections/LecturePlayer/LecturePlayer.tsx +63 -37
  292. package/src/sections/LecturePlayer/types.ts +8 -0
  293. package/src/sections/LessonPage/LessonPage.tsx +40 -13
  294. package/src/sections/LessonPage/types.ts +6 -0
  295. package/src/sections/PracticeQuiz/PracticeQuiz.tsx +119 -98
  296. package/src/sections/PracticeQuiz/types.ts +6 -0
  297. package/src/sections/ProgressDashboard/ProgressDashboard.tsx +121 -67
  298. package/src/sections/ProgressDashboard/types.ts +6 -0
  299. package/src/sections/QuizSession/QuizSession.tsx +115 -67
  300. package/src/sections/QuizSession/types.ts +12 -1
  301. package/src/sections/RequirementsChecklist/RequirementsChecklist.tsx +147 -0
  302. package/src/sections/RequirementsChecklist/types.ts +44 -0
  303. package/src/sections/ResourceLibrary/ResourceLibrary.tsx +68 -17
  304. package/src/sections/ResourceLibrary/types.ts +15 -1
  305. package/src/sections/RubricView/RubricView.tsx +174 -0
  306. package/src/sections/RubricView/types.ts +58 -0
  307. package/src/sections/ScrollableQuiz/ScrollableQuiz.tsx +58 -23
  308. package/src/sections/ScrollableQuiz/types.ts +6 -0
  309. package/src/sections/StudentProfile/StudentProfile.tsx +279 -0
  310. package/src/sections/StudentProfile/types.ts +99 -0
  311. package/src/sections/SurveyForm/SurveyForm.tsx +40 -10
  312. package/src/sections/SurveyForm/types.ts +6 -0
  313. package/src/sections/_shared/merge-answers.ts +22 -0
  314. package/src/sections/_shared/section-shell.tsx +64 -0
  315. package/src/sections/_shared/use-assessment-session.ts +125 -0
  316. package/src/sections/index.ts +42 -1
  317. package/src/social/post-card.tsx +8 -19
  318. package/src/social/user-avatar.tsx +10 -5
  319. package/src/styles/globals.css +52 -41
  320. package/src/ui/badge.tsx +8 -0
  321. package/src/ui/drawer.tsx +600 -0
  322. package/src/ui/index.ts +21 -0
  323. package/src/ui/progress.tsx +4 -0
  324. package/src/ui/rich-text-editor.tsx +119 -0
  325. package/src/ui/rich-text-toolbar.tsx +157 -0
  326. package/src/ui/toast.tsx +170 -0
  327. package/src/utils/array-utils.ts +17 -0
  328. package/src/utils/debounce.ts +8 -2
  329. package/src/utils/flatten-leaves.ts +17 -0
  330. package/src/utils/format-file-size.ts +5 -0
  331. package/src/utils/format-timestamp.ts +13 -0
  332. package/src/utils/is-empty-html.ts +7 -0
  333. package/src/utils/pick-palette-color.ts +33 -0
  334. package/src/utils/shuffle.ts +8 -0
  335. package/src/utils/string-utils.ts +30 -0
  336. package/src/video/types.ts +16 -0
  337. package/src/video/video-bookmark.tsx +4 -3
  338. package/src/video/video-chapter-list.tsx +9 -4
  339. package/src/video/video-player.tsx +24 -5
  340. package/src/video/video-playlist-item.tsx +8 -3
  341. package/src/video/video-thumbnail-card.tsx +4 -0
  342. package/src/video/video-transcript.tsx +8 -5
  343. package/dist/table-BrS5cDQu.js +0 -2510
  344. package/dist/table-D6AkBBEo.cjs +0 -1
@@ -0,0 +1,600 @@
1
+ import * as React from "react";
2
+ import {
3
+ createContext,
4
+ useContext,
5
+ useEffect,
6
+ useRef,
7
+ useState,
8
+ useId,
9
+ useCallback,
10
+ } from "react";
11
+ import { createPortal } from "react-dom";
12
+ import { cva } from "class-variance-authority";
13
+ import { X } from "lucide-react";
14
+
15
+ import { cn } from "../lib/utils";
16
+ import { buttonVariants } from "./button";
17
+ import { Tooltip, TooltipTrigger, TooltipContent } from "./tooltip";
18
+
19
+ /* --------------------------------- Context -------------------------------- */
20
+
21
+ interface DrawerContextValue {
22
+ open: boolean;
23
+ onOpenChange: (open: boolean) => void;
24
+ side: "left" | "right";
25
+ titleId: string;
26
+ descriptionId: string;
27
+ hasNav: boolean;
28
+ setHasNav: (v: boolean) => void;
29
+ }
30
+
31
+ /* ------------------------------ Nav Context ------------------------------- */
32
+
33
+ interface DrawerNavContextValue {
34
+ value: string;
35
+ onValueChange: (value: string) => void;
36
+ }
37
+
38
+ const DrawerNavContext = createContext<DrawerNavContextValue | null>(null);
39
+
40
+ function useDrawerNavContext() {
41
+ const ctx = useContext(DrawerNavContext);
42
+ if (!ctx)
43
+ throw new Error(
44
+ "DrawerNavItem must be used within <DrawerNav>",
45
+ );
46
+ return ctx;
47
+ }
48
+
49
+ const DrawerContext = createContext<DrawerContextValue | null>(null);
50
+
51
+ function useDrawerContext() {
52
+ const ctx = useContext(DrawerContext);
53
+ if (!ctx)
54
+ throw new Error("Drawer compound components must be used within <Drawer>");
55
+ return ctx;
56
+ }
57
+
58
+ /* ------------------------------- Focus Trap ------------------------------- */
59
+
60
+ const FOCUSABLE_SELECTOR = [
61
+ "a[href]",
62
+ "button:not([disabled])",
63
+ "input:not([disabled])",
64
+ "select:not([disabled])",
65
+ "textarea:not([disabled])",
66
+ '[tabindex]:not([tabindex="-1"])',
67
+ ].join(", ");
68
+
69
+ function useFocusTrap(
70
+ containerRef: React.RefObject<HTMLElement | null>,
71
+ active: boolean,
72
+ ) {
73
+ const previousFocusRef = useRef<Element | null>(null);
74
+
75
+ useEffect(() => {
76
+ if (!active || !containerRef.current) return;
77
+
78
+ previousFocusRef.current = document.activeElement;
79
+
80
+ const focusable =
81
+ containerRef.current.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR);
82
+ if (focusable.length > 0) {
83
+ focusable[0].focus();
84
+ }
85
+
86
+ return () => {
87
+ if (previousFocusRef.current instanceof HTMLElement) {
88
+ previousFocusRef.current.focus();
89
+ }
90
+ };
91
+ }, [active, containerRef]);
92
+
93
+ const handleKeyDown = useCallback(
94
+ (e: React.KeyboardEvent) => {
95
+ if (e.key !== "Tab" || !containerRef.current) return;
96
+
97
+ const focusable = Array.from(
98
+ containerRef.current.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),
99
+ );
100
+ if (focusable.length === 0) return;
101
+
102
+ const first = focusable[0];
103
+ const last = focusable[focusable.length - 1];
104
+
105
+ if (e.shiftKey && document.activeElement === first) {
106
+ e.preventDefault();
107
+ last.focus();
108
+ } else if (!e.shiftKey && document.activeElement === last) {
109
+ e.preventDefault();
110
+ first.focus();
111
+ }
112
+ },
113
+ [containerRef],
114
+ );
115
+
116
+ return handleKeyDown;
117
+ }
118
+
119
+ /* ------------------------------- Scroll Lock ------------------------------ */
120
+
121
+ function useScrollLock(active: boolean) {
122
+ useEffect(() => {
123
+ if (!active) return;
124
+
125
+ const original = document.body.style.overflow;
126
+ document.body.style.overflow = "hidden";
127
+
128
+ return () => {
129
+ document.body.style.overflow = original;
130
+ };
131
+ }, [active]);
132
+ }
133
+
134
+ /* ------------------------------ Variants --------------------------------- */
135
+
136
+ const drawerContentVariants = cva(
137
+ "bg-background fixed inset-y-0 z-50 flex flex-col shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out",
138
+ {
139
+ variants: {
140
+ side: {
141
+ right:
142
+ "right-0 border-l data-[state=open]:slide-in-from-right data-[state=closed]:slide-out-to-right",
143
+ left: "left-0 border-r data-[state=open]:slide-in-from-left data-[state=closed]:slide-out-to-left",
144
+ },
145
+ size: {
146
+ sm: "w-80",
147
+ default: "w-100",
148
+ lg: "w-128",
149
+ xl: "w-160",
150
+ full: "w-full",
151
+ },
152
+ },
153
+ defaultVariants: {
154
+ side: "right",
155
+ size: "default",
156
+ },
157
+ },
158
+ );
159
+
160
+ /* -------------------------------- Drawer --------------------------------- */
161
+
162
+ /**
163
+ * A slide-in side panel for contextual content like navigation, settings, or detail views.
164
+ *
165
+ * @example
166
+ * ```tsx
167
+ * <Drawer open={open} onOpenChange={setOpen} side="right">
168
+ * <DrawerContent>
169
+ * <DrawerHeader><DrawerTitle>Panel</DrawerTitle></DrawerHeader>
170
+ * <DrawerBody>Content</DrawerBody>
171
+ * </DrawerContent>
172
+ * </Drawer>
173
+ * ```
174
+ */
175
+ interface DrawerProps {
176
+ /** Compound component children (DrawerContent, DrawerTrigger, etc.) */
177
+ children: React.ReactNode;
178
+ /** Whether the drawer is open. */
179
+ open: boolean;
180
+ /** Callback fired when the drawer should open or close. */
181
+ onOpenChange: (open: boolean) => void;
182
+ /** Which edge of the viewport the drawer slides in from. */
183
+ side?: "left" | "right";
184
+ }
185
+
186
+ function Drawer({ children, open, onOpenChange, side = "right" }: DrawerProps) {
187
+ const titleId = useId();
188
+ const descriptionId = useId();
189
+ const [hasNav, setHasNav] = useState(false);
190
+
191
+ return (
192
+ <DrawerContext.Provider
193
+ value={{ open, onOpenChange, side, titleId, descriptionId, hasNav, setHasNav }}
194
+ >
195
+ {children}
196
+ </DrawerContext.Provider>
197
+ );
198
+ }
199
+
200
+ /* ------------------------------ DrawerTrigger ----------------------------- */
201
+
202
+ function DrawerTrigger({
203
+ className,
204
+ onClick,
205
+ ...props
206
+ }: React.ComponentProps<"button">) {
207
+ const { onOpenChange } = useDrawerContext();
208
+
209
+ return (
210
+ <button
211
+ type="button"
212
+ data-slot="drawer-trigger"
213
+ className={className}
214
+ onClick={(e) => {
215
+ onOpenChange(true);
216
+ onClick?.(e);
217
+ }}
218
+ {...props}
219
+ />
220
+ );
221
+ }
222
+
223
+ /* ------------------------------- DrawerPortal ----------------------------- */
224
+
225
+ function DrawerPortal({ children }: { children: React.ReactNode }) {
226
+ return createPortal(children, document.body);
227
+ }
228
+
229
+ /* ----------------------------- DrawerBackdrop ----------------------------- */
230
+
231
+ function DrawerBackdrop({
232
+ className,
233
+ ...props
234
+ }: React.ComponentProps<"div">) {
235
+ const { onOpenChange } = useDrawerContext();
236
+
237
+ return (
238
+ <div
239
+ data-slot="drawer-backdrop"
240
+ className={cn(
241
+ "fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
242
+ className,
243
+ )}
244
+ data-state="open"
245
+ onClick={() => onOpenChange(false)}
246
+ {...props}
247
+ />
248
+ );
249
+ }
250
+
251
+ /* ----------------------------- DrawerContent ------------------------------ */
252
+
253
+ interface DrawerContentProps
254
+ extends Omit<React.ComponentProps<"div">, "size"> {
255
+ /** Width preset for the drawer panel. */
256
+ size?: "sm" | "default" | "lg" | "xl" | "full";
257
+ /** Whether to lock body scroll when the drawer is open. */
258
+ scrollLock?: boolean;
259
+ }
260
+
261
+ function DrawerContent({
262
+ className,
263
+ children,
264
+ size = "default",
265
+ scrollLock: scrollLockProp = true,
266
+ ...props
267
+ }: DrawerContentProps) {
268
+ const { open, onOpenChange, side, titleId, descriptionId, hasNav } =
269
+ useDrawerContext();
270
+ const contentRef = useRef<HTMLDivElement>(null);
271
+
272
+ const handleFocusTrap = useFocusTrap(contentRef, open);
273
+ useScrollLock(open && scrollLockProp);
274
+
275
+ useEffect(() => {
276
+ if (!open) return;
277
+ const handler = (e: KeyboardEvent) => {
278
+ if (e.key === "Escape") onOpenChange(false);
279
+ };
280
+ document.addEventListener("keydown", handler);
281
+ return () => document.removeEventListener("keydown", handler);
282
+ }, [open, onOpenChange]);
283
+
284
+ if (!open) return null;
285
+
286
+ return (
287
+ <DrawerPortal>
288
+ <DrawerBackdrop />
289
+ <div
290
+ ref={contentRef}
291
+ role="dialog"
292
+ aria-modal="true"
293
+ aria-labelledby={titleId}
294
+ aria-describedby={descriptionId}
295
+ data-slot="drawer-content"
296
+ data-state="open"
297
+ className={cn(drawerContentVariants({ side, size }), hasNav && "flex-row", className)}
298
+ onKeyDown={handleFocusTrap}
299
+ {...props}
300
+ >
301
+ {children}
302
+ </div>
303
+ </DrawerPortal>
304
+ );
305
+ }
306
+
307
+ /* -------------------------------- Layout --------------------------------- */
308
+
309
+ function DrawerHeader({
310
+ className,
311
+ ...props
312
+ }: React.ComponentProps<"div">) {
313
+ return (
314
+ <div
315
+ data-slot="drawer-header"
316
+ className={cn("flex flex-col gap-1.5 border-b px-6 py-4 shrink-0", className)}
317
+ {...props}
318
+ />
319
+ );
320
+ }
321
+
322
+ function DrawerBody({
323
+ className,
324
+ ...props
325
+ }: React.ComponentProps<"div">) {
326
+ return (
327
+ <div
328
+ data-slot="drawer-body"
329
+ className={cn("flex-1 overflow-y-auto px-6 py-4", className)}
330
+ {...props}
331
+ />
332
+ );
333
+ }
334
+
335
+ function DrawerFooter({
336
+ className,
337
+ ...props
338
+ }: React.ComponentProps<"div">) {
339
+ return (
340
+ <div
341
+ data-slot="drawer-footer"
342
+ className={cn(
343
+ "flex items-center gap-2 border-t px-6 py-4 shrink-0",
344
+ className,
345
+ )}
346
+ {...props}
347
+ />
348
+ );
349
+ }
350
+
351
+ /* ------------------------------- Title/Desc ------------------------------ */
352
+
353
+ function DrawerTitle({
354
+ className,
355
+ ...props
356
+ }: React.ComponentProps<"h2">) {
357
+ const { titleId } = useDrawerContext();
358
+
359
+ return (
360
+ <h2
361
+ id={titleId}
362
+ data-slot="drawer-title"
363
+ className={cn("text-lg font-semibold", className)}
364
+ {...props}
365
+ />
366
+ );
367
+ }
368
+
369
+ function DrawerDescription({
370
+ className,
371
+ ...props
372
+ }: React.ComponentProps<"p">) {
373
+ const { descriptionId } = useDrawerContext();
374
+
375
+ return (
376
+ <p
377
+ id={descriptionId}
378
+ data-slot="drawer-description"
379
+ className={cn("text-muted-foreground text-sm", className)}
380
+ {...props}
381
+ />
382
+ );
383
+ }
384
+
385
+ /* -------------------------------- Close ---------------------------------- */
386
+
387
+ function DrawerClose({
388
+ className,
389
+ children,
390
+ onClick,
391
+ ...props
392
+ }: React.ComponentProps<"button">) {
393
+ const { onOpenChange } = useDrawerContext();
394
+
395
+ return (
396
+ <button
397
+ type="button"
398
+ data-slot="drawer-close"
399
+ className={cn(buttonVariants({ variant: "ghost", size: "sm" }), className)}
400
+ onClick={(e) => {
401
+ onOpenChange(false);
402
+ onClick?.(e);
403
+ }}
404
+ {...props}
405
+ >
406
+ {children ?? <X className="h-4 w-4" />}
407
+ </button>
408
+ );
409
+ }
410
+
411
+ /* --------------------------------- Nav ----------------------------------- */
412
+
413
+ /**
414
+ * A vertical icon rail for switching between views inside a Drawer.
415
+ * Provides controlled navigation state to DrawerNavItem children.
416
+ *
417
+ * @example
418
+ * ```tsx
419
+ * <DrawerNav value={activeView} onValueChange={setActiveView}>
420
+ * <DrawerNavItem value="notes" icon={<FileText />} label="Notes" />
421
+ * <DrawerNavItem value="bookmarks" icon={<Bookmark />} label="Bookmarks" />
422
+ * </DrawerNav>
423
+ * ```
424
+ */
425
+ interface DrawerNavProps extends React.ComponentProps<"nav"> {
426
+ /** The value of the currently active nav item. */
427
+ value: string;
428
+ /** Callback fired when the active nav item changes. */
429
+ onValueChange: (value: string) => void;
430
+ }
431
+
432
+ function DrawerNav({
433
+ value,
434
+ onValueChange,
435
+ className,
436
+ children,
437
+ ...props
438
+ }: DrawerNavProps) {
439
+ const { setHasNav } = useDrawerContext();
440
+ const navRef = useRef<HTMLElement>(null);
441
+
442
+ useEffect(() => {
443
+ setHasNav(true);
444
+ return () => setHasNav(false);
445
+ }, [setHasNav]);
446
+
447
+ const handleKeyDown = (e: React.KeyboardEvent) => {
448
+ if (!["ArrowUp", "ArrowDown", "Home", "End"].includes(e.key)) return;
449
+
450
+ const nav = navRef.current;
451
+ if (!nav) return;
452
+
453
+ const items = Array.from(
454
+ nav.querySelectorAll<HTMLButtonElement>('[role="tab"]:not([disabled])'),
455
+ );
456
+ const current = items.indexOf(document.activeElement as HTMLButtonElement);
457
+ if (current === -1) return;
458
+
459
+ e.preventDefault();
460
+ let next: number;
461
+ switch (e.key) {
462
+ case "ArrowDown":
463
+ next = (current + 1) % items.length;
464
+ break;
465
+ case "ArrowUp":
466
+ next = (current - 1 + items.length) % items.length;
467
+ break;
468
+ case "Home":
469
+ next = 0;
470
+ break;
471
+ case "End":
472
+ next = items.length - 1;
473
+ break;
474
+ default:
475
+ return;
476
+ }
477
+ items[next].focus();
478
+ };
479
+
480
+ return (
481
+ <DrawerNavContext.Provider value={{ value, onValueChange }}>
482
+ <nav
483
+ ref={navRef}
484
+ role="tablist"
485
+ aria-orientation="vertical"
486
+ data-slot="drawer-nav"
487
+ className={cn(
488
+ "flex shrink-0 flex-col items-center gap-1 border-r bg-muted/30 px-1.5 py-2",
489
+ className,
490
+ )}
491
+ onKeyDown={handleKeyDown}
492
+ {...props}
493
+ >
494
+ {children}
495
+ </nav>
496
+ </DrawerNavContext.Provider>
497
+ );
498
+ }
499
+
500
+ /* ------------------------------ DrawerNavItem ----------------------------- */
501
+
502
+ /**
503
+ * An icon button inside a DrawerNav rail. Renders a tooltip on hover.
504
+ */
505
+ interface DrawerNavItemProps
506
+ extends Omit<React.ComponentProps<"button">, "value"> {
507
+ /** Unique value identifying this view. Must match a DrawerViewport's conditional content. */
508
+ value: string;
509
+ /** Icon element to render (e.g., `<FileText />`). */
510
+ icon: React.ReactNode;
511
+ /** Accessible label shown in the tooltip. */
512
+ label: string;
513
+ }
514
+
515
+ function DrawerNavItem({
516
+ value,
517
+ icon,
518
+ label,
519
+ className,
520
+ ...props
521
+ }: DrawerNavItemProps) {
522
+ const { value: activeValue, onValueChange } = useDrawerNavContext();
523
+ const { side } = useDrawerContext();
524
+ const isActive = activeValue === value;
525
+
526
+ return (
527
+ <Tooltip>
528
+ <TooltipTrigger>
529
+ <button
530
+ role="tab"
531
+ type="button"
532
+ aria-selected={isActive}
533
+ aria-label={label}
534
+ data-slot="drawer-nav-item"
535
+ {...(isActive ? { "data-active": "" } : {})}
536
+ className={cn(
537
+ "relative flex cursor-pointer items-center justify-center rounded-md size-9 transition-colors",
538
+ "text-muted-foreground hover:text-foreground hover:bg-accent",
539
+ isActive && "text-foreground bg-accent/50",
540
+ className,
541
+ )}
542
+ onClick={() => onValueChange(value)}
543
+ {...props}
544
+ >
545
+ {isActive && (
546
+ <span
547
+ aria-hidden="true"
548
+ className={cn(
549
+ "absolute top-1 bottom-1 w-0.5 rounded-full bg-primary",
550
+ side === "left" ? "right-0 -mr-1.5" : "left-0 -ml-1.5",
551
+ )}
552
+ />
553
+ )}
554
+ {icon}
555
+ </button>
556
+ </TooltipTrigger>
557
+ <TooltipContent>{label}</TooltipContent>
558
+ </Tooltip>
559
+ );
560
+ }
561
+
562
+ /* ----------------------------- DrawerViewport ----------------------------- */
563
+
564
+ /**
565
+ * Content panel paired with a DrawerNav rail. Wraps header/body/footer in the
566
+ * remaining space beside the icon rail.
567
+ */
568
+ function DrawerViewport({
569
+ className,
570
+ ...props
571
+ }: React.ComponentProps<"div">) {
572
+ return (
573
+ <div
574
+ role="tabpanel"
575
+ data-slot="drawer-viewport"
576
+ className={cn("flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", className)}
577
+ {...props}
578
+ />
579
+ );
580
+ }
581
+
582
+ /* -------------------------------- Exports -------------------------------- */
583
+
584
+ export {
585
+ Drawer,
586
+ DrawerTrigger,
587
+ DrawerPortal,
588
+ DrawerBackdrop,
589
+ DrawerContent,
590
+ DrawerHeader,
591
+ DrawerBody,
592
+ DrawerFooter,
593
+ DrawerTitle,
594
+ DrawerDescription,
595
+ DrawerClose,
596
+ DrawerNav,
597
+ DrawerNavItem,
598
+ DrawerViewport,
599
+ drawerContentVariants,
600
+ };
package/src/ui/index.ts CHANGED
@@ -18,6 +18,23 @@ export {
18
18
  AlertDialogCancel,
19
19
  } from "./alert-dialog";
20
20
  export { Avatar, AvatarImage, AvatarFallback } from "./avatar";
21
+ export {
22
+ Drawer,
23
+ DrawerTrigger,
24
+ DrawerPortal,
25
+ DrawerBackdrop,
26
+ DrawerContent,
27
+ DrawerHeader,
28
+ DrawerBody,
29
+ DrawerFooter,
30
+ DrawerTitle,
31
+ DrawerDescription,
32
+ DrawerClose,
33
+ DrawerNav,
34
+ DrawerNavItem,
35
+ DrawerViewport,
36
+ drawerContentVariants,
37
+ } from "./drawer";
21
38
  export {
22
39
  Card,
23
40
  CardHeader,
@@ -42,3 +59,7 @@ export {
42
59
  } from "./table";
43
60
  export { Skeleton } from "./skeleton";
44
61
  export { Tooltip, TooltipTrigger, TooltipContent } from "./tooltip";
62
+ export { RichTextEditor } from "./rich-text-editor";
63
+ export type { RichTextEditorProps, RichTextEditorVariant } from "./rich-text-editor";
64
+ export { ToastProvider, Toaster, useToast, toastVariants } from "./toast";
65
+ export type { ToastOptions, ToastVariant } from "./toast";
@@ -10,6 +10,10 @@ const progressVariants = cva("h-full rounded-full transition-all duration-300",
10
10
  success: "bg-success",
11
11
  warning: "bg-warning",
12
12
  info: "bg-info",
13
+ palette0: "bg-palette-0",
14
+ palette1: "bg-palette-1",
15
+ palette2: "bg-palette-2",
16
+ palette3: "bg-palette-3",
13
17
  },
14
18
  size: {
15
19
  sm: "",