coursecode 0.1.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 (362) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +322 -0
  3. package/THIRD_PARTY_NOTICES.md +22 -0
  4. package/bin/cli.js +331 -0
  5. package/framework/assets/logo-coursecode-black.svg +14 -0
  6. package/framework/assets/logo-coursecode-white.svg +14 -0
  7. package/framework/assets/logo-coursecode.svg +14 -0
  8. package/framework/css/01-base.css +160 -0
  9. package/framework/css/02-layout.css +499 -0
  10. package/framework/css/accessibility.css +834 -0
  11. package/framework/css/components/accordions.css +710 -0
  12. package/framework/css/components/assessments.css +520 -0
  13. package/framework/css/components/audio-player.css +570 -0
  14. package/framework/css/components/badges.css +80 -0
  15. package/framework/css/components/breadcrumbs.css +87 -0
  16. package/framework/css/components/buttons.css +707 -0
  17. package/framework/css/components/callouts.css +1280 -0
  18. package/framework/css/components/cards.css +475 -0
  19. package/framework/css/components/carousel.css +193 -0
  20. package/framework/css/components/checkbox-group.css +123 -0
  21. package/framework/css/components/checklist.css +203 -0
  22. package/framework/css/components/collapse.css +96 -0
  23. package/framework/css/components/comparison.css +33 -0
  24. package/framework/css/components/content-image.css +36 -0
  25. package/framework/css/components/document-gallery.css +425 -0
  26. package/framework/css/components/dropdown.css +115 -0
  27. package/framework/css/components/embed-frame.css +142 -0
  28. package/framework/css/components/engagement.css +412 -0
  29. package/framework/css/components/features.css +35 -0
  30. package/framework/css/components/flip-cards.css +253 -0
  31. package/framework/css/components/footer.css +353 -0
  32. package/framework/css/components/forms.css +294 -0
  33. package/framework/css/components/hero.css +216 -0
  34. package/framework/css/components/images.css +528 -0
  35. package/framework/css/components/interactive-timeline.css +274 -0
  36. package/framework/css/components/intro-cards.css +30 -0
  37. package/framework/css/components/lightbox.css +666 -0
  38. package/framework/css/components/loading.css +65 -0
  39. package/framework/css/components/modals.css +235 -0
  40. package/framework/css/components/notifications.css +107 -0
  41. package/framework/css/components/quote.css +150 -0
  42. package/framework/css/components/sidebar.css +684 -0
  43. package/framework/css/components/slide-header.css +52 -0
  44. package/framework/css/components/spinner.css +62 -0
  45. package/framework/css/components/stats.css +44 -0
  46. package/framework/css/components/steps.css +232 -0
  47. package/framework/css/components/tables.css +90 -0
  48. package/framework/css/components/tabs.css +347 -0
  49. package/framework/css/components/timeline.css +154 -0
  50. package/framework/css/components/toggle.css +95 -0
  51. package/framework/css/components/tooltip.css +226 -0
  52. package/framework/css/components/video-player.css +438 -0
  53. package/framework/css/design-tokens.css +707 -0
  54. package/framework/css/framework.css +86 -0
  55. package/framework/css/interactions/accessibility.css +75 -0
  56. package/framework/css/interactions/base.css +92 -0
  57. package/framework/css/interactions/drag-drop.css +295 -0
  58. package/framework/css/interactions/fill-in-the-blank.css +236 -0
  59. package/framework/css/interactions/hotspots.css +69 -0
  60. package/framework/css/interactions/index.css +45 -0
  61. package/framework/css/interactions/interactive-image.css +359 -0
  62. package/framework/css/interactions/likert.css +126 -0
  63. package/framework/css/interactions/matching.css +354 -0
  64. package/framework/css/interactions/numeric-input.css +78 -0
  65. package/framework/css/interactions/sequencing.css +378 -0
  66. package/framework/css/interactions/true-false.css +177 -0
  67. package/framework/css/layouts/article.css +258 -0
  68. package/framework/css/layouts/base.css +30 -0
  69. package/framework/css/layouts/canvas.css +38 -0
  70. package/framework/css/layouts/focused.css +236 -0
  71. package/framework/css/layouts/index.css +29 -0
  72. package/framework/css/layouts/presentation.css +191 -0
  73. package/framework/css/layouts/traditional.css +52 -0
  74. package/framework/css/responsive.css +439 -0
  75. package/framework/css/utilities/accessibility-utils.css +59 -0
  76. package/framework/css/utilities/animations.css +419 -0
  77. package/framework/css/utilities/borders.css +72 -0
  78. package/framework/css/utilities/colors.css +76 -0
  79. package/framework/css/utilities/container.css +46 -0
  80. package/framework/css/utilities/decorative.css +442 -0
  81. package/framework/css/utilities/display.css +257 -0
  82. package/framework/css/utilities/flexbox.css +80 -0
  83. package/framework/css/utilities/grid.css +69 -0
  84. package/framework/css/utilities/icons.css +534 -0
  85. package/framework/css/utilities/lists.css +190 -0
  86. package/framework/css/utilities/spacing.css +167 -0
  87. package/framework/css/utilities/tables.css +81 -0
  88. package/framework/css/utilities/typography.css +159 -0
  89. package/framework/css/utilities/visibility.css +117 -0
  90. package/framework/docs/COURSE_AUTHORING_GUIDE.md +1773 -0
  91. package/framework/docs/COURSE_OUTLINE_GUIDE.md +725 -0
  92. package/framework/docs/COURSE_OUTLINE_TEMPLATE.md +161 -0
  93. package/framework/docs/DATA_MODEL.md +409 -0
  94. package/framework/docs/FRAMEWORK_GUIDE.md +1088 -0
  95. package/framework/docs/USER_GUIDE.md +583 -0
  96. package/framework/docs/examples/cloudflare-channel-relay.js +169 -0
  97. package/framework/docs/examples/cloudflare-data-worker.js +102 -0
  98. package/framework/docs/examples/cloudflare-error-worker.js +228 -0
  99. package/framework/index.html +175 -0
  100. package/framework/js/app/AppActions.js +410 -0
  101. package/framework/js/app/AppState.js +225 -0
  102. package/framework/js/app/AppUI.js +616 -0
  103. package/framework/js/assessment/AssessmentActions.js +615 -0
  104. package/framework/js/assessment/AssessmentFactory.js +471 -0
  105. package/framework/js/assessment/AssessmentState.js +322 -0
  106. package/framework/js/assessment/AssessmentUI.js +451 -0
  107. package/framework/js/automation/api-engagement.js +196 -0
  108. package/framework/js/automation/api-interactions.js +167 -0
  109. package/framework/js/automation/api.js +242 -0
  110. package/framework/js/automation/index.js +41 -0
  111. package/framework/js/components/interactions/drag-drop.js +884 -0
  112. package/framework/js/components/interactions/fill-in.js +535 -0
  113. package/framework/js/components/interactions/hotspot.js +702 -0
  114. package/framework/js/components/interactions/interaction-base.js +511 -0
  115. package/framework/js/components/interactions/likert.js +301 -0
  116. package/framework/js/components/interactions/matching.js +699 -0
  117. package/framework/js/components/interactions/multiple-choice.js +377 -0
  118. package/framework/js/components/interactions/numeric.js +271 -0
  119. package/framework/js/components/interactions/sequencing.js +423 -0
  120. package/framework/js/components/interactions/true-false.js +241 -0
  121. package/framework/js/components/ui-components/accordion.js +442 -0
  122. package/framework/js/components/ui-components/alert.js +88 -0
  123. package/framework/js/components/ui-components/audio-player.js +1193 -0
  124. package/framework/js/components/ui-components/callout.js +121 -0
  125. package/framework/js/components/ui-components/carousel.js +145 -0
  126. package/framework/js/components/ui-components/checkbox-group.js +87 -0
  127. package/framework/js/components/ui-components/checklist.js +40 -0
  128. package/framework/js/components/ui-components/collapse.js +114 -0
  129. package/framework/js/components/ui-components/comparison.js +30 -0
  130. package/framework/js/components/ui-components/conditional-display.js +150 -0
  131. package/framework/js/components/ui-components/content-image.js +41 -0
  132. package/framework/js/components/ui-components/dropdown.js +262 -0
  133. package/framework/js/components/ui-components/embed-frame.js +274 -0
  134. package/framework/js/components/ui-components/features.js +33 -0
  135. package/framework/js/components/ui-components/flip-card.js +230 -0
  136. package/framework/js/components/ui-components/form-validator.js +76 -0
  137. package/framework/js/components/ui-components/hero.js +49 -0
  138. package/framework/js/components/ui-components/index.js +12 -0
  139. package/framework/js/components/ui-components/interactive-image.js +235 -0
  140. package/framework/js/components/ui-components/interactive-timeline.js +285 -0
  141. package/framework/js/components/ui-components/intro-cards.js +35 -0
  142. package/framework/js/components/ui-components/lightbox.js +652 -0
  143. package/framework/js/components/ui-components/modal.js +386 -0
  144. package/framework/js/components/ui-components/notifications.js +145 -0
  145. package/framework/js/components/ui-components/progress.js +88 -0
  146. package/framework/js/components/ui-components/quote.js +41 -0
  147. package/framework/js/components/ui-components/stats.js +33 -0
  148. package/framework/js/components/ui-components/steps.js +41 -0
  149. package/framework/js/components/ui-components/tabs.js +255 -0
  150. package/framework/js/components/ui-components/timeline.js +42 -0
  151. package/framework/js/components/ui-components/toggle-group.js +73 -0
  152. package/framework/js/components/ui-components/tooltip.js +458 -0
  153. package/framework/js/components/ui-components/value-display.js +133 -0
  154. package/framework/js/components/ui-components/video-player.js +686 -0
  155. package/framework/js/core/component-catalog.js +121 -0
  156. package/framework/js/core/event-bus.js +178 -0
  157. package/framework/js/core/interaction-catalog.js +149 -0
  158. package/framework/js/dev/runtime-linter.js +1725 -0
  159. package/framework/js/drivers/cmi5-driver.js +768 -0
  160. package/framework/js/drivers/driver-factory.js +77 -0
  161. package/framework/js/drivers/driver-interface.js +110 -0
  162. package/framework/js/drivers/http-driver-base.js +241 -0
  163. package/framework/js/drivers/lti-driver.js +508 -0
  164. package/framework/js/drivers/proxy-driver.js +444 -0
  165. package/framework/js/drivers/scorm-12-driver.js +560 -0
  166. package/framework/js/drivers/scorm-2004-driver.js +775 -0
  167. package/framework/js/drivers/scorm-driver-base.js +112 -0
  168. package/framework/js/engagement/engagement-manager.js +404 -0
  169. package/framework/js/engagement/engagement-progress.js +191 -0
  170. package/framework/js/engagement/engagement-trackers.js +215 -0
  171. package/framework/js/engagement/requirement-strategies.js +268 -0
  172. package/framework/js/main.js +727 -0
  173. package/framework/js/managers/accessibility-manager.js +499 -0
  174. package/framework/js/managers/assessment-manager.js +230 -0
  175. package/framework/js/managers/audio-manager.js +944 -0
  176. package/framework/js/managers/comment-manager.js +88 -0
  177. package/framework/js/managers/flag-manager.js +86 -0
  178. package/framework/js/managers/interaction-manager.js +254 -0
  179. package/framework/js/managers/interaction-registry.js +96 -0
  180. package/framework/js/managers/objective-manager.js +423 -0
  181. package/framework/js/managers/score-manager.js +441 -0
  182. package/framework/js/managers/video-manager.js +536 -0
  183. package/framework/js/navigation/Breadcrumbs.js +234 -0
  184. package/framework/js/navigation/NavigationActions.js +1132 -0
  185. package/framework/js/navigation/NavigationState.js +276 -0
  186. package/framework/js/navigation/NavigationUI.js +574 -0
  187. package/framework/js/navigation/document-gallery.js +357 -0
  188. package/framework/js/navigation/navigation-helpers.js +175 -0
  189. package/framework/js/navigation/navigation-validators.js +174 -0
  190. package/framework/js/state/index.js +8 -0
  191. package/framework/js/state/lms-connection.js +482 -0
  192. package/framework/js/state/lms-error-utils.js +58 -0
  193. package/framework/js/state/state-commits.js +200 -0
  194. package/framework/js/state/state-domains.js +86 -0
  195. package/framework/js/state/state-manager.js +502 -0
  196. package/framework/js/state/state-validation.js +311 -0
  197. package/framework/js/state/transaction-log.js +41 -0
  198. package/framework/js/state/xapi-statement-service.js +325 -0
  199. package/framework/js/utilities/access-control.js +99 -0
  200. package/framework/js/utilities/breakpoint-manager.js +315 -0
  201. package/framework/js/utilities/canvas-slide.js +35 -0
  202. package/framework/js/utilities/conditional-display.js +388 -0
  203. package/framework/js/utilities/course-channel.js +214 -0
  204. package/framework/js/utilities/course-helpers.js +420 -0
  205. package/framework/js/utilities/data-reporter.js +273 -0
  206. package/framework/js/utilities/error-reporter.js +313 -0
  207. package/framework/js/utilities/hotspot-helper.js +341 -0
  208. package/framework/js/utilities/icons.js +348 -0
  209. package/framework/js/utilities/logger.js +92 -0
  210. package/framework/js/utilities/markdown-renderer.js +45 -0
  211. package/framework/js/utilities/scroll-tracker.js +68 -0
  212. package/framework/js/utilities/ui-initializer.js +146 -0
  213. package/framework/js/utilities/utilities.js +293 -0
  214. package/framework/js/utilities/view-manager.js +227 -0
  215. package/framework/js/validation/html-validators.js +422 -0
  216. package/framework/js/validation/scorm-validators.js +438 -0
  217. package/framework/js/vendor/pipwerks.js +931 -0
  218. package/framework/scripts/generate-narration.js +629 -0
  219. package/framework/scripts/tts-providers/azure-provider.js +178 -0
  220. package/framework/scripts/tts-providers/base-provider.js +81 -0
  221. package/framework/scripts/tts-providers/deepgram-provider.js +135 -0
  222. package/framework/scripts/tts-providers/elevenlabs-provider.js +148 -0
  223. package/framework/scripts/tts-providers/google-provider.js +272 -0
  224. package/framework/scripts/tts-providers/index.js +158 -0
  225. package/framework/scripts/tts-providers/openai-provider.js +143 -0
  226. package/framework/version.json +63 -0
  227. package/lib/authoring-api.js +919 -0
  228. package/lib/build-linter.js +450 -0
  229. package/lib/build-packaging.js +186 -0
  230. package/lib/build.js +88 -0
  231. package/lib/cloud.js +691 -0
  232. package/lib/convert.js +341 -0
  233. package/lib/course-parser.js +936 -0
  234. package/lib/course-writer.js +258 -0
  235. package/lib/create.js +248 -0
  236. package/lib/css-index.js +237 -0
  237. package/lib/dev.js +51 -0
  238. package/lib/export-content.js +1246 -0
  239. package/lib/headless-browser.js +413 -0
  240. package/lib/import.js +377 -0
  241. package/lib/index.js +80 -0
  242. package/lib/info.js +79 -0
  243. package/lib/interaction-formatters.js +568 -0
  244. package/lib/manifest/cmi5-manifest.js +63 -0
  245. package/lib/manifest/lti-tool-config.js +53 -0
  246. package/lib/manifest/manifest-factory.js +99 -0
  247. package/lib/manifest/scorm-12-manifest.js +61 -0
  248. package/lib/manifest/scorm-2004-manifest.js +94 -0
  249. package/lib/manifest/scorm-proxy-manifest.js +104 -0
  250. package/lib/manifest-parser.js +96 -0
  251. package/lib/mcp-prompts.js +753 -0
  252. package/lib/mcp-server.js +316 -0
  253. package/lib/narration.js +53 -0
  254. package/lib/pdf-structure.js +142 -0
  255. package/lib/preview-export.js +231 -0
  256. package/lib/preview-routes-api.js +662 -0
  257. package/lib/preview-routes-editing.js +159 -0
  258. package/lib/preview-routes-lms.js +230 -0
  259. package/lib/preview-server.js +564 -0
  260. package/lib/project-utils.js +269 -0
  261. package/lib/proxy-templates/proxy.html +68 -0
  262. package/lib/proxy-templates/scorm-bridge.js +112 -0
  263. package/lib/scaffold.js +193 -0
  264. package/lib/schema-extractor.js +361 -0
  265. package/lib/slide-source-editor.js +586 -0
  266. package/lib/stub-player/app-viewer.js +195 -0
  267. package/lib/stub-player/app.js +370 -0
  268. package/lib/stub-player/catalog-panel.js +312 -0
  269. package/lib/stub-player/config-panel.js +1303 -0
  270. package/lib/stub-player/content-generator.js +586 -0
  271. package/lib/stub-player/content-viewer.js +173 -0
  272. package/lib/stub-player/debug-panel.js +420 -0
  273. package/lib/stub-player/edit-mode.js +922 -0
  274. package/lib/stub-player/edit-utils.js +400 -0
  275. package/lib/stub-player/header-bar.js +354 -0
  276. package/lib/stub-player/interaction-editor.js +210 -0
  277. package/lib/stub-player/interactions-panel.js +565 -0
  278. package/lib/stub-player/lms-api.js +1094 -0
  279. package/lib/stub-player/login-screen.js +74 -0
  280. package/lib/stub-player/outline-mode.js +689 -0
  281. package/lib/stub-player/styles/_assessments-panel.css +245 -0
  282. package/lib/stub-player/styles/_base.css +89 -0
  283. package/lib/stub-player/styles/_catalog-icons.css +96 -0
  284. package/lib/stub-player/styles/_catalog-panel.css +291 -0
  285. package/lib/stub-player/styles/_config-panel.css +636 -0
  286. package/lib/stub-player/styles/_content-viewer.css +834 -0
  287. package/lib/stub-player/styles/_debug-panel.css +576 -0
  288. package/lib/stub-player/styles/_edit-mode.css +128 -0
  289. package/lib/stub-player/styles/_header-bar.css +343 -0
  290. package/lib/stub-player/styles/_interaction-editor.css +140 -0
  291. package/lib/stub-player/styles/_interactions-panel.css +1038 -0
  292. package/lib/stub-player/styles/_login-screen.css +102 -0
  293. package/lib/stub-player/styles/_outline-mode.css +752 -0
  294. package/lib/stub-player/styles.css +15 -0
  295. package/lib/stub-player.js +160 -0
  296. package/lib/test-data-reporting.js +176 -0
  297. package/lib/test-error-reporting.js +146 -0
  298. package/lib/token.js +86 -0
  299. package/lib/upgrade.js +257 -0
  300. package/lib/validation-rules.js +517 -0
  301. package/lib/vite-plugin-content-discovery.js +296 -0
  302. package/package.json +108 -0
  303. package/schemas/XMLSchema.dtd +402 -0
  304. package/schemas/adlcp_v1p3.xsd +111 -0
  305. package/schemas/adlnav_v1p3.xsd +61 -0
  306. package/schemas/adlseq_v1p3.xsd +93 -0
  307. package/schemas/common/anyElement.xsd +27 -0
  308. package/schemas/common/dataTypes.xsd +138 -0
  309. package/schemas/common/elementNames.xsd +767 -0
  310. package/schemas/common/elementTypes.xsd +786 -0
  311. package/schemas/common/rootElement.xsd +31 -0
  312. package/schemas/common/vocabTypes.xsd +345 -0
  313. package/schemas/common/vocabValues.xsd +257 -0
  314. package/schemas/datatypes.dtd +203 -0
  315. package/schemas/ims_xml.xsd +35 -0
  316. package/schemas/imscp_v1p1.xsd +368 -0
  317. package/schemas/imsss_v1p0.xsd +67 -0
  318. package/schemas/imsss_v1p0auxresource.xsd +19 -0
  319. package/schemas/imsss_v1p0control.xsd +20 -0
  320. package/schemas/imsss_v1p0delivery.xsd +17 -0
  321. package/schemas/imsss_v1p0limit.xsd +47 -0
  322. package/schemas/imsss_v1p0objective.xsd +67 -0
  323. package/schemas/imsss_v1p0random.xsd +16 -0
  324. package/schemas/imsss_v1p0rollup.xsd +46 -0
  325. package/schemas/imsss_v1p0seqrule.xsd +108 -0
  326. package/schemas/imsss_v1p0util.xsd +94 -0
  327. package/schemas/license.txt +17 -0
  328. package/schemas/lom.xsd +102 -0
  329. package/schemas/lomCustom.xsd +62 -0
  330. package/schemas/lomLoose.xsd +62 -0
  331. package/schemas/lomStrict.xsd +62 -0
  332. package/schemas/xml.xsd +81 -0
  333. package/template/.env.example +92 -0
  334. package/template/course/assets/audio/example-intro.mp3 +0 -0
  335. package/template/course/assets/audio/example-ui-demo--compact-player.mp3 +0 -0
  336. package/template/course/assets/audio/example-ui-demo--demo-modal.mp3 +0 -0
  337. package/template/course/assets/audio/example-ui-demo--full-player.mp3 +0 -0
  338. package/template/course/assets/docs/example_md_1.md +39 -0
  339. package/template/course/assets/docs/example_md_2.md +41 -0
  340. package/template/course/assets/docs/example_pdf_1_thumbnail.png +0 -0
  341. package/template/course/assets/docs/example_pdf_2.pdf +0 -0
  342. package/template/course/assets/images/course-architecture.svg +36 -0
  343. package/template/course/assets/images/logo.svg +14 -0
  344. package/template/course/assets/widgets/counter-demo.html +190 -0
  345. package/template/course/assets/widgets/gravity-painter.html +384 -0
  346. package/template/course/course-config.js +539 -0
  347. package/template/course/icons.js +19 -0
  348. package/template/course/interactions/PLUGIN_GUIDE.md +97 -0
  349. package/template/course/slides/example-course-structure.js +138 -0
  350. package/template/course/slides/example-final-exam.js +144 -0
  351. package/template/course/slides/example-finishing.js +127 -0
  352. package/template/course/slides/example-interactions-showcase.js +615 -0
  353. package/template/course/slides/example-preview-tour.js +129 -0
  354. package/template/course/slides/example-remedial.js +143 -0
  355. package/template/course/slides/example-summary.js +103 -0
  356. package/template/course/slides/example-ui-showcase.js +1805 -0
  357. package/template/course/slides/example-welcome.js +123 -0
  358. package/template/course/slides/example-workflow.js +140 -0
  359. package/template/course/theme.css +165 -0
  360. package/template/eslint.config.js +47 -0
  361. package/template/package.json +28 -0
  362. package/template/vite.config.js +339 -0
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @file callout.js
3
+ * @description Optional icon hydration for callout.
4
+ *
5
+ * Usage:
6
+ * <aside class="callout callout--warning" data-component="callout" data-icon="auto">
7
+ * <h4 class="callout__title">Title</h4>
8
+ * <div class="callout__body"><p>Body text.</p></div>
9
+ * </aside>
10
+ */
11
+
12
+ import { iconManager } from '../../utilities/icons.js';
13
+
14
+ export const schema = {
15
+ type: 'callout',
16
+ description: 'Modern callout with optional automatic icon injection',
17
+ example: `<aside class="callout callout--info" data-component="callout" data-icon="auto">
18
+ <h4 class="callout__title">Helpful context</h4>
19
+ <div class="callout__body"><p>Message text.</p></div>
20
+ </aside>`,
21
+ properties: {
22
+ icon: {
23
+ type: 'string',
24
+ required: false,
25
+ description: 'Icon name or "auto" (maps from severity).'
26
+ },
27
+ iconSize: {
28
+ type: 'string',
29
+ required: false,
30
+ description: 'Icon size token: xs|sm|md|lg|xl|2xl|3xl (default: sm).'
31
+ },
32
+ iconClass: {
33
+ type: 'string',
34
+ required: false,
35
+ description: 'Optional extra icon class.'
36
+ }
37
+ },
38
+ structure: {
39
+ container: '[data-component="callout"]',
40
+ children: {
41
+ title: { selector: '.callout__title', required: true },
42
+ body: { selector: '.callout__body', required: true },
43
+ icon: { selector: '.callout__icon', required: false }
44
+ }
45
+ }
46
+ };
47
+
48
+ export const metadata = {
49
+ category: 'ui-component',
50
+ cssFile: 'components/callouts.css',
51
+ engagementTracking: null,
52
+ emitsEvents: []
53
+ };
54
+
55
+ const ICON_BY_SEVERITY = Object.freeze({
56
+ neutral: 'info',
57
+ info: 'info',
58
+ success: 'check-circle',
59
+ warning: 'alert-circle',
60
+ danger: 'alert-octagon'
61
+ });
62
+
63
+ function getSeverity(element) {
64
+ if (element.classList.contains('callout--danger')) return 'danger';
65
+ if (element.classList.contains('callout--warning')) return 'warning';
66
+ if (element.classList.contains('callout--success')) return 'success';
67
+ if (element.classList.contains('callout--neutral')) return 'neutral';
68
+ return 'info';
69
+ }
70
+
71
+ function resolveIconName(element, requested) {
72
+ if (requested && requested !== 'auto') return requested;
73
+ const severity = getSeverity(element);
74
+ return ICON_BY_SEVERITY[severity] || 'info';
75
+ }
76
+
77
+ function findTopLevelIconSlot(element) {
78
+ return Array.from(element.children).find((child) => child.classList?.contains('callout__icon')) || null;
79
+ }
80
+
81
+ function ensureIconSlot(element) {
82
+ const existing = findTopLevelIconSlot(element);
83
+ if (existing) {
84
+ existing.setAttribute('aria-hidden', 'true');
85
+ return existing;
86
+ }
87
+
88
+ const iconSlot = document.createElement('span');
89
+ iconSlot.className = 'callout__icon';
90
+ iconSlot.setAttribute('aria-hidden', 'true');
91
+
92
+ const title = Array.from(element.children).find((child) => child.classList?.contains('callout__title'));
93
+ if (title) {
94
+ element.insertBefore(iconSlot, title);
95
+ } else {
96
+ element.prepend(iconSlot);
97
+ }
98
+
99
+ return iconSlot;
100
+ }
101
+
102
+ export function init(element) {
103
+ if (!element?.classList?.contains('callout')) return;
104
+
105
+ const requested = (element.dataset.icon || '').trim();
106
+ const existingIconSlot = findTopLevelIconSlot(element);
107
+
108
+ // Preserve manual icon markup unless explicit icon hydration is requested.
109
+ if (!requested && existingIconSlot) return;
110
+
111
+ // Keep no-icon callouts clean unless icon hydration is requested.
112
+ if (!requested && !existingIconSlot) return;
113
+
114
+ const iconName = resolveIconName(element, requested);
115
+ const iconSize = (element.dataset.iconSize || 'sm').trim();
116
+ const iconClass = (element.dataset.iconClass || '').trim();
117
+ const iconSlot = ensureIconSlot(element);
118
+
119
+ iconSlot.innerHTML = iconManager.getIcon(iconName, { size: iconSize, class: iconClass });
120
+ }
121
+
@@ -0,0 +1,145 @@
1
+ /**
2
+ * @file carousel.js
3
+ * @description A simple, accessible image carousel/slider component.
4
+ *
5
+ * Usage (Declarative):
6
+ * <div class="carousel" data-component="carousel" id="my-carousel">
7
+ * <div class="carousel-track">
8
+ * <div class="carousel-slide">...</div>
9
+ * <div class="carousel-slide">...</div>
10
+ * </div>
11
+ * <button class="carousel-button prev" data-action="prev-slide" aria-label="Previous Slide">&#10094;</button>
12
+ * <button class="carousel-button next" data-action="next-slide" aria-label="Next Slide">&#10095;</button>
13
+ * <div class="carousel-dots"></div>
14
+ * </div>
15
+ */
16
+
17
+ // Schema for validation, linting, and AI-assisted authoring
18
+ export const schema = {
19
+ type: 'carousel',
20
+ description: 'Accessible image carousel/slider with dot navigation',
21
+ example: `<div data-component="carousel">
22
+ <div class="carousel-track">
23
+ <div class="carousel-slide active"><div class="card no-hover"><div class="card-body"><h3>Slide 1</h3><p>First slide content with important information.</p></div></div></div>
24
+ <div class="carousel-slide"><div class="card no-hover"><div class="card-body"><h3>Slide 2</h3><p>Second slide with additional details.</p></div></div></div>
25
+ <div class="carousel-slide"><div class="card no-hover"><div class="card-body"><h3>Slide 3</h3><p>Third slide wrapping up the sequence.</p></div></div></div>
26
+ </div>
27
+ <button class="carousel-button prev" data-action="prev-slide" aria-label="Previous Slide">&#10094;</button>
28
+ <button class="carousel-button next" data-action="next-slide" aria-label="Next Slide">&#10095;</button>
29
+ <div class="carousel-dots"></div>
30
+ </div>`,
31
+ properties: {
32
+ autoPlay: { type: 'boolean', default: false, description: 'Auto-advance slides' },
33
+ interval: { type: 'number', default: 5000, description: 'Auto-advance interval in ms' }
34
+ },
35
+ structure: {
36
+ container: '[data-component="carousel"]',
37
+ children: {
38
+ track: { selector: '.carousel-track', required: true },
39
+ slide: { selector: '.carousel-slide', parent: '.carousel-track', required: true, minItems: 1 },
40
+ prevButton: { selector: '[data-action="prev-slide"]' },
41
+ nextButton: { selector: '[data-action="next-slide"]' },
42
+ dots: { selector: '.carousel-dots' }
43
+ }
44
+ }
45
+ };
46
+
47
+ export const metadata = {
48
+ category: 'ui-component',
49
+ cssFile: 'components/carousel.css',
50
+ engagementTracking: null,
51
+ emitsEvents: ['carousel:slide-changed']
52
+ };
53
+
54
+ import { logger } from '../../utilities/logger.js';
55
+
56
+ /**
57
+ * Initializes a carousel component.
58
+ * @param {HTMLElement} container - The main container for the carousel.
59
+ */
60
+ export function init(container) {
61
+ if (!container) {
62
+ logger.fatal('initCarousel: container not found.', { domain: 'ui', operation: 'initCarousel' });
63
+ return;
64
+ }
65
+
66
+ const track = container.querySelector('.carousel-track');
67
+ const slides = Array.from(container.querySelectorAll('.carousel-slide'));
68
+ const prevButton = container.querySelector('[data-action="prev-slide"]');
69
+ const nextButton = container.querySelector('[data-action="next-slide"]');
70
+ const dotsContainer = container.querySelector('.carousel-dots');
71
+
72
+ if (!track || !slides.length) {
73
+ logger.fatal('initCarousel: A track and slides are required. Ensure carousel HTML structure is correct with .carousel-track and .carousel-slide elements.', { domain: 'ui', operation: 'initCarousel' });
74
+ return;
75
+ }
76
+
77
+ let currentIndex = 0;
78
+
79
+ const updateCarousel = () => {
80
+ // Move the track
81
+ track.style.transform = `translateX(-${currentIndex * 100}%)`;
82
+
83
+ // Update buttons
84
+ if (prevButton) {
85
+ prevButton.disabled = currentIndex === 0;
86
+ }
87
+ if (nextButton) {
88
+ nextButton.disabled = currentIndex === slides.length - 1;
89
+ }
90
+
91
+ // Update dots
92
+ if (dotsContainer) {
93
+ const dots = Array.from(dotsContainer.children);
94
+ dots.forEach((dot, index) => {
95
+ dot.classList.toggle('active', index === currentIndex);
96
+ });
97
+ }
98
+
99
+ // Update slide visibility for screen readers
100
+ slides.forEach((slide, index) => {
101
+ slide.setAttribute('aria-hidden', index !== currentIndex);
102
+ });
103
+ };
104
+
105
+ const goToSlide = (index) => {
106
+ if (index < 0 || index >= slides.length) return;
107
+ currentIndex = index;
108
+ updateCarousel();
109
+ };
110
+
111
+ // Create dot indicators
112
+ if (dotsContainer) {
113
+ dotsContainer.innerHTML = '';
114
+ slides.forEach((_, index) => {
115
+ const dot = document.createElement('button');
116
+ dot.className = 'carousel-dot';
117
+ dot.setAttribute('aria-label', `Go to slide ${index + 1}`);
118
+ dot.addEventListener('click', () => goToSlide(index));
119
+ dotsContainer.appendChild(dot);
120
+ });
121
+ }
122
+
123
+ const clickHandler = (event) => {
124
+ const actionTarget = event.target.closest('[data-action]');
125
+ if (!actionTarget) return;
126
+
127
+ const action = actionTarget.dataset.action;
128
+ if (action === 'next-slide') {
129
+ goToSlide(currentIndex + 1);
130
+ } else if (action === 'prev-slide') {
131
+ goToSlide(currentIndex - 1);
132
+ }
133
+ };
134
+
135
+ container.addEventListener('click', clickHandler);
136
+
137
+ // Initialize state
138
+ updateCarousel();
139
+
140
+ return {
141
+ destroy: () => {
142
+ container.removeEventListener('click', clickHandler);
143
+ }
144
+ };
145
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @file checkbox-group.js
3
+ * @description Handles a group of checkboxes, aggregating their values and emitting change events.
4
+ */
5
+
6
+ export const schema = {
7
+ type: 'checkbox-group',
8
+ description: 'Group of checkboxes with aggregated value tracking',
9
+ example: `<div data-component="checkbox-group" class="checkbox-group">
10
+ <div class="checkbox-option"><input type="checkbox" value="html" id="cb-html" checked><label for="cb-html" class="checkbox-label">HTML &amp; CSS</label></div>
11
+ <div class="checkbox-option"><input type="checkbox" value="js" id="cb-js"><label for="cb-js" class="checkbox-label">JavaScript</label></div>
12
+ <div class="checkbox-option"><input type="checkbox" value="a11y" id="cb-a11y"><label for="cb-a11y" class="checkbox-label">Accessibility</label></div>
13
+ </div>`,
14
+ properties: {},
15
+ structure: {
16
+ container: '[data-component="checkbox-group"]',
17
+ children: {
18
+ checkbox: { selector: 'input[type="checkbox"]', required: true, minItems: 1 }
19
+ }
20
+ }
21
+ };
22
+
23
+ export const metadata = {
24
+ category: 'ui-component',
25
+ cssFile: 'components/checkbox-group.css',
26
+ engagementTracking: null,
27
+ emitsEvents: ['checkbox-group:change']
28
+ };
29
+
30
+ import { logger } from '../../utilities/logger.js';
31
+
32
+ export function init(element) {
33
+ if (!element) {
34
+ logger.fatal('CheckboxGroup: Container element is required', { domain: 'ui', operation: 'initCheckboxGroup' });
35
+ return;
36
+ }
37
+
38
+ const checkboxes = element.querySelectorAll('input[type="checkbox"]');
39
+
40
+ if (checkboxes.length === 0) {
41
+ logger.fatal('CheckboxGroup: No checkbox inputs found in container', { domain: 'ui', operation: 'initCheckboxGroup' });
42
+ return;
43
+ }
44
+
45
+ const handleChange = () => {
46
+ const checked = Array.from(checkboxes).filter(cb => cb.checked);
47
+ const values = checked.map(cb => cb.value);
48
+
49
+ // Try to find labels.
50
+ // 1. Look for .checkbox-label inside the closest .checkbox-option (Framework convention)
51
+ // 2. Look for associated <label> (Standard HTML)
52
+ // 3. Use value
53
+ const labels = checked.map(cb => {
54
+ const option = cb.closest('.checkbox-option');
55
+ if (option) {
56
+ const labelEl = option.querySelector('.checkbox-label');
57
+ if (labelEl) return labelEl.textContent;
58
+ }
59
+ if (cb.labels && cb.labels.length > 0) return cb.labels[0].textContent;
60
+ return cb.value;
61
+ });
62
+
63
+ const event = new CustomEvent('checkbox-group:change', {
64
+ bubbles: true,
65
+ detail: {
66
+ values: values,
67
+ labels: labels,
68
+ count: checked.length,
69
+ // Helper for simple display
70
+ value: labels.join(', ')
71
+ }
72
+ });
73
+ element.dispatchEvent(event);
74
+ };
75
+
76
+ checkboxes.forEach(cb => {
77
+ cb.addEventListener('change', handleChange);
78
+ });
79
+
80
+ return {
81
+ destroy: () => {
82
+ checkboxes.forEach(cb => {
83
+ cb.removeEventListener('change', handleChange);
84
+ });
85
+ }
86
+ };
87
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Checklist Layout Pattern
3
+ *
4
+ * CSS-only component for task/requirement checklists.
5
+ */
6
+
7
+ export const schema = {
8
+ type: 'checklist',
9
+ description: 'Checklist with items and completion states',
10
+ example: `<div data-component="checklist">
11
+ <div class="checklist-item completed"><span class="checklist-text">Review course outline</span></div>
12
+ <div class="checklist-item completed"><span class="checklist-text">Create slide content</span></div>
13
+ <div class="checklist-item"><span class="checklist-text">Add interactions and assessments</span></div>
14
+ </div>`,
15
+ properties: {
16
+ style: {
17
+ type: 'string',
18
+ enum: ['default', 'cards', 'minimal', 'numbered'],
19
+ default: 'default',
20
+ description: 'Visual style variant (data-checklist-style attribute)'
21
+ }
22
+ },
23
+ structure: {
24
+ container: '[data-component="checklist"]',
25
+ children: {
26
+ item: { selector: '.checklist-item', required: true, minItems: 1 },
27
+ text: { selector: '.checklist-text', parent: '.checklist-item', required: true },
28
+ status: { selector: '.checklist-status', parent: '.checklist-item' }
29
+ }
30
+ }
31
+ };
32
+
33
+ export const metadata = {
34
+ category: 'ui-component',
35
+ cssOnly: true,
36
+ cssFile: 'components/checklist.css'
37
+ };
38
+
39
+ /** No-op initializer — CSS-only component, registered for consistency. */
40
+ export function init() {}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @file collapse.js
3
+ * @description Handles "Show More/Less" or collapsible content sections.
4
+ *
5
+ * Usage (Declarative):
6
+ * <div data-component="collapse">
7
+ * <div class="collapse-panel" id="transcript-panel">
8
+ * <p>Long content goes here...</p>
9
+ * </div>
10
+ * <button class="collapse-trigger" data-action="toggle-collapse" aria-controls="transcript-panel" aria-expanded="false">
11
+ * <span class="collapse-text-show">Show Transcript</span>
12
+ * <span class="collapse-text-hide">Hide Transcript</span>
13
+ * </button>
14
+ * </div>
15
+ */
16
+
17
+ export const schema = {
18
+ type: 'collapse',
19
+ description: 'Collapsible content section with show/hide toggle',
20
+ example: `<div data-component="collapse">
21
+ <button class="btn btn-secondary collapse-trigger" data-action="toggle-collapse" aria-controls="preview-collapse" aria-expanded="false">
22
+ <span class="collapse-text-show">Show Details</span><span class="collapse-text-hide">Hide Details</span>
23
+ </button>
24
+ <div class="collapse-panel mt-3" id="preview-collapse">
25
+ <div class="p-4 bg-gray-100 rounded"><p>This content is hidden by default. Click the button above to toggle visibility.</p></div>
26
+ </div>
27
+ </div>`,
28
+ properties: {},
29
+ structure: {
30
+ container: '[data-component="collapse"]',
31
+ children: {
32
+ panel: { selector: '.collapse-panel', required: true },
33
+ trigger: { selector: '[data-action="toggle-collapse"]', required: true }
34
+ }
35
+ }
36
+ };
37
+
38
+ export const metadata = {
39
+ category: 'ui-component',
40
+ cssFile: 'components/collapse.css',
41
+ engagementTracking: null,
42
+ emitsEvents: ['collapse:toggle']
43
+ };
44
+
45
+ import { logger } from '../../utilities/logger.js';
46
+
47
+ /**
48
+ * Initializes a collapse component.
49
+ * @param {HTMLElement} container - The main container for the collapse component.
50
+ * @returns {object} An object with a `destroy` method.
51
+ */
52
+ export function init(container) {
53
+ if (!container) {
54
+ logger.fatal('initCollapse: container not found.', { domain: 'ui', operation: 'initCollapse' });
55
+ return;
56
+ }
57
+
58
+ const trigger = container.querySelector('[data-action="toggle-collapse"]');
59
+ if (!trigger) {
60
+ logger.fatal('initCollapse: No trigger found with data-action="toggle-collapse". Ensure collapse trigger button exists.', { domain: 'ui', operation: 'initCollapse' });
61
+ return;
62
+ }
63
+
64
+ const panelId = trigger.getAttribute('aria-controls');
65
+ const panel = panelId ? container.querySelector(`#${panelId}`) : null;
66
+
67
+ if (!panel) {
68
+ logger.fatal(`initCollapse: No panel found with ID "${panelId}". Ensure trigger's aria-controls matches panel ID.`, { domain: 'ui', operation: 'initCollapse' });
69
+ return;
70
+ }
71
+
72
+ const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
73
+ panel.style.maxHeight = isExpanded ? `${panel.scrollHeight}px` : '0';
74
+ container.classList.toggle('expanded', isExpanded);
75
+
76
+ const toggle = () => {
77
+ const currentlyExpanded = trigger.getAttribute('aria-expanded') === 'true';
78
+
79
+ trigger.setAttribute('aria-expanded', !currentlyExpanded);
80
+ container.classList.toggle('expanded', !currentlyExpanded);
81
+
82
+ if (!currentlyExpanded) {
83
+ // Expanding
84
+ panel.style.display = 'block'; // Make it visible before calculating scrollHeight
85
+ panel.style.maxHeight = `${panel.scrollHeight}px`;
86
+ } else {
87
+ // Collapsing
88
+ panel.style.maxHeight = '0';
89
+ }
90
+ };
91
+
92
+ // Handle transition end to set display: none for accessibility and performance
93
+ panel.addEventListener('transitionend', () => {
94
+ if (trigger.getAttribute('aria-expanded') === 'false') {
95
+ panel.style.display = 'none';
96
+ }
97
+ });
98
+
99
+ // Ensure panel is correctly displayed or hidden on init
100
+ if (trigger.getAttribute('aria-expanded') === 'false') {
101
+ panel.style.display = 'none';
102
+ } else {
103
+ panel.style.display = 'block';
104
+ }
105
+
106
+
107
+ trigger.addEventListener('click', toggle);
108
+
109
+ return {
110
+ destroy: () => {
111
+ trigger.removeEventListener('click', toggle);
112
+ }
113
+ };
114
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Comparison Layout Pattern
3
+ *
4
+ * CSS-only component for side-by-side comparison of two options.
5
+ */
6
+
7
+ export const schema = {
8
+ type: 'comparison',
9
+ description: 'Side-by-side comparison grid',
10
+ example: `<div data-component="comparison">
11
+ <div class="comparison-item"><h3>Advantages</h3><ul><li>Easy to use</li><li>Fast performance</li><li>Great documentation</li></ul></div>
12
+ <div class="comparison-item"><h3>Limitations</h3><ul><li>Steeper learning curve</li><li>Limited integrations</li><li>Requires modern browser</li></ul></div>
13
+ </div>`,
14
+ properties: {},
15
+ structure: {
16
+ container: '[data-component="comparison"]',
17
+ children: {
18
+ item: { selector: '.comparison-item', required: true, minItems: 2 }
19
+ }
20
+ }
21
+ };
22
+
23
+ export const metadata = {
24
+ category: 'ui-component',
25
+ cssOnly: true,
26
+ cssFile: 'components/comparison.css'
27
+ };
28
+
29
+ /** No-op initializer — CSS-only component, registered for consistency. */
30
+ export function init() {}