@cyberskill/design 1.1.1 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (481) hide show
  1. package/DESIGN.md +28 -5
  2. package/README.md +23 -22
  3. package/SKILL.md +7 -7
  4. package/THIRD-PARTY-NOTICES.md +35 -0
  5. package/VERSION +1 -1
  6. package/_ds_bundle.js +1495 -1733
  7. package/_ds_manifest.json +1 -1
  8. package/_esm/cs.mjs +18 -4
  9. package/_esm/react.d.ts +2 -0
  10. package/_esm/react.mjs +7 -1
  11. package/_vendor/NOTICE.md +16 -0
  12. package/_vendor/babel/README.md +7 -0
  13. package/_vendor/babel/babel.min.js +4 -0
  14. package/_vendor/react/README.md +3 -0
  15. package/_vendor/react/react-dom.production.min.js +1 -0
  16. package/_vendor/react/react.production.min.js +1 -0
  17. package/assets/aurora-hoa.webp +0 -0
  18. package/assets/aurora-kim.webp +0 -0
  19. package/assets/aurora-moc.webp +0 -0
  20. package/assets/aurora-tho.webp +0 -0
  21. package/assets/aurora-thuy.webp +0 -0
  22. package/base/a11y.css +18 -0
  23. package/base/ai.css +9 -4
  24. package/base/collections.css +18 -7
  25. package/base/components.css +16 -4
  26. package/base/controls.css +116 -66
  27. package/base/data.css +4 -4
  28. package/base/feedback.css +14 -2
  29. package/base/forms.css +10 -4
  30. package/base/glass.css +7 -1
  31. package/base/interaction.css +6 -2
  32. package/base/navigation.css +9 -3
  33. package/base/reset.css +7 -1
  34. package/base/responsive.css +38 -12
  35. package/base/shell.css +15 -3
  36. package/components/_i18n/i18n.js +41 -4
  37. package/components/_i18n/strings.js +89 -22
  38. package/components/ai/ChatMessage.d.ts +2 -0
  39. package/components/ai/ChatMessage.jsx +8 -4
  40. package/components/ai/ConfidenceMeter.d.ts +2 -0
  41. package/components/ai/ConfidenceMeter.jsx +8 -6
  42. package/components/ai/HumanReviewGate.d.ts +2 -0
  43. package/components/ai/PromptInput.d.ts +2 -0
  44. package/components/ai/TypingIndicator.d.ts +2 -0
  45. package/components/ai/ai-conversation.card.html +3 -3
  46. package/components/ai/ai-lumi.card.html +3 -3
  47. package/components/ai/ai.card.html +3 -3
  48. package/components/brand/brand.card.html +3 -3
  49. package/components/button/buttons.card.html +3 -3
  50. package/components/data/Image.jsx +18 -2
  51. package/components/data/Sortable.d.ts +5 -1
  52. package/components/data/Sortable.jsx +42 -9
  53. package/components/data/Sortable.prompt.md +4 -2
  54. package/components/data/Terminal.jsx +3 -2
  55. package/components/data/Tooltip.d.ts +2 -0
  56. package/components/data/Tooltip.jsx +6 -3
  57. package/components/data/Tree.d.ts +2 -1
  58. package/components/data/Tree.jsx +137 -16
  59. package/components/data/Tree.prompt.md +1 -1
  60. package/components/data/Watermark.jsx +11 -3
  61. package/components/data/collections.card.html +3 -3
  62. package/components/data/data.card.html +3 -3
  63. package/components/datatable/DataGrid.jsx +10 -1
  64. package/components/datatable/DataTable.d.ts +24 -1
  65. package/components/datatable/DataTable.jsx +78 -5
  66. package/components/datatable/DataTable.prompt.md +2 -1
  67. package/components/datatable/datatable.card.html +20 -16
  68. package/components/dialog/Dialog.jsx +13 -45
  69. package/components/dialog/dialog.card.html +3 -3
  70. package/components/feedback/Alert.d.ts +2 -0
  71. package/components/feedback/Alert.jsx +8 -2
  72. package/components/feedback/EmptyState.d.ts +2 -0
  73. package/components/feedback/EmptyState.jsx +6 -3
  74. package/components/feedback/ProgressBar.d.ts +2 -0
  75. package/components/feedback/ProgressBar.jsx +9 -4
  76. package/components/feedback/Toast.jsx +15 -1
  77. package/components/feedback/Toast.prompt.md +2 -1
  78. package/components/feedback/feedback-2.card.html +3 -3
  79. package/components/feedback/feedback.card.html +3 -3
  80. package/components/forms/Cascader.jsx +76 -8
  81. package/components/forms/Editor.jsx +37 -2
  82. package/components/forms/FileUpload.d.ts +2 -0
  83. package/components/forms/FileUpload.jsx +22 -8
  84. package/components/forms/Form.d.ts +3 -1
  85. package/components/forms/Form.jsx +9 -1
  86. package/components/forms/Form.prompt.md +1 -1
  87. package/components/forms/Mentions.d.ts +1 -1
  88. package/components/forms/Mentions.jsx +59 -7
  89. package/components/forms/Mentions.prompt.md +3 -1
  90. package/components/forms/Rating.d.ts +3 -2
  91. package/components/forms/Rating.jsx +34 -3
  92. package/components/forms/Rating.prompt.md +1 -1
  93. package/components/forms/Select.d.ts +2 -0
  94. package/components/forms/Select.jsx +6 -2
  95. package/components/forms/Slider.d.ts +2 -0
  96. package/components/forms/Slider.jsx +5 -2
  97. package/components/forms/Switch.d.ts +2 -0
  98. package/components/forms/Switch.jsx +15 -3
  99. package/components/forms/TimePicker.jsx +3 -1
  100. package/components/forms/Toggle.d.ts +2 -0
  101. package/components/forms/Toggle.jsx +14 -2
  102. package/components/forms/forms-2.card.html +3 -3
  103. package/components/forms/forms.card.html +3 -3
  104. package/components/icon/Icon.d.ts +1 -1
  105. package/components/icon/Icon.jsx +1 -0
  106. package/components/icon/Icon.prompt.md +1 -1
  107. package/components/icon/icon.card.html +3 -3
  108. package/components/logo/logo.card.html +3 -3
  109. package/components/navigation/CommandPalette.d.ts +2 -0
  110. package/components/navigation/CommandPalette.jsx +22 -7
  111. package/components/navigation/Menu.d.ts +2 -0
  112. package/components/navigation/Menu.jsx +60 -6
  113. package/components/navigation/Steps.d.ts +2 -0
  114. package/components/navigation/Steps.jsx +6 -3
  115. package/components/navigation/Tabs.d.ts +2 -0
  116. package/components/navigation/Tabs.jsx +6 -3
  117. package/components/navigation/Toolbar.d.ts +3 -2
  118. package/components/navigation/Toolbar.jsx +119 -12
  119. package/components/navigation/Toolbar.prompt.md +1 -1
  120. package/components/navigation/navigation.card.html +3 -3
  121. package/components/navigation/shell.card.html +3 -3
  122. package/components/overlays/AlertDialog.jsx +19 -49
  123. package/components/overlays/ContextMenu.d.ts +2 -0
  124. package/components/overlays/ContextMenu.jsx +6 -3
  125. package/components/overlays/Drawer.d.ts +2 -0
  126. package/components/overlays/Drawer.jsx +24 -20
  127. package/components/overlays/OverlayManager.d.ts +40 -0
  128. package/components/overlays/OverlayManager.jsx +214 -0
  129. package/components/overlays/OverlayManager.prompt.md +12 -0
  130. package/components/overlays/Tour.jsx +14 -7
  131. package/components/overlays/alertdialog.card.html +3 -3
  132. package/components/overlays/overlays.card.html +3 -3
  133. package/components/textfield/textfield.card.html +3 -3
  134. package/dist/styles.min.css +2252 -0
  135. package/docs/benchmark-rubric.md +1 -1
  136. package/docs/ci-cd.md +19 -12
  137. package/docs/consumer-grant.md +3 -0
  138. package/docs/consuming.md +13 -13
  139. package/docs/contrast-report.md +79 -1
  140. package/docs/conventions.md +21 -8
  141. package/docs/decisions.md +45 -7
  142. package/docs/deploy.md +17 -6
  143. package/docs/design-styles.md +2 -2
  144. package/docs/quality-gates.md +35 -15
  145. package/docs/release-notes.md +2 -2
  146. package/docs/release-runbook.md +78 -0
  147. package/docs/sync.md +2 -2
  148. package/docs/vi/benchmark-rubric.md +1 -1
  149. package/docs/vi/ci-cd.md +17 -12
  150. package/docs/vi/consumer-grant.md +3 -0
  151. package/docs/vi/consuming.md +13 -13
  152. package/docs/vi/contrast-report.md +79 -1
  153. package/docs/vi/conventions.md +20 -7
  154. package/docs/vi/decisions.md +43 -6
  155. package/docs/vi/deploy.md +17 -6
  156. package/docs/vi/quality-gates.md +34 -15
  157. package/docs/vi/release-notes.md +2 -2
  158. package/docs/vi/release-runbook.md +78 -0
  159. package/docs/vi/sync.md +2 -2
  160. package/docs/viewer.html +2 -2
  161. package/fonts/OFL.txt +98 -0
  162. package/guidelines/atomic-view.html +13 -9
  163. package/guidelines/brand-imagery.html +1 -1
  164. package/guidelines/elements-variants.html +5 -5
  165. package/guidelines/spacing-breakpoints.html +2 -1
  166. package/guidelines/surfaces-glass.html +1 -1
  167. package/llms.txt +5 -5
  168. package/package.json +38 -10
  169. package/templates/_ds-base.canonical.js +55 -0
  170. package/templates/ai-cluster-demo.html +2 -2
  171. package/templates/app-shell/AppShell.dc.html +7 -5
  172. package/templates/app-shell/content-schema.json +7 -0
  173. package/templates/app-shell/ds-base.js +33 -1
  174. package/templates/app-shell/support.js +13 -20
  175. package/templates/article/Article.dc.html +7 -3
  176. package/templates/article/content-schema.json +7 -0
  177. package/templates/article/ds-base.js +33 -1
  178. package/templates/article/support.js +13 -20
  179. package/templates/auth/Auth.dc.html +33 -12
  180. package/templates/auth/content-schema.json +37 -0
  181. package/templates/auth/ds-base.js +33 -1
  182. package/templates/auth/support.js +13 -20
  183. package/templates/bod-deck/BodDeck.dc.html +11 -10
  184. package/templates/bod-deck/ds-base.js +33 -1
  185. package/templates/bod-deck/support.js +13 -20
  186. package/templates/bod-investor-update/BodInvestorUpdate.dc.html +4 -3
  187. package/templates/bod-investor-update/ds-base.js +33 -1
  188. package/templates/bod-investor-update/support.js +13 -20
  189. package/templates/bod-memo/BodMemo.dc.html +6 -5
  190. package/templates/bod-memo/ds-base.js +33 -1
  191. package/templates/bod-memo/support.js +13 -20
  192. package/templates/bod-minutes/BodMinutes.dc.html +2 -1
  193. package/templates/bod-minutes/ds-base.js +33 -1
  194. package/templates/bod-minutes/support.js +13 -20
  195. package/templates/bod-report/BodReport.dc.html +3 -2
  196. package/templates/bod-report/ds-base.js +33 -1
  197. package/templates/bod-report/support.js +13 -20
  198. package/templates/bod-resolution/BodResolution.dc.html +5 -4
  199. package/templates/bod-resolution/ds-base.js +33 -1
  200. package/templates/bod-resolution/support.js +13 -20
  201. package/templates/culture-event-invite/CultureEventInvite.dc.html +3 -2
  202. package/templates/culture-event-invite/ds-base.js +33 -1
  203. package/templates/culture-event-invite/support.js +13 -20
  204. package/templates/dashboard/Dashboard.dc.html +10 -6
  205. package/templates/dashboard/content-schema.json +7 -0
  206. package/templates/dashboard/ds-base.js +33 -1
  207. package/templates/dashboard/support.js +13 -20
  208. package/templates/delivery-change-note/DeliveryChangeNote.dc.html +4 -3
  209. package/templates/delivery-change-note/ds-base.js +33 -1
  210. package/templates/delivery-change-note/support.js +13 -20
  211. package/templates/delivery-kickoff/DeliveryKickoff.dc.html +3 -2
  212. package/templates/delivery-kickoff/ds-base.js +33 -1
  213. package/templates/delivery-kickoff/support.js +13 -20
  214. package/templates/delivery-qbr-deck/DeliveryQbrDeck.dc.html +6 -5
  215. package/templates/delivery-qbr-deck/ds-base.js +33 -1
  216. package/templates/delivery-qbr-deck/support.js +13 -20
  217. package/templates/delivery-runbook/DeliveryRunbook.dc.html +5 -4
  218. package/templates/delivery-runbook/ds-base.js +33 -1
  219. package/templates/delivery-runbook/support.js +13 -20
  220. package/templates/delivery-status-email/DeliveryStatusEmail.dc.html +3 -2
  221. package/templates/delivery-status-email/ds-base.js +33 -1
  222. package/templates/delivery-status-email/support.js +13 -20
  223. package/templates/doc-style-guide/DocStyleGuide.dc.html +67 -64
  224. package/templates/doc-style-guide/ds-base.js +33 -1
  225. package/templates/doc-style-guide/support.js +13 -20
  226. package/templates/doc-suite-index/DocSuiteIndex.dc.html +46 -45
  227. package/templates/doc-suite-index/ds-base.js +33 -1
  228. package/templates/doc-suite-index/support.js +13 -20
  229. package/templates/doc-templates/DocTemplates.dc.html +42 -41
  230. package/templates/doc-templates/ds-base.js +33 -1
  231. package/templates/doc-templates/support.js +13 -20
  232. package/templates/email/Email.dc.html +3 -2
  233. package/templates/email/ds-base.js +33 -1
  234. package/templates/email/support.js +13 -20
  235. package/templates/email-safe/email-announcement.html +2 -2
  236. package/templates/email-safe/email-dunning.html +2 -2
  237. package/templates/email-safe/email-investor.html +2 -2
  238. package/templates/email-safe/email-launch.html +2 -2
  239. package/templates/email-safe/email-newsletter.html +2 -2
  240. package/templates/email-safe/email-status.html +2 -2
  241. package/templates/email-safe/email-transactional.html +2 -2
  242. package/templates/finance-dunning-email/FinanceDunningEmail.dc.html +6 -5
  243. package/templates/finance-dunning-email/ds-base.js +33 -1
  244. package/templates/finance-dunning-email/support.js +13 -20
  245. package/templates/finance-expense-report/FinanceExpenseReport.dc.html +4 -3
  246. package/templates/finance-expense-report/ds-base.js +33 -1
  247. package/templates/finance-expense-report/support.js +13 -20
  248. package/templates/finance-invoice/FinanceInvoice.dc.html +6 -5
  249. package/templates/finance-invoice/ds-base.js +33 -1
  250. package/templates/finance-invoice/support.js +13 -20
  251. package/templates/finance-quote/FinanceQuote.dc.html +6 -5
  252. package/templates/finance-quote/ds-base.js +33 -1
  253. package/templates/finance-quote/support.js +13 -20
  254. package/templates/hr-announcement/HrAnnouncement.dc.html +5 -4
  255. package/templates/hr-announcement/ds-base.js +33 -1
  256. package/templates/hr-announcement/support.js +13 -20
  257. package/templates/hr-interview-kit/HrInterviewKit.dc.html +5 -4
  258. package/templates/hr-interview-kit/ds-base.js +33 -1
  259. package/templates/hr-interview-kit/support.js +13 -20
  260. package/templates/hr-offboarding/HrOffboarding.dc.html +2 -1
  261. package/templates/hr-offboarding/ds-base.js +33 -1
  262. package/templates/hr-offboarding/support.js +13 -20
  263. package/templates/hr-performance-review/HrPerformanceReview.dc.html +2 -1
  264. package/templates/hr-performance-review/ds-base.js +33 -1
  265. package/templates/hr-performance-review/support.js +13 -20
  266. package/templates/hr-pip/HrPip.dc.html +2 -1
  267. package/templates/hr-pip/ds-base.js +33 -1
  268. package/templates/hr-pip/support.js +13 -20
  269. package/templates/kitchen-sink.html +9 -4
  270. package/templates/legal-msa/LegalMsa.dc.html +2 -1
  271. package/templates/legal-msa/ds-base.js +33 -1
  272. package/templates/legal-msa/support.js +13 -20
  273. package/templates/legal-sow/LegalSow.dc.html +2 -1
  274. package/templates/legal-sow/ds-base.js +33 -1
  275. package/templates/legal-sow/support.js +13 -20
  276. package/templates/marketing-campaign-brief/MarketingCampaignBrief.dc.html +3 -2
  277. package/templates/marketing-campaign-brief/ds-base.js +33 -1
  278. package/templates/marketing-campaign-brief/support.js +13 -20
  279. package/templates/marketing-case-study/MarketingCaseStudy.dc.html +6 -5
  280. package/templates/marketing-case-study/ds-base.js +33 -1
  281. package/templates/marketing-case-study/support.js +13 -20
  282. package/templates/marketing-launch/MarketingLaunch.dc.html +6 -5
  283. package/templates/marketing-launch/ds-base.js +33 -1
  284. package/templates/marketing-launch/support.js +13 -20
  285. package/templates/marketing-newsletter/MarketingNewsletter.dc.html +13 -12
  286. package/templates/marketing-newsletter/ds-base.js +33 -1
  287. package/templates/marketing-newsletter/support.js +13 -20
  288. package/templates/marketing-page/MarketingPage.dc.html +19 -14
  289. package/templates/marketing-page/content-schema.json +1 -0
  290. package/templates/marketing-page/ds-base.js +33 -1
  291. package/templates/marketing-page/support.js +13 -20
  292. package/templates/marketing-press-release/MarketingPressRelease.dc.html +2 -1
  293. package/templates/marketing-press-release/ds-base.js +33 -1
  294. package/templates/marketing-press-release/support.js +13 -20
  295. package/templates/marketing-social-kit/MarketingSocialKit.dc.html +5 -4
  296. package/templates/marketing-social-kit/ds-base.js +33 -1
  297. package/templates/marketing-social-kit/support.js +13 -20
  298. package/templates/sales-one-pager/SalesOnePager.dc.html +5 -4
  299. package/templates/sales-one-pager/ds-base.js +33 -1
  300. package/templates/sales-one-pager/support.js +13 -20
  301. package/templates/sales-proposal-deck/SalesProposalDeck.dc.html +7 -6
  302. package/templates/sales-proposal-deck/ds-base.js +33 -1
  303. package/templates/sales-proposal-deck/support.js +13 -20
  304. package/templates/settings/Settings.dc.html +7 -3
  305. package/templates/settings/content-schema.json +7 -0
  306. package/templates/settings/ds-base.js +33 -1
  307. package/templates/settings/support.js +13 -20
  308. package/templates/slide-deck/SlideDeck.dc.html +5 -4
  309. package/templates/slide-deck/ds-base.js +33 -1
  310. package/templates/slide-deck/support.js +13 -20
  311. package/templates/team-meeting-agenda/TeamMeetingAgenda.dc.html +2 -1
  312. package/templates/team-meeting-agenda/ds-base.js +33 -1
  313. package/templates/team-meeting-agenda/support.js +13 -20
  314. package/templates/tech-incident-report/TechIncidentReport.dc.html +3 -2
  315. package/templates/tech-incident-report/ds-base.js +33 -1
  316. package/templates/tech-incident-report/support.js +13 -20
  317. package/templates/tech-release-notes/TechReleaseNotes.dc.html +6 -5
  318. package/templates/tech-release-notes/ds-base.js +33 -1
  319. package/templates/tech-release-notes/support.js +13 -20
  320. package/templates/tech-retro/TechRetro.dc.html +4 -3
  321. package/templates/tech-retro/ds-base.js +33 -1
  322. package/templates/tech-retro/support.js +13 -20
  323. package/templates/tech-rfc/TechRfc.dc.html +3 -2
  324. package/templates/tech-rfc/ds-base.js +33 -1
  325. package/templates/tech-rfc/support.js +13 -20
  326. package/templates/vn-appointment-transfer/VnAppointmentTransfer.dc.html +36 -35
  327. package/templates/vn-appointment-transfer/ds-base.js +33 -1
  328. package/templates/vn-appointment-transfer/support.js +13 -20
  329. package/templates/vn-certificate-of-employment/VnCertificateOfEmployment.dc.html +27 -26
  330. package/templates/vn-certificate-of-employment/ds-base.js +33 -1
  331. package/templates/vn-certificate-of-employment/support.js +13 -20
  332. package/templates/vn-code-of-conduct/VnCodeOfConduct.dc.html +44 -43
  333. package/templates/vn-code-of-conduct/ds-base.js +33 -1
  334. package/templates/vn-code-of-conduct/support.js +13 -20
  335. package/templates/vn-compensation-regulation/VnCompensationRegulation.dc.html +53 -52
  336. package/templates/vn-compensation-regulation/ds-base.js +33 -1
  337. package/templates/vn-compensation-regulation/support.js +13 -20
  338. package/templates/vn-contract-amendment/VnContractAmendment.dc.html +41 -40
  339. package/templates/vn-contract-amendment/ds-base.js +33 -1
  340. package/templates/vn-contract-amendment/support.js +13 -20
  341. package/templates/vn-contractor-agreement/VnContractorAgreement.dc.html +50 -49
  342. package/templates/vn-contractor-agreement/ds-base.js +33 -1
  343. package/templates/vn-contractor-agreement/support.js +13 -20
  344. package/templates/vn-data-protection-policy/VnDataProtectionPolicy.dc.html +54 -53
  345. package/templates/vn-data-protection-policy/ds-base.js +33 -1
  346. package/templates/vn-data-protection-policy/support.js +13 -20
  347. package/templates/vn-disciplinary-case-file/VnDisciplinaryCaseFile.dc.html +72 -71
  348. package/templates/vn-disciplinary-case-file/ds-base.js +33 -1
  349. package/templates/vn-disciplinary-case-file/support.js +13 -20
  350. package/templates/vn-disciplinary-schedule/VnDisciplinarySchedule.dc.html +21 -20
  351. package/templates/vn-disciplinary-schedule/ds-base.js +33 -1
  352. package/templates/vn-disciplinary-schedule/support.js +13 -20
  353. package/templates/vn-employee-request/VnEmployeeRequest.dc.html +54 -53
  354. package/templates/vn-employee-request/ds-base.js +33 -1
  355. package/templates/vn-employee-request/support.js +13 -20
  356. package/templates/vn-exit-handover/VnExitHandover.dc.html +122 -121
  357. package/templates/vn-exit-handover/ds-base.js +33 -1
  358. package/templates/vn-exit-handover/support.js +13 -20
  359. package/templates/vn-foreign-employee-pack/VnForeignEmployeePack.dc.html +31 -30
  360. package/templates/vn-foreign-employee-pack/ds-base.js +33 -1
  361. package/templates/vn-foreign-employee-pack/support.js +13 -20
  362. package/templates/vn-grassroots-democracy/VnGrassrootsDemocracy.dc.html +54 -53
  363. package/templates/vn-grassroots-democracy/ds-base.js +33 -1
  364. package/templates/vn-grassroots-democracy/support.js +13 -20
  365. package/templates/vn-infosec-policy/VnInfosecPolicy.dc.html +50 -49
  366. package/templates/vn-infosec-policy/ds-base.js +33 -1
  367. package/templates/vn-infosec-policy/support.js +13 -20
  368. package/templates/vn-internal-labor-regulations/VnInternalLaborRegulations.dc.html +84 -83
  369. package/templates/vn-internal-labor-regulations/ds-base.js +33 -1
  370. package/templates/vn-internal-labor-regulations/support.js +13 -20
  371. package/templates/vn-internship-agreement/VnInternshipAgreement.dc.html +44 -43
  372. package/templates/vn-internship-agreement/ds-base.js +33 -1
  373. package/templates/vn-internship-agreement/support.js +13 -20
  374. package/templates/vn-job-description/VnJobDescription.dc.html +28 -27
  375. package/templates/vn-job-description/ds-base.js +33 -1
  376. package/templates/vn-job-description/support.js +13 -20
  377. package/templates/vn-labor-contract/VnLaborContract.dc.html +109 -108
  378. package/templates/vn-labor-contract/ds-base.js +33 -1
  379. package/templates/vn-labor-contract/support.js +13 -20
  380. package/templates/vn-labour-management-book/VnLabourManagementBook.dc.html +32 -31
  381. package/templates/vn-labour-management-book/ds-base.js +33 -1
  382. package/templates/vn-labour-management-book/support.js +13 -20
  383. package/templates/vn-mutual-nda/VnMutualNda.dc.html +45 -44
  384. package/templates/vn-mutual-nda/ds-base.js +33 -1
  385. package/templates/vn-mutual-nda/support.js +13 -20
  386. package/templates/vn-ndnca-ip/VnNdncaIp.dc.html +73 -72
  387. package/templates/vn-ndnca-ip/ds-base.js +33 -1
  388. package/templates/vn-ndnca-ip/support.js +13 -20
  389. package/templates/vn-offer-letter/VnOfferLetter.dc.html +32 -31
  390. package/templates/vn-offer-letter/ds-base.js +33 -1
  391. package/templates/vn-offer-letter/support.js +13 -20
  392. package/templates/vn-onboarding-checklist/VnOnboardingChecklist.dc.html +16 -15
  393. package/templates/vn-onboarding-checklist/ds-base.js +33 -1
  394. package/templates/vn-onboarding-checklist/support.js +13 -20
  395. package/templates/vn-pdpl-consent/VnPdplConsent.dc.html +43 -42
  396. package/templates/vn-pdpl-consent/ds-base.js +33 -1
  397. package/templates/vn-pdpl-consent/support.js +13 -20
  398. package/templates/vn-performance-evaluation-regulation/VnPerformanceEvaluationRegulation.dc.html +65 -64
  399. package/templates/vn-performance-evaluation-regulation/ds-base.js +33 -1
  400. package/templates/vn-performance-evaluation-regulation/support.js +13 -20
  401. package/templates/vn-performance-review-form/VnPerformanceReviewForm.dc.html +49 -48
  402. package/templates/vn-performance-review-form/ds-base.js +33 -1
  403. package/templates/vn-performance-review-form/support.js +13 -20
  404. package/templates/vn-phantom-stock/VnPhantomStock.dc.html +41 -40
  405. package/templates/vn-phantom-stock/ds-base.js +33 -1
  406. package/templates/vn-phantom-stock/support.js +13 -20
  407. package/templates/vn-probation-contract/VnProbationContract.dc.html +56 -55
  408. package/templates/vn-probation-contract/ds-base.js +33 -1
  409. package/templates/vn-probation-contract/support.js +13 -20
  410. package/templates/vn-receipt-acknowledgement/VnReceiptAcknowledgement.dc.html +28 -27
  411. package/templates/vn-receipt-acknowledgement/ds-base.js +33 -1
  412. package/templates/vn-receipt-acknowledgement/support.js +13 -20
  413. package/templates/vn-remote-work-policy/VnRemoteWorkPolicy.dc.html +44 -43
  414. package/templates/vn-remote-work-policy/ds-base.js +33 -1
  415. package/templates/vn-remote-work-policy/support.js +13 -20
  416. package/templates/vn-resignation-letter/VnResignationLetter.dc.html +20 -19
  417. package/templates/vn-resignation-letter/ds-base.js +33 -1
  418. package/templates/vn-resignation-letter/support.js +13 -20
  419. package/templates/vn-salary-scale/VnSalaryScale.dc.html +39 -38
  420. package/templates/vn-salary-scale/ds-base.js +33 -1
  421. package/templates/vn-salary-scale/support.js +13 -20
  422. package/templates/vn-sow-appendix/VnSowAppendix.dc.html +21 -20
  423. package/templates/vn-sow-appendix/ds-base.js +33 -1
  424. package/templates/vn-sow-appendix/support.js +13 -20
  425. package/templates/vn-termination-decision/VnTerminationDecision.dc.html +25 -24
  426. package/templates/vn-termination-decision/ds-base.js +33 -1
  427. package/templates/vn-termination-decision/support.js +13 -20
  428. package/templates/vn-total-rewards-appendix/VnTotalRewardsAppendix.dc.html +63 -62
  429. package/templates/vn-total-rewards-appendix/ds-base.js +33 -1
  430. package/templates/vn-total-rewards-appendix/support.js +13 -20
  431. package/templates/vn-training-commitment/VnTrainingCommitment.dc.html +39 -38
  432. package/templates/vn-training-commitment/ds-base.js +33 -1
  433. package/templates/vn-training-commitment/support.js +13 -20
  434. package/templates/vn-travel-expense-policy/VnTravelExpensePolicy.dc.html +50 -49
  435. package/templates/vn-travel-expense-policy/ds-base.js +33 -1
  436. package/templates/vn-travel-expense-policy/support.js +13 -20
  437. package/tokens/colors.css +19 -1
  438. package/tokens/component-tokens.css +1 -0
  439. package/tokens/element-seeds.json +2 -2
  440. package/tokens/elements.css +228 -21
  441. package/tokens/elevation.css +8 -2
  442. package/tokens/native/CSTokens.kt +37 -2
  443. package/tokens/native/CSTokens.swift +37 -2
  444. package/tokens/native/cs_tokens.dart +37 -2
  445. package/tokens/provenance.json +12 -12
  446. package/tokens/spacing.css +4 -1
  447. package/tokens/tokens.dtcg.json +129 -16
  448. package/tokens/tokens.js +59 -18
  449. package/tokens/tokens.json +58 -17
  450. package/tokens/typography.css +2 -0
  451. package/ui_kits/deck/index.html +4 -0
  452. package/ui_kits/status-hub/StatusHub.jsx +30 -4
  453. package/ui_kits/status-hub/index.html +3 -3
  454. package/ui_kits/status-hub/login.html +55 -9
  455. package/ui_kits/status-hub/project.html +4 -0
  456. package/ui_kits/status-hub/settings.html +35 -13
  457. package/ui_kits/status-hub/status.css +5 -1
  458. package/ui_kits/website/Website.jsx +25 -3
  459. package/ui_kits/website/careers.html +5 -1
  460. package/ui_kits/website/chat.html +5 -3
  461. package/ui_kits/website/index.html +3 -3
  462. package/ui_kits/website/site.css +3 -3
  463. package/ui_kits/website/work.html +5 -1
  464. package/assets/aurora-hoa.png +0 -0
  465. package/assets/aurora-kim.png +0 -0
  466. package/assets/aurora-moc.png +0 -0
  467. package/assets/aurora-thuy.png +0 -0
  468. package/docs/status/assets/favicon.svg +0 -5
  469. package/docs/status/assets/status-legacy.css +0 -399
  470. package/docs/status/assets/status-legacy.js +0 -505
  471. package/docs/status/assets/status.css +0 -644
  472. package/docs/status/assets/status.js +0 -1308
  473. package/docs/status/data/status-feed.json +0 -1
  474. package/docs/status/data/task/TASK-REL-001.js +0 -1
  475. package/docs/status/data/task/TASK-REL-002.js +0 -1
  476. package/docs/status/index.html +0 -42
  477. package/docs/status/roadmap.html +0 -4
  478. package/docs/status/status-legacy.html +0 -117
  479. package/docs/tasks/BACKLOG.md +0 -26
  480. package/docs/tasks/release/TASK-REL-001-launch-1-1-0/spec.md +0 -55
  481. package/docs/tasks/release/TASK-REL-002-republish-1-1-1/spec.md +0 -54
@@ -9,6 +9,8 @@ export interface FileUploadProps {
9
9
  multiple?: boolean;
10
10
  onFiles?: (files: File[]) => void;
11
11
  icon?: ReactNode;
12
+ /** "en" | "vi" — else resolved from the nearest [lang] ancestor (vi default). */
13
+ lang?: string;
12
14
  className?: string;
13
15
  }
14
16
  export function FileUpload(props: FileUploadProps): React.ReactElement;
@@ -2,28 +2,42 @@ import React from "react";
2
2
  import { makeT, useLang } from "../_i18n/i18n.js";
3
3
  import { cx } from "../_utils/cx.js";
4
4
 
5
- /** CyberSkill FileUpload — click-or-drag dropzone. Calls onFiles(File[]) on select/drop. */
5
+ /** CyberSkill FileUpload — click-or-drag dropzone. Calls onFiles(File[]) on select/drop.
6
+ * Uses a single labeled <input type="file"> (no nested interactive) for axe name/role. */
6
7
  export function FileUpload({ title, hint, accept, multiple = false, onFiles, icon, lang, className }) {
7
8
  const [drag, setDrag] = React.useState(false);
9
+ const inputRef = React.useRef(null);
8
10
  const pick = (files) => { if (files && files.length && onFiles) onFiles(Array.from(files)); };
9
11
  const [ref, L] = useLang(lang);
10
12
  const t = makeT("FileUpload", L);
11
13
  const tt = title != null ? title : t("title");
12
14
  const hh = hint != null ? hint : t("hint");
15
+ const id = React.useId();
13
16
  return (
14
- <label
17
+ <div
15
18
  ref={ref}
16
19
  className={cx("cs-dropzone", drag && "is-dragging", className)}
17
20
  onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
18
21
  onDragLeave={() => setDrag(false)}
19
22
  onDrop={(e) => { e.preventDefault(); setDrag(false); pick(e.dataTransfer.files); }}
23
+ onClick={() => inputRef.current && inputRef.current.click()}
20
24
  >
21
- <span className="cs-dropzone__icon">
22
- {icon ?? <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M12 16V4M7 9l5-5 5 5" /><path d="M5 16v3a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3" /></svg>}
25
+ <span className="cs-dropzone__icon" aria-hidden="true">
26
+ {icon ?? <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M12 16V4M7 9l5-5 5 5" /><path d="M5 16v3a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3" /></svg>}
23
27
  </span>
24
- <span className="cs-dropzone__title">{tt}</span>
25
- <span className="cs-dropzone__hint">{hh}</span>
26
- <input type="file" accept={accept} multiple={multiple} style={{ display: "none" }} onChange={(e) => pick(e.target.files)} />
27
- </label>
28
+ <label className="cs-dropzone__title" htmlFor={id}>{tt}</label>
29
+ <span className="cs-dropzone__hint" id={id + "-hint"}>{hh}</span>
30
+ <input
31
+ id={id}
32
+ ref={inputRef}
33
+ type="file"
34
+ accept={accept}
35
+ multiple={multiple}
36
+ className="cs-sr-only"
37
+ aria-describedby={id + "-hint"}
38
+ onChange={(e) => pick(e.target.files)}
39
+ onClick={(e) => e.stopPropagation()}
40
+ />
41
+ </div>
28
42
  );
29
43
  }
@@ -13,7 +13,9 @@ export interface FormProps {
13
13
  errors?: Record<string, string | undefined>;
14
14
  /** name → rule or rule[]; validated on submit. */
15
15
  rules?: Record<string, FormRule | FormRule[]>;
16
- /** name → async (value, values) => message | null; runs after sync rules pass. */
16
+ /** name → async (value, values) => message | null; runs after sync rules pass.
17
+ * While awaiting, Form sets `is-pending` / `aria-busy`, disables controls via CSS,
18
+ * and ignores duplicate submits. Context exposes `pending: boolean`. */
17
19
  asyncRules?: Record<string, (value: unknown, values: Record<string, unknown>) => Promise<string | null | undefined>>;
18
20
  /** Seed for controller-registered fields. */
19
21
  initialValues?: Record<string, unknown>;
@@ -55,6 +55,7 @@ export function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues,
55
55
  const [values, setValues] = React.useState(() => initialValues || {});
56
56
  const [ruleErrors, setRuleErrors] = React.useState({});
57
57
  const [pending, setPending] = React.useState(false);
58
+ const pendingRef = React.useRef(false);
58
59
  const setValue = React.useCallback((name, v) => {
59
60
  setValues((s) => (String(name).includes(".") ? setPath(s, name, v) : { ...s, [name]: v }));
60
61
  setRuleErrors((e) => (e[name] ? { ...e, [name]: undefined } : e));
@@ -96,20 +97,27 @@ export function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues,
96
97
  return (
97
98
  <Ctx.Provider value={ctx}>
98
99
  <form {...props} ref={ref} className={cx("cs-form", pending && "is-pending", className)} noValidate
100
+ aria-busy={pending || undefined}
99
101
  onSubmit={async (e) => {
100
102
  e.preventDefault();
103
+ // FIND-015 / FIND-044 — lock re-entry while async validation / submit runs.
104
+ if (pendingRef.current) return;
101
105
  const fd = new FormData(e.currentTarget);
102
106
  const v = {}; fd.forEach((val, k) => { v[k] = val; });
103
107
  Object.assign(v, values); // controller-registered fields are the source of truth
104
108
  const errs = runRules(v);
105
109
  if (Object.keys(errs).some((k) => errs[k])) { setRuleErrors(errs); return; }
110
+ pendingRef.current = true;
106
111
  setPending(true);
107
112
  try {
108
113
  const aerrs = await runAsyncRules(v);
109
114
  setRuleErrors(aerrs);
110
115
  if (Object.keys(aerrs).some((k) => aerrs[k])) return;
111
116
  onSubmit && onSubmit(v);
112
- } finally { setPending(false); }
117
+ } finally {
118
+ pendingRef.current = false;
119
+ setPending(false);
120
+ }
113
121
  }}>
114
122
  {keys.length ? (
115
123
  <div className="cs-form__summary" role="alert">
@@ -10,7 +10,7 @@
10
10
 
11
11
  Manual mode unchanged: omit `rules`/`name`, pass `errors={{email:"…"}}` yourself.
12
12
 
13
- **asyncRules:** optional map of field name → async `(value, values) => message|null`. Runs after sync `rules` pass; blocks submit while pending.
13
+ **asyncRules:** optional map of field name → async `(value, values) => message|null`. Runs after sync `rules` pass; Form emits `is-pending` + `aria-busy`, locks controls, and refuses re-entry until the async pass finishes (FIND-015 / FIND-044). Controllers can also read `pending` from Form context.
14
14
 
15
15
  **FormFieldArray** — manage an array of row objects under `name` on the surrounding Form. Children render-prop: `({ index, item, remove, path })`.
16
16
 
@@ -1,4 +1,4 @@
1
- /** Textarea with @user suggestions (typeahead on the trailing "@word"). */
1
+ /** Textarea with @user suggestions (APG editable combobox / listbox). */
2
2
  export interface MentionsProps {
3
3
  value?: string;
4
4
  defaultValue?: string;
@@ -2,28 +2,80 @@ import React from "react";
2
2
  import { makeT, useLang } from "../_i18n/i18n.js";
3
3
  import { cx } from "../_utils/cx.js";
4
4
 
5
- /** CyberSkill Mentions — textarea that suggests @users while typing. */
5
+ let mentionsUid = 0;
6
+
7
+ /** CyberSkill Mentions — textarea that suggests @users while typing (APG editable combobox). */
6
8
  export function Mentions({ value, defaultValue = "", onChange, users = [], placeholder, rows = 3, lang, className }) {
7
9
  const [inner, setInner] = React.useState(defaultValue);
8
10
  const val = value != null ? value : inner;
9
11
  const set = (v) => { if (value == null) setInner(v); onChange && onChange(v); };
10
12
  const [q, setQ] = React.useState(null);
13
+ const [activeIndex, setActiveIndex] = React.useState(0);
14
+ const [listId] = React.useState(() => "cs-mentions-" + (++mentionsUid));
11
15
  const [ref, L] = useLang(lang);
12
16
  const t = makeT("Mentions", L);
13
17
  const ph = placeholder != null ? placeholder : t("placeholder");
18
+ const hits = q == null ? [] : users.filter((u) => u.toLowerCase().includes(q)).slice(0, 6);
19
+ const open = hits.length > 0;
20
+
21
+ React.useEffect(() => {
22
+ setActiveIndex((i) => (hits.length ? Math.min(i, hits.length - 1) : 0));
23
+ }, [q, users]);
24
+
14
25
  const onInput = (v) => {
15
26
  set(v);
16
27
  const m = /(^|\s)@(\w*)$/.exec(v);
17
- setQ(m ? m[2].toLowerCase() : null);
28
+ const next = m ? m[2].toLowerCase() : null;
29
+ setQ(next);
30
+ if (next != null) setActiveIndex(0);
18
31
  };
19
- const hits = q == null ? [] : users.filter((u) => u.toLowerCase().includes(q)).slice(0, 6);
20
32
  const pick = (u) => { set(val.replace(/(^|\s)@\w*$/, "$1@" + u + " ")); setQ(null); };
33
+ const optionId = (i) => listId + "-opt-" + i;
34
+ const onKeyDown = (e) => {
35
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
36
+ if (!open) return;
37
+ if (e.key === "ArrowDown") {
38
+ e.preventDefault();
39
+ setActiveIndex((i) => Math.min(hits.length - 1, i + 1));
40
+ } else if (e.key === "ArrowUp") {
41
+ e.preventDefault();
42
+ setActiveIndex((i) => Math.max(0, i - 1));
43
+ } else if (e.key === "Enter") {
44
+ if (hits[activeIndex]) { e.preventDefault(); pick(hits[activeIndex]); }
45
+ } else if (e.key === "Escape") {
46
+ e.preventDefault();
47
+ setQ(null);
48
+ }
49
+ };
21
50
  return (
22
51
  <span ref={ref} className={cx("cs-mentions", className)}>
23
- <textarea className="cs-field__control" rows={rows} value={val} placeholder={ph} onChange={(e) => onInput(e.target.value)} />
24
- {hits.length ? (
25
- <span className="cs-mentions__pop" role="listbox">
26
- {hits.map((u) => <button key={u} type="button" role="option" onMouseDown={(e) => { e.preventDefault(); pick(u); }}>@{u}</button>)}
52
+ <textarea
53
+ className="cs-field__control"
54
+ rows={rows}
55
+ value={val}
56
+ placeholder={ph}
57
+ role="combobox"
58
+ aria-autocomplete="list"
59
+ aria-expanded={open}
60
+ aria-controls={listId}
61
+ aria-activedescendant={open && hits[activeIndex] ? optionId(activeIndex) : undefined}
62
+ onChange={(e) => onInput(e.target.value)}
63
+ onKeyDown={onKeyDown}
64
+ />
65
+ {open ? (
66
+ <span className="cs-mentions__pop" role="listbox" id={listId}>
67
+ {hits.map((u, i) => (
68
+ <button
69
+ key={u}
70
+ type="button"
71
+ id={optionId(i)}
72
+ role="option"
73
+ aria-selected={i === activeIndex}
74
+ className={cx(i === activeIndex && "hl")}
75
+ onMouseEnter={() => setActiveIndex(i)}
76
+ onMouseDown={(e) => { e.preventDefault(); pick(u); }}
77
+ >@{u}</button>
78
+ ))}
27
79
  </span>
28
80
  ) : null}
29
81
  </span>
@@ -1,4 +1,6 @@
1
- **Mentions** — textarea suggesting @users as you type.
1
+ **Mentions** — textarea suggesting @users as you type (APG editable combobox).
2
+
3
+ Keyboard: ArrowUp/Down move the active suggestion (`aria-activedescendant`); Enter selects; Escape closes. Mouse click still picks.
2
4
 
3
5
  ```jsx
4
6
  <Mentions users={["anle","baovu","lumi"]} placeholder="Add a note — @ to mention"/>
@@ -1,5 +1,6 @@
1
- /** Whole-star rating (radiogroup). Controlled (value/onChange) or uncontrolled
2
- * (defaultValue). Clicking the current value clears to 0. readOnly for display. */
1
+ /** Whole-star rating (APG radiogroup: roving tabindex + arrow keys). Controlled
2
+ * (value/onChange) or uncontrolled (defaultValue). Clicking the current value
3
+ * clears to 0. readOnly for display. */
3
4
  export interface RatingProps {
4
5
  value?: number;
5
6
  defaultValue?: number;
@@ -3,17 +3,48 @@ import { makeT, useLang } from "../_i18n/i18n.js";
3
3
  import { cx } from "../_utils/cx.js";
4
4
  const STAR = "M12 2l2.9 6.2 6.6.8-4.9 4.6 1.3 6.5L12 16.9 6.1 20l1.3-6.5L2.5 9l6.6-.8z";
5
5
 
6
- /** CyberSkill Rating — whole-star rating. Controlled or uncontrolled; readOnly for display. Click the current value to clear. */
6
+ /** CyberSkill Rating — whole-star rating (APG radiogroup: roving tabindex + arrows). Controlled or uncontrolled; readOnly for display. Click the current value to clear. */
7
7
  export function Rating({ value, defaultValue = 0, onChange, max = 5, readOnly = false, label, lang, className }) {
8
8
  const [inner, setInner] = React.useState(defaultValue);
9
9
  const val = value != null ? value : inner;
10
- const set = (n) => { if (readOnly) return; const v = n === val ? 0 : n; if (value == null) setInner(v); onChange && onChange(v); };
10
+ const commit = (n) => { if (readOnly) return; if (value == null) setInner(n); onChange && onChange(n); };
11
+ const set = (n) => commit(n === val ? 0 : n);
11
12
  const [ref, L] = useLang(lang);
12
13
  const lbl = label != null ? label : makeT("Rating", L)("label");
14
+ const refs = React.useRef([]);
15
+ const focusIdx = val > 0 ? Math.min(max, val) - 1 : 0;
16
+
17
+ const move = (from, delta) => {
18
+ if (readOnly) return;
19
+ const next = Math.max(0, Math.min(max - 1, from + delta));
20
+ commit(next + 1);
21
+ const b = refs.current[next];
22
+ if (b) b.focus();
23
+ };
24
+
25
+ const onKeyDown = (e, i) => {
26
+ if (readOnly || e.nativeEvent.isComposing || e.keyCode === 229) return;
27
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") { e.preventDefault(); move(i, 1); }
28
+ else if (e.key === "ArrowLeft" || e.key === "ArrowDown") { e.preventDefault(); move(i, -1); }
29
+ else if (e.key === "Home") { e.preventDefault(); move(i, -i); }
30
+ else if (e.key === "End") { e.preventDefault(); move(i, max - 1 - i); }
31
+ };
32
+
13
33
  return (
14
34
  <div ref={ref} className={cx("cs-rating", className)} role="radiogroup" aria-label={lbl + ": " + val + " / " + max} data-readonly={readOnly ? "true" : undefined}>
15
35
  {Array.from({ length: max }).map((_, i) => (
16
- <button key={i} type="button" role="radio" aria-checked={i < val} aria-label={(i + 1) + " / " + max} className={i < val ? "on" : undefined} onClick={() => set(i + 1)} tabIndex={readOnly ? -1 : 0}>
36
+ <button
37
+ key={i}
38
+ type="button"
39
+ role="radio"
40
+ aria-checked={i < val}
41
+ aria-label={(i + 1) + " / " + max}
42
+ className={i < val ? "on" : undefined}
43
+ onClick={() => set(i + 1)}
44
+ tabIndex={readOnly ? -1 : (i === focusIdx ? 0 : -1)}
45
+ ref={(el) => { refs.current[i] = el; }}
46
+ onKeyDown={(e) => onKeyDown(e, i)}
47
+ >
17
48
  <svg width="22" height="22" viewBox="0 0 24 24" fill={i < val ? "currentColor" : "none"} stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round" aria-hidden="true"><path d={STAR} /></svg>
18
49
  </button>
19
50
  ))}
@@ -1,4 +1,4 @@
1
- **Rating** — whole-star rating (ochre stars). Controlled or uncontrolled; `readOnly` for display; click current value to clear. Bilingual aria via the registry.
1
+ **Rating** — whole-star rating (ochre stars). APG radiogroup: one tab stop, ArrowLeft/Right (and Up/Down) change value. Controlled or uncontrolled; `readOnly` for display; click current value to clear. Bilingual aria via the registry.
2
2
 
3
3
  ```jsx
4
4
  <Rating defaultValue={4} onChange={setStars}/>
@@ -14,6 +14,8 @@ export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElemen
14
14
  /** Options as data; alternatively pass <option> children. */
15
15
  options?: SelectOption[];
16
16
  children?: ReactNode;
17
+ /** "en" | "vi" — else resolved from the nearest [lang] ancestor (vi default). */
18
+ lang?: string;
17
19
  }
18
20
 
19
21
  export function Select(props: SelectProps): React.ReactElement;
@@ -1,20 +1,24 @@
1
1
  import React from "react";
2
+ import { makeT, useLang } from "../_i18n/i18n.js";
2
3
  import { cx } from "../_utils/cx.js";
3
4
 
4
5
  /** CyberSkill Select — native <select> in the .cs-field frame with a chevron.
5
6
  * Pass `options` [{value,label}] or <option> children. */
6
- export function Select({ id, label, description, error, options, children, disabled = false, className, ...props }) {
7
+ export function Select({ id, label, description, error, options, children, disabled = false, lang, className, placeholder, ...props }) {
8
+ const [ref, L] = useLang(lang);
9
+ const t = makeT("Select", L);
7
10
  const gid = React.useId();
8
11
  const sid = id ?? gid;
9
12
  const descId = description ? sid + "-desc" : undefined;
10
13
  const errId = error ? sid + "-err" : undefined;
11
14
  const describedBy = [descId, errId].filter(Boolean).join(" ") || undefined;
12
15
  return (
13
- <label className={cx("cs-field", disabled && "is-disabled", error && "is-invalid", className)} htmlFor={sid}>
16
+ <label ref={ref} className={cx("cs-field", disabled && "is-disabled", error && "is-invalid", className)} htmlFor={sid}>
14
17
  {label ? <span className="cs-field__label">{label}</span> : null}
15
18
  {description ? <span id={descId} className="cs-field__description">{description}</span> : null}
16
19
  <span className="cs-select">
17
20
  <select {...props} id={sid} disabled={disabled} aria-invalid={error ? true : undefined} aria-describedby={describedBy} className="cs-field__control">
21
+ {placeholder != null || !children ? <option value="" disabled hidden>{placeholder ?? t("placeholder")}</option> : null}
18
22
  {options ? options.map((o) => <option key={o.value} value={o.value}>{o.label}</option>) : children}
19
23
  </select>
20
24
  <span className="cs-select__chevron" aria-hidden="true">
@@ -4,3 +4,5 @@ import type * as React from "react";
4
4
  * range props (min, max, step, value, defaultValue, onChange). */
5
5
  export interface SliderProps extends React.InputHTMLAttributes<HTMLInputElement> {}
6
6
  export function Slider(props: SliderProps): React.ReactElement;
7
+ /** "en" | "vi" — else resolved from the nearest [lang] ancestor (vi default). */
8
+ lang?: string;
@@ -1,8 +1,11 @@
1
1
  import React from "react";
2
+ import { makeT, useLang } from "../_i18n/i18n.js";
2
3
  import { cx } from "../_utils/cx.js";
3
4
 
4
5
  /** CyberSkill Slider — brand-tinted native range input. Pass native props (min/max/step/value/onChange). */
5
6
  // `children` is destructured but never rendered on purpose: keeps stray children out of {...props} → void <input>.
6
- export function Slider({ className, children, ...props }) {
7
- return <input type="range" className={cx("cs-slider", className)} {...props} />;
7
+ export function Slider({ className, children, lang, "aria-label": ariaLabel, ...props }) {
8
+ const [ref, L] = useLang(lang);
9
+ const t = makeT("Slider", L);
10
+ return <input ref={ref} type="range" className={cx("cs-slider", className)} aria-label={ariaLabel ?? t("label")} {...props} />;
8
11
  }
@@ -7,6 +7,8 @@ import type { ReactNode } from "react";
7
7
  */
8
8
  export interface SwitchProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> {
9
9
  label?: ReactNode;
10
+ /** "en" | "vi" — else resolved from the nearest [lang] ancestor (vi default). */
11
+ lang?: string;
10
12
  }
11
13
 
12
14
  export function Switch(props: SwitchProps): React.ReactElement;
@@ -1,12 +1,24 @@
1
1
  import React from "react";
2
+ import { makeT, useLang } from "../_i18n/i18n.js";
2
3
  import { cx } from "../_utils/cx.js";
3
4
 
4
5
  /** CyberSkill Switch — accessible toggle (role="switch"); Umber on, Ochre knob-track in dark. */
5
6
  // `children` is destructured but never rendered on purpose: keeps stray children out of {...props} → void <input>.
6
- export function Switch({ label, disabled = false, className, children, ...props }) {
7
+ export function Switch({ label, disabled = false, className, children, lang, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...props }) {
8
+ const [ref, L] = useLang(lang);
9
+ const t = makeT("Switch", L);
10
+ const named = !!(label || ariaLabel || ariaLabelledby);
11
+ const fallback = props.checked || props.defaultChecked ? t("on") : t("off");
7
12
  return (
8
- <label className={cx("cs-switch", disabled && "is-disabled", className)}>
9
- <input type="checkbox" role="switch" disabled={disabled} {...props} />
13
+ <label ref={ref} className={cx("cs-switch", disabled && "is-disabled", className)}>
14
+ <input
15
+ type="checkbox"
16
+ role="switch"
17
+ disabled={disabled}
18
+ aria-label={ariaLabel ?? (named ? undefined : fallback)}
19
+ aria-labelledby={ariaLabelledby}
20
+ {...props}
21
+ />
10
22
  <span className="cs-switch__track" aria-hidden="true" />
11
23
  {label ? <span className="cs-switch__label">{label}</span> : null}
12
24
  </label>
@@ -7,8 +7,10 @@ const CLOCK = <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke
7
7
  export function TimePicker({ value = "09:00", onChange, step = 30, label, disabled = false, lang, className }) {
8
8
  const [ref, L] = useLang(lang);
9
9
  const t = makeT("TimePicker", L);
10
+ const stepMin = Number(step);
11
+ const safeStep = Number.isFinite(stepMin) && stepMin > 0 ? stepMin : 30;
10
12
  const opts = [];
11
- for (let m = 0; m < 24 * 60; m += step) {
13
+ for (let m = 0; m < 24 * 60; m += safeStep) {
12
14
  opts.push(String(Math.floor(m / 60)).padStart(2, "0") + ":" + String(m % 60).padStart(2, "0"));
13
15
  }
14
16
  return (
@@ -11,6 +11,8 @@ export interface ToggleProps {
11
11
  icon?: ReactNode;
12
12
  children?: ReactNode;
13
13
  disabled?: boolean;
14
+ /** "en" | "vi" — else resolved from the nearest [lang] ancestor (vi default). */
15
+ lang?: string;
14
16
  className?: string;
15
17
  }
16
18
  export function Toggle(props: ToggleProps): React.ReactElement;
@@ -1,13 +1,25 @@
1
1
  import React from "react";
2
+ import { makeT, useLang } from "../_i18n/i18n.js";
2
3
  import { cx } from "../_utils/cx.js";
3
4
 
4
5
  /** CyberSkill Toggle — a single pressable on/off button (≠ Switch: no form semantics; ≠ SegmentedControl: standalone). */
5
- export function Toggle({ pressed, defaultPressed = false, onChange, icon, children, disabled = false, className, ...props }) {
6
+ export function Toggle({ pressed, defaultPressed = false, onChange, icon, children, disabled = false, lang, className, "aria-label": ariaLabel, ...props }) {
6
7
  const [inner, setInner] = React.useState(defaultPressed);
7
8
  const on = pressed != null ? pressed : inner;
9
+ const [ref, L] = useLang(lang);
10
+ const t = makeT("Toggle", L);
8
11
  const flip = () => { const v = !on; if (pressed == null) setInner(v); onChange && onChange(v); };
9
12
  return (
10
- <button type="button" className={cx("cs-toggle", className)} aria-pressed={on} disabled={disabled} onClick={flip} {...props}>
13
+ <button
14
+ ref={ref}
15
+ type="button"
16
+ className={cx("cs-toggle", className)}
17
+ aria-pressed={on}
18
+ aria-label={ariaLabel ?? (on ? t("pressed") : t("unpressed"))}
19
+ disabled={disabled}
20
+ onClick={flip}
21
+ {...props}
22
+ >
11
23
  {icon ? <span aria-hidden="true" style={{ display: "inline-flex" }}>{icon}</span> : null}
12
24
  {children}
13
25
  </button>
@@ -10,9 +10,9 @@
10
10
  .row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
11
11
  .lbl { width: 100%; font: 600 11px/1 var(--cs-font-family-ui); text-transform: uppercase; letter-spacing: .09em; color: var(--cs-color-text-muted); }
12
12
  </style>
13
- <script src="https://unpkg.com/umd-react@19.2.8/dist/react.development.js" integrity="sha384-r22tQd8qRIHDPIkmdXZcVtvPi1Tj2x1QaW2tmFQHEvHscATUnHQ5siTyxWw5PMUs" crossorigin="anonymous"></script>
14
- <script src="https://unpkg.com/umd-react@19.2.8/dist/react-dom.development.js" integrity="sha384-gJZYmumEmwqVmX3JVsLZq+4QrxoPGh/BW47Rk7SqNPCPFpTGnRtvmeRPJ7//ZDPk" crossorigin="anonymous"></script>
15
- <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
13
+ <script src="../../_vendor/react/react.production.min.js"></script>
14
+ <script src="../../_vendor/react/react-dom.production.min.js"></script>
15
+ <script src="../../_vendor/babel/babel.min.js"></script>
16
16
  <script src="../../_ds_bundle.js"></script>
17
17
  </head>
18
18
  <body>
@@ -9,9 +9,9 @@
9
9
  body { margin: 0; background: var(--cs-color-surface-page); padding: 22px 24px; }
10
10
  .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px 28px; max-width: 640px; align-items: start; }
11
11
  </style>
12
- <script src="https://unpkg.com/umd-react@19.2.8/dist/react.development.js" integrity="sha384-r22tQd8qRIHDPIkmdXZcVtvPi1Tj2x1QaW2tmFQHEvHscATUnHQ5siTyxWw5PMUs" crossorigin="anonymous"></script>
13
- <script src="https://unpkg.com/umd-react@19.2.8/dist/react-dom.development.js" integrity="sha384-gJZYmumEmwqVmX3JVsLZq+4QrxoPGh/BW47Rk7SqNPCPFpTGnRtvmeRPJ7//ZDPk" crossorigin="anonymous"></script>
14
- <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
12
+ <script src="../../_vendor/react/react.production.min.js"></script>
13
+ <script src="../../_vendor/react/react-dom.production.min.js"></script>
14
+ <script src="../../_vendor/babel/babel.min.js"></script>
15
15
  <script src="../../_ds_bundle.js"></script>
16
16
  </head>
17
17
  <body>
@@ -6,7 +6,7 @@ export type IconName =
6
6
  | "search" | "sliders" | "upload" | "download" | "calendar"
7
7
  | "user" | "plus" | "trash" | "external" | "menu"
8
8
  | "chevron-down" | "chevron-up" | "chevron-left" | "chevron-right"
9
- | "edit" | "copy" | "info" | "alert-triangle";
9
+ | "edit" | "copy" | "info" | "alert-triangle" | "pin";
10
10
 
11
11
  /**
12
12
  * CyberSkill line-icon renderer. One component drives the whole in-repo icon set
@@ -49,6 +49,7 @@ export const CS_ICONS = {
49
49
  copy: { viewBox: "0 0 24 24", els: [["rect", { x: 8, y: 8, width: 12, height: 12, rx: 2 }], ["path", { d: "M4 16V6a2 2 0 0 1 2-2h10" }]] },
50
50
  info: { viewBox: "0 0 24 24", els: [["circle", { cx: 12, cy: 12, r: 9 }], ["path", { d: "M12 11v5M12 7.75v.01" }]] },
51
51
  "alert-triangle": { viewBox: "0 0 24 24", els: [["path", { d: "M12 3.5l9 16H3z" }], ["path", { d: "M12 10v4M12 17.5v.01" }]] },
52
+ pin: { viewBox: "0 0 24 24", els: [["path", { d: "M12 21v-7" }], ["path", { d: "M8.5 3.5h7l-1.2 6.5h2.7L12 14.5 6.999 10h2.7z" }]] },
52
53
  };
53
54
 
54
55
  export function Icon({ name, size = "md", label, className, strokeWidth = 1.75, ...props }) {
@@ -5,4 +5,4 @@
5
5
  <Icon name="sparkle" size="lg" label="Wish" /> {/* meaningful */}
6
6
  ```
7
7
 
8
- Set: `close · sun · moon · arrow-right · check · sparkle · chat · sound-on · sound-off` + v2.0 extension `search · sliders · upload · download · calendar · user · plus · trash · external · menu` + v2.13 `chevron-down/up/left/right · edit · copy · info · alert-triangle` (same grammar). `sparkle` is the brand's "wish" glyph. Colour follows the surrounding text token — set `color` on a parent to recolour.
8
+ Set: `close · sun · moon · arrow-right · check · sparkle · chat · sound-on · sound-off` + v2.0 extension `search · sliders · upload · download · calendar · user · plus · trash · external · menu` + v2.13 `chevron-down/up/left/right · edit · copy · info · alert-triangle · pin` (same grammar). `sparkle` is the brand's "wish" glyph. Colour follows the surrounding text token — set `color` on a parent to recolour.
@@ -13,9 +13,9 @@
13
13
  .cell .g { color: var(--cs-color-brand-umber); }
14
14
  .cell:nth-child(6) .g { color: var(--cs-color-brand-ochre); }
15
15
  </style>
16
- <script src="https://unpkg.com/umd-react@19.2.8/dist/react.development.js" integrity="sha384-r22tQd8qRIHDPIkmdXZcVtvPi1Tj2x1QaW2tmFQHEvHscATUnHQ5siTyxWw5PMUs" crossorigin="anonymous"></script>
17
- <script src="https://unpkg.com/umd-react@19.2.8/dist/react-dom.development.js" integrity="sha384-gJZYmumEmwqVmX3JVsLZq+4QrxoPGh/BW47Rk7SqNPCPFpTGnRtvmeRPJ7//ZDPk" crossorigin="anonymous"></script>
18
- <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
16
+ <script src="../../_vendor/react/react.production.min.js"></script>
17
+ <script src="../../_vendor/react/react-dom.production.min.js"></script>
18
+ <script src="../../_vendor/babel/babel.min.js"></script>
19
19
  <script src="../../_ds_bundle.js"></script>
20
20
  </head>
21
21
  <body>
@@ -15,9 +15,9 @@
15
15
  .lockup b { font: 800 22px/1 var(--cs-font-family-ui); color: var(--cs-color-brand-umber); letter-spacing: -.01em; }
16
16
  .cap { font: 500 12px/1.4 var(--cs-font-family-ui); color: var(--cs-color-text-muted); }
17
17
  </style>
18
- <script src="https://unpkg.com/umd-react@19.2.8/dist/react.development.js" integrity="sha384-r22tQd8qRIHDPIkmdXZcVtvPi1Tj2x1QaW2tmFQHEvHscATUnHQ5siTyxWw5PMUs" crossorigin="anonymous"></script>
19
- <script src="https://unpkg.com/umd-react@19.2.8/dist/react-dom.development.js" integrity="sha384-gJZYmumEmwqVmX3JVsLZq+4QrxoPGh/BW47Rk7SqNPCPFpTGnRtvmeRPJ7//ZDPk" crossorigin="anonymous"></script>
20
- <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
18
+ <script src="../../_vendor/react/react.production.min.js"></script>
19
+ <script src="../../_vendor/react/react-dom.production.min.js"></script>
20
+ <script src="../../_vendor/babel/babel.min.js"></script>
21
21
  <script src="../../_ds_bundle.js"></script>
22
22
  </head>
23
23
  <body>
@@ -14,6 +14,8 @@ export interface CommandPaletteProps {
14
14
  onClose?: () => void;
15
15
  placeholder?: string;
16
16
  groups: CommandGroup[];
17
+ /** "en" | "vi" — else resolved from the nearest [lang] ancestor (vi default). */
18
+ lang?: string;
17
19
  className?: string;
18
20
  }
19
21
  export function CommandPalette(props: CommandPaletteProps): React.ReactElement | null;
@@ -1,23 +1,38 @@
1
1
  import React from "react";
2
2
  import { makeT, useLang } from "../_i18n/i18n.js";
3
3
  import { cx } from "../_utils/cx.js";
4
+ import { useOverlayLayer } from "../overlays/OverlayManager.jsx";
4
5
 
5
6
  /** CyberSkill CommandPalette — ⌘K overlay with search + grouped actions. Controlled via open/onClose. */
6
7
  export function CommandPalette({ open, onClose, placeholder, groups = [], lang, className }) {
7
8
  const [q, setQ] = React.useState("");
8
- React.useEffect(() => {
9
- if (!open) return;
10
- const onKey = (e) => { if (e.key === "Escape") onClose && onClose(); };
11
- document.addEventListener("keydown", onKey);
12
- return () => document.removeEventListener("keydown", onKey);
13
- }, [open]);
9
+ const panel = React.useRef(null);
10
+ const closeRef = React.useRef(onClose);
11
+ closeRef.current = onClose;
12
+
13
+ useOverlayLayer({
14
+ open: !!open,
15
+ kind: "modal",
16
+ trapFocus: true,
17
+ preferFocusSelector: ".cs-cmdk__search input",
18
+ onEscape: () => closeRef.current && closeRef.current(),
19
+ panelRef: panel,
20
+ });
21
+
14
22
  const [ref, L] = useLang(lang);
15
23
  const t = makeT("CommandPalette", L);
16
24
  const ph = placeholder != null ? placeholder : t("placeholder");
17
25
  if (!open) return null;
18
26
  const needle = q.trim().toLowerCase();
19
27
  return (
20
- <div ref={ref} className="cs-cmdk-scrim" onClick={onClose}>
28
+ <div
29
+ ref={(el) => {
30
+ ref.current = el;
31
+ panel.current = el;
32
+ }}
33
+ className="cs-cmdk-scrim"
34
+ onClick={onClose}
35
+ >
21
36
  <div className={cx("cs-cmdk", className)} role="dialog" aria-modal="true" aria-label={t("aria")} onClick={(e) => e.stopPropagation()}>
22
37
  <div className="cs-cmdk__search">
23
38
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3" /></svg>
@@ -12,6 +12,8 @@ export interface MenuProps {
12
12
  align?: "start" | "end";
13
13
  open?: boolean;
14
14
  onOpenChange?: (open: boolean) => void;
15
+ /** "en" | "vi" — else resolved from the nearest [lang] ancestor (vi default). */
16
+ lang?: string;
15
17
  className?: string;
16
18
  }
17
19
  export function Menu(props: MenuProps): React.ReactElement;