@archytechtor/ui 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (479) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -2
  3. package/dist/components/accordion/accordion.d.ts +25 -0
  4. package/dist/components/accordion/accordion.js +88 -0
  5. package/dist/components/accordion/index.d.ts +3 -0
  6. package/dist/components/accordion/index.js +1 -0
  7. package/dist/components/accordion/types.d.ts +39 -0
  8. package/dist/components/alert/alert.d.ts +25 -0
  9. package/dist/components/alert/alert.js +59 -0
  10. package/dist/components/alert/index.d.ts +3 -0
  11. package/dist/components/alert/index.js +3 -0
  12. package/dist/components/alert/types.d.ts +32 -0
  13. package/dist/components/alert/types.js +19 -0
  14. package/dist/components/alert-dialog/alert-dialog.d.ts +73 -0
  15. package/dist/components/alert-dialog/alert-dialog.js +170 -0
  16. package/dist/components/alert-dialog/index.d.ts +3 -0
  17. package/dist/components/alert-dialog/index.js +1 -0
  18. package/dist/components/alert-dialog/types.d.ts +80 -0
  19. package/dist/components/aspect-ratio/aspect-ratio.d.ts +8 -0
  20. package/dist/components/aspect-ratio/aspect-ratio.js +20 -0
  21. package/dist/components/aspect-ratio/index.d.ts +3 -0
  22. package/dist/components/aspect-ratio/index.js +1 -0
  23. package/dist/components/aspect-ratio/types.d.ts +8 -0
  24. package/dist/components/attachment/attachment.d.ts +57 -0
  25. package/dist/components/attachment/attachment.js +139 -0
  26. package/dist/components/attachment/index.d.ts +3 -0
  27. package/dist/components/attachment/index.js +3 -0
  28. package/dist/components/attachment/types.d.ts +79 -0
  29. package/dist/components/attachment/types.js +43 -0
  30. package/dist/components/avatar/avatar.d.ts +39 -0
  31. package/dist/components/avatar/avatar.js +88 -0
  32. package/dist/components/avatar/index.d.ts +3 -0
  33. package/dist/components/avatar/index.js +3 -0
  34. package/dist/components/avatar/types.d.ts +54 -0
  35. package/dist/components/avatar/types.js +21 -0
  36. package/dist/components/badge/badge.d.ts +7 -0
  37. package/dist/components/badge/badge.js +23 -0
  38. package/dist/components/badge/index.d.ts +3 -0
  39. package/dist/components/badge/index.js +3 -0
  40. package/dist/components/badge/types.d.ts +21 -0
  41. package/dist/components/badge/types.js +26 -0
  42. package/dist/components/bottom-nav/bottom-nav.d.ts +19 -0
  43. package/dist/components/bottom-nav/bottom-nav.js +59 -0
  44. package/dist/components/bottom-nav/index.d.ts +3 -0
  45. package/dist/components/bottom-nav/index.js +3 -0
  46. package/dist/components/bottom-nav/types.d.ts +45 -0
  47. package/dist/components/bottom-nav/types.js +44 -0
  48. package/dist/components/box/box.d.ts +7 -0
  49. package/dist/components/box/box.js +26 -0
  50. package/dist/components/box/index.d.ts +3 -0
  51. package/dist/components/box/index.js +3 -0
  52. package/dist/components/box/types.d.ts +23 -0
  53. package/dist/components/box/types.js +27 -0
  54. package/dist/components/breadcrumb/breadcrumb.d.ts +43 -0
  55. package/dist/components/breadcrumb/breadcrumb.js +115 -0
  56. package/dist/components/breadcrumb/index.d.ts +3 -0
  57. package/dist/components/breadcrumb/index.js +1 -0
  58. package/dist/components/breadcrumb/types.d.ts +36 -0
  59. package/dist/components/bubble/bubble.d.ts +25 -0
  60. package/dist/components/bubble/bubble.js +69 -0
  61. package/dist/components/bubble/index.d.ts +3 -0
  62. package/dist/components/bubble/index.js +3 -0
  63. package/dist/components/bubble/types.d.ts +51 -0
  64. package/dist/components/bubble/types.js +60 -0
  65. package/dist/components/button/button.d.ts +7 -0
  66. package/dist/components/button/button.js +155 -0
  67. package/dist/components/button/index.d.ts +4 -0
  68. package/dist/components/button/index.js +4 -0
  69. package/dist/components/button/types.d.ts +80 -0
  70. package/dist/components/button/types.js +99 -0
  71. package/dist/components/button/use-hold-to-confirm.d.ts +25 -0
  72. package/dist/components/button/use-hold-to-confirm.js +161 -0
  73. package/dist/components/button-group/button-group.d.ts +20 -0
  74. package/dist/components/button-group/button-group.js +62 -0
  75. package/dist/components/button-group/index.d.ts +3 -0
  76. package/dist/components/button-group/index.js +3 -0
  77. package/dist/components/button-group/types.d.ts +30 -0
  78. package/dist/components/button-group/types.js +19 -0
  79. package/dist/components/calendar/calendar.d.ts +15 -0
  80. package/dist/components/calendar/calendar.js +157 -0
  81. package/dist/components/calendar/index.d.ts +3 -0
  82. package/dist/components/calendar/index.js +1 -0
  83. package/dist/components/calendar/types.d.ts +21 -0
  84. package/dist/components/card/card.d.ts +43 -0
  85. package/dist/components/card/card.js +97 -0
  86. package/dist/components/card/index.d.ts +3 -0
  87. package/dist/components/card/index.js +3 -0
  88. package/dist/components/card/types.d.ts +44 -0
  89. package/dist/components/card/types.js +19 -0
  90. package/dist/components/carousel/carousel.d.ts +36 -0
  91. package/dist/components/carousel/carousel.js +190 -0
  92. package/dist/components/carousel/index.d.ts +3 -0
  93. package/dist/components/carousel/index.js +1 -0
  94. package/dist/components/carousel/types.d.ts +79 -0
  95. package/dist/components/chart/chart.d.ts +35 -0
  96. package/dist/components/chart/chart.js +212 -0
  97. package/dist/components/chart/index.d.ts +3 -0
  98. package/dist/components/chart/index.js +1 -0
  99. package/dist/components/chart/types.d.ts +89 -0
  100. package/dist/components/chart/types.js +7 -0
  101. package/dist/components/checkbox/checkbox.d.ts +7 -0
  102. package/dist/components/checkbox/checkbox.js +31 -0
  103. package/dist/components/checkbox/index.d.ts +3 -0
  104. package/dist/components/checkbox/index.js +1 -0
  105. package/dist/components/checkbox/types.d.ts +25 -0
  106. package/dist/components/code-block/code-block.d.ts +49 -0
  107. package/dist/components/code-block/code-block.js +188 -0
  108. package/dist/components/code-block/highlighter.d.ts +15 -0
  109. package/dist/components/code-block/highlighter.js +123 -0
  110. package/dist/components/code-block/index.d.ts +5 -0
  111. package/dist/components/code-block/index.js +5 -0
  112. package/dist/components/code-block/types.d.ts +94 -0
  113. package/dist/components/code-block/types.js +38 -0
  114. package/dist/components/code-block/use-highlighted-code.d.ts +9 -0
  115. package/dist/components/code-block/use-highlighted-code.js +67 -0
  116. package/dist/components/collapsible/collapsible.d.ts +19 -0
  117. package/dist/components/collapsible/collapsible.js +44 -0
  118. package/dist/components/collapsible/index.d.ts +3 -0
  119. package/dist/components/collapsible/index.js +1 -0
  120. package/dist/components/collapsible/types.d.ts +26 -0
  121. package/dist/components/combobox/combobox.d.ts +104 -0
  122. package/dist/components/combobox/combobox.js +365 -0
  123. package/dist/components/combobox/index.d.ts +3 -0
  124. package/dist/components/combobox/index.js +1 -0
  125. package/dist/components/combobox/types.d.ts +229 -0
  126. package/dist/components/command/command.d.ts +55 -0
  127. package/dist/components/command/command.js +179 -0
  128. package/dist/components/command/index.d.ts +3 -0
  129. package/dist/components/command/index.js +1 -0
  130. package/dist/components/command/types.d.ts +69 -0
  131. package/dist/components/context-menu/context-menu.d.ts +92 -0
  132. package/dist/components/context-menu/context-menu.js +270 -0
  133. package/dist/components/context-menu/index.d.ts +3 -0
  134. package/dist/components/context-menu/index.js +1 -0
  135. package/dist/components/context-menu/types.d.ts +138 -0
  136. package/dist/components/data-table/data-table-features.d.ts +21 -0
  137. package/dist/components/data-table/data-table-features.js +22 -0
  138. package/dist/components/data-table/data-table.d.ts +69 -0
  139. package/dist/components/data-table/data-table.js +283 -0
  140. package/dist/components/data-table/index.d.ts +4 -0
  141. package/dist/components/data-table/index.js +3 -0
  142. package/dist/components/data-table/types.d.ts +63 -0
  143. package/dist/components/date-input/date-input.d.ts +7 -0
  144. package/dist/components/date-input/date-input.js +147 -0
  145. package/dist/components/date-input/index.d.ts +3 -0
  146. package/dist/components/date-input/index.js +1 -0
  147. package/dist/components/date-input/types.d.ts +27 -0
  148. package/dist/components/date-picker/date-picker.d.ts +7 -0
  149. package/dist/components/date-picker/date-picker.js +74 -0
  150. package/dist/components/date-picker/index.d.ts +3 -0
  151. package/dist/components/date-picker/index.js +1 -0
  152. package/dist/components/date-picker/types.d.ts +38 -0
  153. package/dist/components/date-range-picker/date-range-picker.d.ts +7 -0
  154. package/dist/components/date-range-picker/date-range-picker.js +89 -0
  155. package/dist/components/date-range-picker/index.d.ts +3 -0
  156. package/dist/components/date-range-picker/index.js +1 -0
  157. package/dist/components/date-range-picker/types.d.ts +43 -0
  158. package/dist/components/dialog/dialog.d.ts +61 -0
  159. package/dist/components/dialog/dialog.js +160 -0
  160. package/dist/components/dialog/index.d.ts +3 -0
  161. package/dist/components/dialog/index.js +1 -0
  162. package/dist/components/dialog/types.d.ts +68 -0
  163. package/dist/components/drawer/drawer.d.ts +69 -0
  164. package/dist/components/drawer/drawer.js +245 -0
  165. package/dist/components/drawer/index.d.ts +3 -0
  166. package/dist/components/drawer/index.js +1 -0
  167. package/dist/components/drawer/types.d.ts +93 -0
  168. package/dist/components/dropdown-menu/dropdown-menu.d.ts +92 -0
  169. package/dist/components/dropdown-menu/dropdown-menu.js +268 -0
  170. package/dist/components/dropdown-menu/index.d.ts +3 -0
  171. package/dist/components/dropdown-menu/index.js +1 -0
  172. package/dist/components/dropdown-menu/types.d.ts +143 -0
  173. package/dist/components/empty/empty.d.ts +37 -0
  174. package/dist/components/empty/empty.js +84 -0
  175. package/dist/components/empty/index.d.ts +3 -0
  176. package/dist/components/empty/index.js +3 -0
  177. package/dist/components/empty/types.d.ts +39 -0
  178. package/dist/components/empty/types.js +13 -0
  179. package/dist/components/expanded-card/expanded-card.d.ts +43 -0
  180. package/dist/components/expanded-card/expanded-card.js +352 -0
  181. package/dist/components/expanded-card/index.d.ts +3 -0
  182. package/dist/components/expanded-card/index.js +1 -0
  183. package/dist/components/expanded-card/types.d.ts +52 -0
  184. package/dist/components/field/field.d.ts +62 -0
  185. package/dist/components/field/field.js +173 -0
  186. package/dist/components/field/index.d.ts +3 -0
  187. package/dist/components/field/index.js +3 -0
  188. package/dist/components/field/types.d.ts +81 -0
  189. package/dist/components/field/types.js +21 -0
  190. package/dist/components/form/form.d.ts +49 -0
  191. package/dist/components/form/form.js +136 -0
  192. package/dist/components/form/index.d.ts +3 -0
  193. package/dist/components/form/index.js +1 -0
  194. package/dist/components/form/types.d.ts +59 -0
  195. package/dist/components/grid/grid.d.ts +7 -0
  196. package/dist/components/grid/grid.js +36 -0
  197. package/dist/components/grid/index.d.ts +3 -0
  198. package/dist/components/grid/index.js +3 -0
  199. package/dist/components/grid/types.d.ts +35 -0
  200. package/dist/components/grid/types.js +102 -0
  201. package/dist/components/hover-card/hover-card.d.ts +19 -0
  202. package/dist/components/hover-card/hover-card.js +63 -0
  203. package/dist/components/hover-card/index.d.ts +3 -0
  204. package/dist/components/hover-card/index.js +1 -0
  205. package/dist/components/hover-card/types.d.ts +27 -0
  206. package/dist/components/image-gallery/image-gallery.d.ts +8 -0
  207. package/dist/components/image-gallery/image-gallery.js +294 -0
  208. package/dist/components/image-gallery/index.d.ts +4 -0
  209. package/dist/components/image-gallery/index.js +3 -0
  210. package/dist/components/image-gallery/masonry.d.ts +11 -0
  211. package/dist/components/image-gallery/masonry.js +62 -0
  212. package/dist/components/image-gallery/types.d.ts +80 -0
  213. package/dist/components/input/index.d.ts +3 -0
  214. package/dist/components/input/index.js +1 -0
  215. package/dist/components/input/input.d.ts +7 -0
  216. package/dist/components/input/input.js +18 -0
  217. package/dist/components/input/types.d.ts +28 -0
  218. package/dist/components/input-group/index.d.ts +3 -0
  219. package/dist/components/input-group/index.js +3 -0
  220. package/dist/components/input-group/input-group.d.ts +37 -0
  221. package/dist/components/input-group/input-group.js +103 -0
  222. package/dist/components/input-group/types.d.ts +64 -0
  223. package/dist/components/input-group/types.js +31 -0
  224. package/dist/components/input-otp/index.d.ts +3 -0
  225. package/dist/components/input-otp/index.js +1 -0
  226. package/dist/components/input-otp/input-otp.d.ts +25 -0
  227. package/dist/components/input-otp/input-otp.js +76 -0
  228. package/dist/components/input-otp/types.d.ts +43 -0
  229. package/dist/components/item/index.d.ts +3 -0
  230. package/dist/components/item/index.js +3 -0
  231. package/dist/components/item/item.d.ts +61 -0
  232. package/dist/components/item/item.js +149 -0
  233. package/dist/components/item/types.d.ts +75 -0
  234. package/dist/components/item/types.js +48 -0
  235. package/dist/components/kbd/index.d.ts +3 -0
  236. package/dist/components/kbd/index.js +1 -0
  237. package/dist/components/kbd/kbd.d.ts +13 -0
  238. package/dist/components/kbd/kbd.js +30 -0
  239. package/dist/components/kbd/types.d.ts +9 -0
  240. package/dist/components/label/index.d.ts +3 -0
  241. package/dist/components/label/index.js +1 -0
  242. package/dist/components/label/label.d.ts +7 -0
  243. package/dist/components/label/label.js +17 -0
  244. package/dist/components/label/types.d.ts +8 -0
  245. package/dist/components/loader/index.d.ts +3 -0
  246. package/dist/components/loader/index.js +3 -0
  247. package/dist/components/loader/loader.d.ts +8 -0
  248. package/dist/components/loader/loader.js +386 -0
  249. package/dist/components/loader/types.d.ts +27 -0
  250. package/dist/components/loader/types.js +58 -0
  251. package/dist/components/marker/index.d.ts +3 -0
  252. package/dist/components/marker/index.js +3 -0
  253. package/dist/components/marker/marker.d.ts +19 -0
  254. package/dist/components/marker/marker.js +51 -0
  255. package/dist/components/marker/types.d.ts +30 -0
  256. package/dist/components/marker/types.js +16 -0
  257. package/dist/components/menubar/index.d.ts +3 -0
  258. package/dist/components/menubar/index.js +1 -0
  259. package/dist/components/menubar/menubar.d.ts +98 -0
  260. package/dist/components/menubar/menubar.js +284 -0
  261. package/dist/components/menubar/types.d.ts +146 -0
  262. package/dist/components/message/index.d.ts +3 -0
  263. package/dist/components/message/index.js +1 -0
  264. package/dist/components/message/message.d.ts +37 -0
  265. package/dist/components/message/message.js +83 -0
  266. package/dist/components/message/types.d.ts +32 -0
  267. package/dist/components/message-scroller/index.d.ts +4 -0
  268. package/dist/components/message-scroller/index.js +3 -0
  269. package/dist/components/message-scroller/message-scroller-primitive.d.ts +21 -0
  270. package/dist/components/message-scroller/message-scroller-primitive.js +1349 -0
  271. package/dist/components/message-scroller/message-scroller.d.ts +39 -0
  272. package/dist/components/message-scroller/message-scroller.js +103 -0
  273. package/dist/components/message-scroller/types.d.ts +107 -0
  274. package/dist/components/money-input/index.d.ts +3 -0
  275. package/dist/components/money-input/index.js +1 -0
  276. package/dist/components/money-input/money-input.d.ts +7 -0
  277. package/dist/components/money-input/money-input.js +143 -0
  278. package/dist/components/money-input/types.d.ts +20 -0
  279. package/dist/components/morph-image/index.d.ts +3 -0
  280. package/dist/components/morph-image/index.js +3 -0
  281. package/dist/components/morph-image/morph-image.d.ts +8 -0
  282. package/dist/components/morph-image/morph-image.js +155 -0
  283. package/dist/components/morph-image/types.d.ts +38 -0
  284. package/dist/components/morph-image/types.js +18 -0
  285. package/dist/components/native-select/index.d.ts +3 -0
  286. package/dist/components/native-select/index.js +3 -0
  287. package/dist/components/native-select/native-select.d.ts +20 -0
  288. package/dist/components/native-select/native-select.js +57 -0
  289. package/dist/components/native-select/types.d.ts +52 -0
  290. package/dist/components/native-select/types.js +23 -0
  291. package/dist/components/navigation-menu/index.d.ts +3 -0
  292. package/dist/components/navigation-menu/index.js +3 -0
  293. package/dist/components/navigation-menu/navigation-menu.d.ts +50 -0
  294. package/dist/components/navigation-menu/navigation-menu.js +145 -0
  295. package/dist/components/navigation-menu/types.d.ts +79 -0
  296. package/dist/components/navigation-menu/types.js +15 -0
  297. package/dist/components/number-stepper/index.d.ts +3 -0
  298. package/dist/components/number-stepper/index.js +3 -0
  299. package/dist/components/number-stepper/number-stepper.d.ts +9 -0
  300. package/dist/components/number-stepper/number-stepper.js +223 -0
  301. package/dist/components/number-stepper/types.d.ts +47 -0
  302. package/dist/components/number-stepper/types.js +174 -0
  303. package/dist/components/pagination/index.d.ts +3 -0
  304. package/dist/components/pagination/index.js +1 -0
  305. package/dist/components/pagination/pagination.d.ts +43 -0
  306. package/dist/components/pagination/pagination.js +121 -0
  307. package/dist/components/pagination/types.d.ts +37 -0
  308. package/dist/components/phone-input/index.d.ts +3 -0
  309. package/dist/components/phone-input/index.js +1 -0
  310. package/dist/components/phone-input/phone-input.d.ts +7 -0
  311. package/dist/components/phone-input/phone-input.js +54 -0
  312. package/dist/components/phone-input/types.d.ts +12 -0
  313. package/dist/components/popover/index.d.ts +3 -0
  314. package/dist/components/popover/index.js +1 -0
  315. package/dist/components/popover/popover.d.ts +37 -0
  316. package/dist/components/popover/popover.js +100 -0
  317. package/dist/components/popover/types.d.ts +51 -0
  318. package/dist/components/progress/index.d.ts +3 -0
  319. package/dist/components/progress/index.js +1 -0
  320. package/dist/components/progress/progress.d.ts +32 -0
  321. package/dist/components/progress/progress.js +100 -0
  322. package/dist/components/progress/types.d.ts +31 -0
  323. package/dist/components/questionnaire/index.d.ts +3 -0
  324. package/dist/components/questionnaire/index.js +1 -0
  325. package/dist/components/questionnaire/questionnaire-primitive.js +1563 -0
  326. package/dist/components/questionnaire/questionnaire.d.ts +91 -0
  327. package/dist/components/questionnaire/questionnaire.js +315 -0
  328. package/dist/components/questionnaire/types.d.ts +203 -0
  329. package/dist/components/radio-group/index.d.ts +3 -0
  330. package/dist/components/radio-group/index.js +1 -0
  331. package/dist/components/radio-group/radio-group.d.ts +13 -0
  332. package/dist/components/radio-group/radio-group.js +44 -0
  333. package/dist/components/radio-group/types.d.ts +33 -0
  334. package/dist/components/reorder-list/index.d.ts +3 -0
  335. package/dist/components/reorder-list/index.js +1 -0
  336. package/dist/components/reorder-list/reorder-list.d.ts +19 -0
  337. package/dist/components/reorder-list/reorder-list.js +279 -0
  338. package/dist/components/reorder-list/types.d.ts +65 -0
  339. package/dist/components/resizable/index.d.ts +3 -0
  340. package/dist/components/resizable/index.js +1 -0
  341. package/dist/components/resizable/resizable.d.ts +19 -0
  342. package/dist/components/resizable/resizable.js +44 -0
  343. package/dist/components/resizable/types.d.ts +89 -0
  344. package/dist/components/scroll-area/index.d.ts +3 -0
  345. package/dist/components/scroll-area/index.js +1 -0
  346. package/dist/components/scroll-area/scroll-area.d.ts +13 -0
  347. package/dist/components/scroll-area/scroll-area.js +47 -0
  348. package/dist/components/scroll-area/types.d.ts +19 -0
  349. package/dist/components/select/index.d.ts +3 -0
  350. package/dist/components/select/index.js +1 -0
  351. package/dist/components/select/select.d.ts +61 -0
  352. package/dist/components/select/select.js +183 -0
  353. package/dist/components/select/types.d.ts +100 -0
  354. package/dist/components/separator/index.d.ts +3 -0
  355. package/dist/components/separator/index.js +1 -0
  356. package/dist/components/separator/separator.d.ts +7 -0
  357. package/dist/components/separator/separator.js +20 -0
  358. package/dist/components/separator/types.d.ts +9 -0
  359. package/dist/components/sheet/index.d.ts +3 -0
  360. package/dist/components/sheet/index.js +1 -0
  361. package/dist/components/sheet/sheet.d.ts +61 -0
  362. package/dist/components/sheet/sheet.js +155 -0
  363. package/dist/components/sheet/types.d.ts +69 -0
  364. package/dist/components/sidebar/index.d.ts +3 -0
  365. package/dist/components/sidebar/index.js +3 -0
  366. package/dist/components/sidebar/sidebar.d.ts +145 -0
  367. package/dist/components/sidebar/sidebar.js +550 -0
  368. package/dist/components/sidebar/types.d.ts +186 -0
  369. package/dist/components/sidebar/types.js +42 -0
  370. package/dist/components/sidebar/use-mobile.js +21 -0
  371. package/dist/components/skeleton/index.d.ts +3 -0
  372. package/dist/components/skeleton/index.js +1 -0
  373. package/dist/components/skeleton/skeleton.d.ts +8 -0
  374. package/dist/components/skeleton/skeleton.js +18 -0
  375. package/dist/components/skeleton/types.d.ts +5 -0
  376. package/dist/components/slider/index.d.ts +3 -0
  377. package/dist/components/slider/index.js +1 -0
  378. package/dist/components/slider/slider.d.ts +7 -0
  379. package/dist/components/slider/slider.js +106 -0
  380. package/dist/components/slider/types.d.ts +39 -0
  381. package/dist/components/spinner/index.d.ts +3 -0
  382. package/dist/components/spinner/index.js +1 -0
  383. package/dist/components/spinner/spinner.d.ts +7 -0
  384. package/dist/components/spinner/spinner.js +21 -0
  385. package/dist/components/spinner/types.d.ts +8 -0
  386. package/dist/components/stack/index.d.ts +3 -0
  387. package/dist/components/stack/index.js +3 -0
  388. package/dist/components/stack/stack.d.ts +7 -0
  389. package/dist/components/stack/stack.js +29 -0
  390. package/dist/components/stack/types.d.ts +56 -0
  391. package/dist/components/stack/types.js +62 -0
  392. package/dist/components/statistics/index.d.ts +3 -0
  393. package/dist/components/statistics/index.js +3 -0
  394. package/dist/components/statistics/statistics.d.ts +31 -0
  395. package/dist/components/statistics/statistics.js +141 -0
  396. package/dist/components/statistics/types.d.ts +84 -0
  397. package/dist/components/statistics/types.js +27 -0
  398. package/dist/components/switch/index.d.ts +3 -0
  399. package/dist/components/switch/index.js +3 -0
  400. package/dist/components/switch/switch.d.ts +7 -0
  401. package/dist/components/switch/switch.js +32 -0
  402. package/dist/components/switch/types.d.ts +37 -0
  403. package/dist/components/switch/types.js +23 -0
  404. package/dist/components/table/index.d.ts +3 -0
  405. package/dist/components/table/index.js +1 -0
  406. package/dist/components/table/table.d.ts +49 -0
  407. package/dist/components/table/table.js +112 -0
  408. package/dist/components/table/types.d.ts +33 -0
  409. package/dist/components/tabs/index.d.ts +3 -0
  410. package/dist/components/tabs/index.js +3 -0
  411. package/dist/components/tabs/tabs.d.ts +25 -0
  412. package/dist/components/tabs/tabs.js +81 -0
  413. package/dist/components/tabs/types.d.ts +61 -0
  414. package/dist/components/tabs/types.js +18 -0
  415. package/dist/components/textarea/index.d.ts +3 -0
  416. package/dist/components/textarea/index.js +1 -0
  417. package/dist/components/textarea/textarea.d.ts +7 -0
  418. package/dist/components/textarea/textarea.js +17 -0
  419. package/dist/components/textarea/types.d.ts +26 -0
  420. package/dist/components/toast/index.d.ts +3 -0
  421. package/dist/components/toast/index.js +1 -0
  422. package/dist/components/toast/toast.d.ts +63 -0
  423. package/dist/components/toast/toast.js +321 -0
  424. package/dist/components/toast/types.d.ts +120 -0
  425. package/dist/components/toggle/index.d.ts +3 -0
  426. package/dist/components/toggle/index.js +3 -0
  427. package/dist/components/toggle/toggle.d.ts +7 -0
  428. package/dist/components/toggle/toggle.js +32 -0
  429. package/dist/components/toggle/types.d.ts +35 -0
  430. package/dist/components/toggle/types.js +37 -0
  431. package/dist/components/toggle-group/index.d.ts +3 -0
  432. package/dist/components/toggle-group/index.js +1 -0
  433. package/dist/components/toggle-group/toggle-group.d.ts +13 -0
  434. package/dist/components/toggle-group/toggle-group.js +88 -0
  435. package/dist/components/toggle-group/types.d.ts +52 -0
  436. package/dist/components/tooltip/index.d.ts +3 -0
  437. package/dist/components/tooltip/index.js +1 -0
  438. package/dist/components/tooltip/tooltip.d.ts +25 -0
  439. package/dist/components/tooltip/tooltip.js +79 -0
  440. package/dist/components/tooltip/types.d.ts +48 -0
  441. package/dist/components/trace/index.d.ts +3 -0
  442. package/dist/components/trace/index.js +1 -0
  443. package/dist/components/trace/trace.d.ts +52 -0
  444. package/dist/components/trace/trace.js +132 -0
  445. package/dist/components/trace/types.d.ts +54 -0
  446. package/dist/components/typography/index.d.ts +3 -0
  447. package/dist/components/typography/index.js +3 -0
  448. package/dist/components/typography/types.d.ts +29 -0
  449. package/dist/components/typography/types.js +25 -0
  450. package/dist/components/typography/typography.d.ts +7 -0
  451. package/dist/components/typography/typography.js +40 -0
  452. package/dist/components/video-player/index.d.ts +6 -0
  453. package/dist/components/video-player/index.js +5 -0
  454. package/dist/components/video-player/types.d.ts +143 -0
  455. package/dist/components/video-player/use-video-player.js +319 -0
  456. package/dist/components/video-player/video-player-context.d.ts +45 -0
  457. package/dist/components/video-player/video-player-context.js +15 -0
  458. package/dist/components/video-player/video-player-controls.d.ts +38 -0
  459. package/dist/components/video-player/video-player-controls.js +226 -0
  460. package/dist/components/video-player/video-player-overlay.d.ts +13 -0
  461. package/dist/components/video-player/video-player-overlay.js +113 -0
  462. package/dist/components/video-player/video-player.d.ts +8 -0
  463. package/dist/components/video-player/video-player.js +139 -0
  464. package/dist/index.d.ts +259 -0
  465. package/dist/index.js +124 -0
  466. package/dist/lib/floating.d.ts +44 -0
  467. package/dist/lib/focus-source.js +51 -0
  468. package/dist/lib/format.d.ts +67 -0
  469. package/dist/lib/format.js +130 -0
  470. package/dist/lib/locale.d.ts +104 -0
  471. package/dist/lib/locale.js +103 -0
  472. package/dist/lib/part-props.d.ts +15 -0
  473. package/dist/lib/use-controllable-state.js +22 -0
  474. package/dist/lib/utils.d.ts +9 -0
  475. package/dist/lib/utils.js +16 -0
  476. package/dist/styles/index.css +128 -0
  477. package/dist/styles/theme.css +84 -0
  478. package/dist/styles/utilities.css +553 -0
  479. package/package.json +89 -3
@@ -0,0 +1,1563 @@
1
+ "use client";
2
+ import { mergeProps } from "@base-ui/react/merge-props";
3
+ import { createContext, createElement, use, useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
4
+ import { useRender } from "@base-ui/react/use-render";
5
+ // src/components/questionnaire/questionnaire-primitive.ts
6
+ /** Типы полей, у которых стрелки вверх/вниз переводят фокус, пока поле пустое */
7
+ const TEXT_INPUT_TYPES = [
8
+ "email",
9
+ "password",
10
+ "search",
11
+ "tel",
12
+ "text",
13
+ "url"
14
+ ];
15
+ /** Типы input, которые не считаются полями ввода текста */
16
+ const NON_TEXT_INPUT_TYPES = [
17
+ "button",
18
+ "checkbox",
19
+ "radio",
20
+ "reset",
21
+ "submit"
22
+ ];
23
+ const RootContext = createContext(null);
24
+ const ItemContext = createContext(null);
25
+ const ChoiceContext = createContext(null);
26
+ /**
27
+ * Контекст опроса или ошибка, если часть вне Questionnaire
28
+ * @param part - имя части для текста ошибки
29
+ * @returns контекст опроса
30
+ */
31
+ function useRootContext(part) {
32
+ const context = use(RootContext);
33
+ if (!context) throw new Error(`${part} должен находиться внутри Questionnaire`);
34
+ return context;
35
+ }
36
+ /**
37
+ * Контекст вопроса или ошибка, если часть вне QuestionnaireItem
38
+ * @param part - имя части для текста ошибки
39
+ * @returns контекст вопроса
40
+ */
41
+ function useItemContext(part) {
42
+ const context = use(ItemContext);
43
+ if (!context) throw new Error(`${part} должен находиться внутри QuestionnaireItem`);
44
+ return context;
45
+ }
46
+ /**
47
+ * Контекст варианта или ошибка, если часть вне QuestionnaireChoice
48
+ * @param part - имя части для текста ошибки
49
+ * @returns контекст варианта
50
+ */
51
+ function useChoiceContext(part) {
52
+ const context = use(ChoiceContext);
53
+ if (!context) throw new Error(`${part} должен находиться внутри QuestionnaireChoice`);
54
+ return context;
55
+ }
56
+ /**
57
+ * Собрать список через пробел без пустых частей
58
+ * @param tokens - части списка
59
+ * @returns строка или undefined, если частей нет
60
+ */
61
+ function joinTokens(tokens) {
62
+ const result = [];
63
+ for (const token of tokens) if (token) result.push(token);
64
+ return result.join(" ") || void 0;
65
+ }
66
+ /**
67
+ * Есть ли в значении непустой текст
68
+ * @param value - значение поля или список значений
69
+ * @returns true, если есть хотя бы один непустой текст
70
+ */
71
+ function hasText(value) {
72
+ if (Array.isArray(value)) return value.some((part) => String(part).trim().length > 0);
73
+ return value !== null && value !== void 0 && String(value).trim().length > 0;
74
+ }
75
+ /**
76
+ * Клавиши подсказок по режиму
77
+ * @param mode - буквы или цифры
78
+ * @returns A–Z, 1–9 или пустой список
79
+ */
80
+ function getShortcutKeys(mode) {
81
+ if (mode === "letters") return Array.from({ length: 26 }, (_, index) => String.fromCharCode(65 + index));
82
+ if (mode === "numbers") return Array.from({ length: 9 }, (_, index) => String(index + 1));
83
+ return [];
84
+ }
85
+ /**
86
+ * Клавиша подсказки по нажатой клавише
87
+ * @param key - нажатая клавиша
88
+ * @param mode - буквы или цифры
89
+ * @returns клавиша подсказки или null
90
+ */
91
+ function getShortcutByKey(key, mode) {
92
+ const shortcut = mode === "letters" ? key.toUpperCase() : key;
93
+ if (getShortcutKeys(mode).includes(shortcut)) return shortcut;
94
+ return null;
95
+ }
96
+ /**
97
+ * Значение aria-keyshortcuts ответа
98
+ * @param shortcut - клавиша варианта
99
+ * @param withEnter - Enter переводит к следующему вопросу
100
+ * @returns список клавиш или undefined
101
+ */
102
+ function getAnswerKeyShortcuts(shortcut, withEnter) {
103
+ return joinTokens([shortcut, withEnter ? "Enter" : null]);
104
+ }
105
+ /**
106
+ * Дан ли ответ этим элементом
107
+ * @param control - вариант или поле
108
+ * @returns true, если вариант выбран или в поле есть текст
109
+ */
110
+ function isAnswered(control) {
111
+ if (control.type === "choice") return control.element.checked;
112
+ return control.element.hasAttribute("name") && hasText(control.element.value);
113
+ }
114
+ /**
115
+ * Пустое текстовое поле: из него стрелки переводят фокус к вариантам
116
+ * @param control - вариант или поле
117
+ * @returns true для пустого текстового поля
118
+ */
119
+ function isEmptyTextInput(control) {
120
+ return control?.type === "input" && TEXT_INPUT_TYPES.includes(control.element.type) && !hasText(control.element.value);
121
+ }
122
+ /**
123
+ * Элемент ввода текста: в нём стрелки и буквы работают как обычно
124
+ * @param element - элемент
125
+ * @returns true для полей ввода текста
126
+ */
127
+ function isTextEntry(element) {
128
+ if (element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) return true;
129
+ if (element instanceof HTMLInputElement) return !NON_TEXT_INPUT_TYPES.includes(element.type);
130
+ return element instanceof HTMLElement && element.isContentEditable;
131
+ }
132
+ /**
133
+ * Радиокнопка ли элемент
134
+ * @param element - элемент
135
+ * @returns true для input type=radio
136
+ */
137
+ function isRadio(element) {
138
+ return element instanceof HTMLInputElement && element.type === "radio";
139
+ }
140
+ /**
141
+ * Сравнить элементы по порядку в документе
142
+ * @param first - первый элемент
143
+ * @param second - второй элемент
144
+ * @returns -1, если первый раньше, 1 — если позже, 0 — если тот же
145
+ */
146
+ function compareByDocumentPosition(first, second) {
147
+ if (first.element === second.element) return 0;
148
+ const position = first.element.compareDocumentPosition(second.element);
149
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
150
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
151
+ return 0;
152
+ }
153
+ /**
154
+ * Статус вопроса
155
+ * @param skipped - вопрос пропущен
156
+ * @param answered - есть ответ
157
+ * @returns статус
158
+ */
159
+ function getItemStatus(skipped, answered) {
160
+ if (skipped) return "skipped";
161
+ if (answered) return "answered";
162
+ return "unanswered";
163
+ }
164
+ /**
165
+ * Разобрать Root items
166
+ * @param items - описания вопросов
167
+ * @returns коллекция или null, если items не переданы
168
+ */
169
+ function createItemCollection(items) {
170
+ if (items === void 0) return null;
171
+ const enabledItems = [];
172
+ const itemByName = /* @__PURE__ */ new Map();
173
+ for (const item of items) {
174
+ if (!item.disabled) enabledItems.push(item);
175
+ itemByName.set(item.name, item);
176
+ }
177
+ return {
178
+ enabledItems,
179
+ itemByName,
180
+ items
181
+ };
182
+ }
183
+ /**
184
+ * Начальный вопрос: defaultItem, если он включён, иначе первый включённый
185
+ * @param collection - описания вопросов
186
+ * @param defaultItem - желаемый вопрос
187
+ * @returns имя вопроса или null
188
+ */
189
+ function resolveItemName(collection, defaultItem) {
190
+ if (!collection) return defaultItem ?? null;
191
+ const item = defaultItem ? collection.itemByName.get(defaultItem) : void 0;
192
+ if (item && !item.disabled) return item.name;
193
+ return collection.enabledItems[0]?.name ?? null;
194
+ }
195
+ /**
196
+ * Клавиши вариантов по значениям из Root items: отключённые варианты пропускаются
197
+ * @param definition - описание вопроса
198
+ * @param mode - буквы или цифры
199
+ * @returns клавиша по значению варианта
200
+ */
201
+ function getChoiceShortcuts(definition, mode) {
202
+ const result = /* @__PURE__ */ new Map();
203
+ if (!definition || !mode) return result;
204
+ const keys = getShortcutKeys(mode);
205
+ let index = 0;
206
+ for (const choice of definition.choices ?? []) {
207
+ if (choice.disabled) continue;
208
+ const key = keys[index];
209
+ if (!key) break;
210
+ result.set(choice.value, key);
211
+ index += 1;
212
+ }
213
+ return result;
214
+ }
215
+ /**
216
+ * Ошибки в самих Root items: повторы имён и значений, неверный defaultItem
217
+ * @param collection - описания вопросов
218
+ * @param defaultItem - начальный вопрос
219
+ * @returns тексты предупреждений
220
+ */
221
+ function getDefinitionWarnings(collection, defaultItem) {
222
+ const result = [];
223
+ const names = /* @__PURE__ */ new Set();
224
+ for (const item of collection.items) {
225
+ if (names.has(item.name)) result.push(`вопрос «${item.name}» описан несколько раз`);
226
+ names.add(item.name);
227
+ const values = /* @__PURE__ */ new Set();
228
+ for (const choice of item.choices ?? []) {
229
+ if (values.has(choice.value)) result.push(`вариант «${choice.value}» описан несколько раз в вопросе «${item.name}»`);
230
+ values.add(choice.value);
231
+ }
232
+ }
233
+ if (defaultItem) {
234
+ const item = collection.itemByName.get(defaultItem);
235
+ if (!item || item.disabled) result.push(`defaultItem «${defaultItem}» — не включённый вопрос, открыт первый включённый`);
236
+ }
237
+ return result;
238
+ }
239
+ /**
240
+ * Расхождения Root items с отрисованными вопросами и вариантами
241
+ * @param collection - описания вопросов
242
+ * @param registeredItems - отрисованные вопросы
243
+ * @param shortcuts - подсказки клавиш
244
+ * @returns тексты предупреждений
245
+ */
246
+ function getRenderWarnings(collection, registeredItems, shortcuts) {
247
+ const result = [];
248
+ const registeredByName = /* @__PURE__ */ new Map();
249
+ for (const item of registeredItems) registeredByName.set(item.name, item);
250
+ for (const definition of collection.items) {
251
+ const item = registeredByName.get(definition.name);
252
+ if (!item) {
253
+ if (!definition.disabled) result.push(`вопрос «${definition.name}» описан, но не отрисован`);
254
+ continue;
255
+ }
256
+ if (item.disabled !== Boolean(definition.disabled)) result.push(`у вопроса «${definition.name}» разный disabled в items и QuestionnaireItem`);
257
+ if (item.required !== Boolean(definition.required)) result.push(`у вопроса «${definition.name}» разный required в items и QuestionnaireItem`);
258
+ const choices = definition.choices ?? [];
259
+ const definedValues = /* @__PURE__ */ new Set();
260
+ const renderedByValue = /* @__PURE__ */ new Map();
261
+ for (const choice of choices) definedValues.add(choice.value);
262
+ for (const choice of item.choices) renderedByValue.set(choice.value, choice);
263
+ for (const choice of choices) {
264
+ const rendered = renderedByValue.get(choice.value);
265
+ if (!rendered) {
266
+ result.push(`вариант «${choice.value}» вопроса «${definition.name}» описан, но не отрисован`);
267
+ continue;
268
+ }
269
+ if (rendered.disabled !== Boolean(choice.disabled)) result.push(`у варианта «${choice.value}» вопроса «${definition.name}» разный disabled в items и QuestionnaireChoice`);
270
+ }
271
+ if (!shortcuts) continue;
272
+ const enabledDefined = [];
273
+ const enabledRendered = [];
274
+ for (const choice of item.choices) {
275
+ if (!definedValues.has(choice.value)) result.push(`вариант «${choice.value}» вопроса «${definition.name}» не описан в items и останется без клавиши`);
276
+ if (!choice.disabled) enabledRendered.push(choice.value);
277
+ }
278
+ for (const choice of choices) if (!choice.disabled) enabledDefined.push(choice.value);
279
+ const sameValues = enabledDefined.length > 0 && enabledDefined.length === enabledRendered.length && enabledDefined.every((value) => enabledRendered.includes(value));
280
+ const otherOrder = enabledDefined.some((value, index) => value !== enabledRendered[index]);
281
+ if (sameValues && otherOrder) result.push(`порядок вариантов вопроса «${definition.name}» в items и в разметке различается`);
282
+ }
283
+ for (const item of registeredItems) if (!item.disabled && !collection.itemByName.has(item.name)) result.push(`вопрос «${item.name}» отрисован, но не описан в items и не участвует в опросе`);
284
+ return result;
285
+ }
286
+ /**
287
+ * Логика варианта ответа: регистрация в вопросе, выбор, клавиша
288
+ * @param parameters - параметры варианта
289
+ * @returns пропсы input и состояние варианта
290
+ */
291
+ function useChoice({ checked, defaultChecked, disabled, onChange, value }) {
292
+ const item = useItemContext("QuestionnaireChoice");
293
+ const id = useId();
294
+ const [element, setElement] = useState(null);
295
+ const initialCheckedRef = useRef(defaultChecked);
296
+ const isControlled = checked !== void 0;
297
+ const isDisabled = item.disabled || disabled;
298
+ const isSelected = item.selectedAnswerIds.includes(id);
299
+ const isSkipped = item.status === "skipped";
300
+ let isChecked = isSelected;
301
+ if (isControlled) isChecked = isSkipped ? false : checked;
302
+ const type = item.multiple ? "checkbox" : "radio";
303
+ const shortcut = item.shortcutByChoiceValue?.get(value) ?? item.shortcutByAnswerId.get(id) ?? null;
304
+ const { registerAnswerControl, registerAnswerSelection, resetVersion, setAnswerDefault, syncControlledAnswerSelection } = item;
305
+ useLayoutEffect(() => registerAnswerSelection(id, initialCheckedRef.current), [id, registerAnswerSelection]);
306
+ useLayoutEffect(() => setAnswerDefault(id, defaultChecked), [
307
+ id,
308
+ defaultChecked,
309
+ setAnswerDefault
310
+ ]);
311
+ useLayoutEffect(() => {
312
+ if (!element) return;
313
+ return registerAnswerControl({
314
+ type: "choice",
315
+ id,
316
+ element,
317
+ disabled: isDisabled,
318
+ ownDisabled: disabled,
319
+ value
320
+ });
321
+ }, [
322
+ id,
323
+ disabled,
324
+ isDisabled,
325
+ element,
326
+ registerAnswerControl,
327
+ value
328
+ ]);
329
+ useLayoutEffect(() => {
330
+ if (isControlled) syncControlledAnswerSelection(id, checked);
331
+ }, [
332
+ id,
333
+ isControlled,
334
+ checked,
335
+ resetVersion,
336
+ syncControlledAnswerSelection
337
+ ]);
338
+ useLayoutEffect(() => {
339
+ if (!element) return;
340
+ element.defaultChecked = isControlled ? checked : defaultChecked;
341
+ if (resetVersion > 0) element.checked = isChecked;
342
+ }, [
343
+ isChecked,
344
+ isControlled,
345
+ checked,
346
+ defaultChecked,
347
+ element,
348
+ resetVersion
349
+ ]);
350
+ /**
351
+ * Вариант выбран или снят пользователем
352
+ * @param event - событие input
353
+ */
354
+ const handleChange = (event) => {
355
+ onChange?.(event);
356
+ if (event.defaultPrevented) return;
357
+ if (!isControlled) {
358
+ item.setAnswerSelectionFromInteraction(id, event.target.checked);
359
+ return;
360
+ }
361
+ if (isSkipped && checked === event.target.checked) item.setAnswerSelectionFromInteraction(id, checked);
362
+ };
363
+ const name = isSkipped ? void 0 : item.name;
364
+ const invalid = item.invalid || void 0;
365
+ const keyShortcuts = getAnswerKeyShortcuts(shortcut, !isDisabled && isChecked);
366
+ const required = item.required && !item.multiple && !item.hasInputAnswer;
367
+ return {
368
+ inputProps: {
369
+ "ref": setElement,
370
+ "aria-invalid": invalid,
371
+ "aria-keyshortcuts": keyShortcuts,
372
+ "checked": isChecked,
373
+ "disabled": isDisabled,
374
+ "id": id,
375
+ "name": name,
376
+ "onChange": handleChange,
377
+ "required": required,
378
+ "type": type,
379
+ "value": value
380
+ },
381
+ state: {
382
+ checked: isChecked,
383
+ disabled: isDisabled,
384
+ invalid: item.invalid,
385
+ shortcut,
386
+ type
387
+ }
388
+ };
389
+ }
390
+ /**
391
+ * Логика поля свободного ответа: регистрация в вопросе и учёт ответа
392
+ * @param parameters - параметры поля
393
+ * @returns ref, пропсы input и состояние поля
394
+ */
395
+ function useInput({ defaultValue, disabled, onChange, type, value }) {
396
+ const item = useItemContext("QuestionnaireInput");
397
+ const id = useId();
398
+ const inputRef = useRef(null);
399
+ const initialFilledRef = useRef(hasText(defaultValue));
400
+ const isControlled = value !== void 0;
401
+ const isDefaultFilled = hasText(defaultValue);
402
+ const isValueFilled = hasText(value);
403
+ const [filled, setFilled] = useState(isDefaultFilled);
404
+ const isDisabled = item.disabled || disabled;
405
+ const isFilled = isControlled ? isValueFilled : filled;
406
+ const isSelected = item.selectedAnswerIds.includes(id);
407
+ const { registerAnswerControl, registerAnswerSelection, resetVersion, setAnswerDefault, syncControlledAnswerSelection } = item;
408
+ useLayoutEffect(() => registerAnswerSelection(id, initialFilledRef.current), [id, registerAnswerSelection]);
409
+ useLayoutEffect(() => setAnswerDefault(id, isDefaultFilled), [
410
+ isDefaultFilled,
411
+ id,
412
+ setAnswerDefault
413
+ ]);
414
+ useLayoutEffect(() => {
415
+ const element = inputRef.current;
416
+ if (!element) return;
417
+ return registerAnswerControl({
418
+ type: "input",
419
+ id,
420
+ element,
421
+ disabled: isDisabled
422
+ });
423
+ }, [
424
+ isDisabled,
425
+ id,
426
+ registerAnswerControl
427
+ ]);
428
+ useLayoutEffect(() => {
429
+ if (isControlled) {
430
+ syncControlledAnswerSelection(id, isValueFilled);
431
+ return;
432
+ }
433
+ if (resetVersion > 0) setFilled(isDefaultFilled);
434
+ }, [
435
+ isControlled,
436
+ isValueFilled,
437
+ value,
438
+ isDefaultFilled,
439
+ id,
440
+ resetVersion,
441
+ syncControlledAnswerSelection
442
+ ]);
443
+ useLayoutEffect(() => {
444
+ const element = inputRef.current;
445
+ if (element && isControlled) element.defaultValue = String(value);
446
+ }, [isControlled, value]);
447
+ /**
448
+ * Изменился текст поля
449
+ * @param event - событие input
450
+ */
451
+ const handleChange = (event) => {
452
+ onChange?.(event);
453
+ if (event.defaultPrevented) return;
454
+ const nextFilled = event.target.value.trim().length > 0;
455
+ if (!isControlled) {
456
+ setFilled(nextFilled);
457
+ item.setAnswerSelectionFromInteraction(id, nextFilled);
458
+ }
459
+ };
460
+ return {
461
+ inputRef,
462
+ inputProps: {
463
+ "aria-invalid": item.invalid || void 0,
464
+ "aria-keyshortcuts": getAnswerKeyShortcuts(null, !isDisabled && isFilled && isSelected),
465
+ "defaultValue": isControlled ? void 0 : defaultValue,
466
+ "disabled": isDisabled,
467
+ "form": isSelected ? void 0 : "",
468
+ "id": id,
469
+ "name": isSelected ? item.name : void 0,
470
+ "onChange": handleChange,
471
+ "type": type,
472
+ "value": isControlled ? value : void 0
473
+ },
474
+ state: {
475
+ disabled: isDisabled,
476
+ filled: isFilled,
477
+ invalid: item.invalid
478
+ }
479
+ };
480
+ }
481
+ /**
482
+ * Ключ отрисованных вариантов: значения и отключённость по порядку
483
+ * @param controls - элементы ответа по порядку в разметке
484
+ * @returns JSON-список вариантов
485
+ */
486
+ function getChoicesKey(controls) {
487
+ const choices = [];
488
+ for (const control of controls) if (control.type === "choice") choices.push({
489
+ disabled: control.ownDisabled,
490
+ value: control.value
491
+ });
492
+ return JSON.stringify(choices);
493
+ }
494
+ /**
495
+ * Логика вопроса: ответы, статус, проверка, навигация стрелками и клавишами
496
+ * @param parameters - параметры вопроса
497
+ * @returns контекст, пропсы fieldset и состояние вопроса
498
+ */
499
+ function useItem({ ariaDescribedBy, ariaKeyShortcuts, disabled, invalid, multiple, name, onStatusChange, required }) {
500
+ const { activeItemName, domVersion, first, itemDefinitionByName, last, nativeValidation, registerItem, shortcuts } = useRootContext("QuestionnaireItem");
501
+ const [itemElement, setItemElement] = useState(null);
502
+ const [answerControls, setAnswerControls] = useState([]);
503
+ const [attempted, setAttempted] = useState(false);
504
+ const [selectedAnswerIds, setSelectedAnswerIds] = useState([]);
505
+ const [skipped, setSkipped] = useState(false);
506
+ const [resetVersion, setResetVersion] = useState(0);
507
+ const [descriptionIds, setDescriptionIds] = useState([]);
508
+ const [errorIds, setErrorIds] = useState([]);
509
+ const defaultAnswerIdsRef = useRef([]);
510
+ const multipleRef = useRef(multiple);
511
+ const previousMultipleRef = useRef(multiple);
512
+ const itemApiRef = useRef(null);
513
+ multipleRef.current = multiple;
514
+ const active = !disabled && activeItemName === name;
515
+ const sortedControls = useMemo(() => [...answerControls].sort(compareByDocumentPosition), [answerControls, domVersion]);
516
+ const enabledControls = useMemo(() => sortedControls.filter((control) => !control.disabled), [sortedControls]);
517
+ const status = getItemStatus(skipped, enabledControls.some((control) => selectedAnswerIds.includes(control.id)));
518
+ const skippedOptional = status === "skipped" && !required;
519
+ const valid = disabled || skippedOptional || !invalid && status === "answered";
520
+ const showInvalid = !disabled && !skippedOptional && (invalid || attempted && !valid);
521
+ const hasInputAnswer = enabledControls.some((control) => control.type === "input");
522
+ const statusRef = useRef(status);
523
+ const definition = itemDefinitionByName?.get(name);
524
+ const shortcutByChoiceValue = useMemo(() => {
525
+ if (!itemDefinitionByName) return null;
526
+ return getChoiceShortcuts(definition, shortcuts);
527
+ }, [
528
+ definition,
529
+ itemDefinitionByName,
530
+ shortcuts
531
+ ]);
532
+ const shortcutByAnswerId = useMemo(() => {
533
+ const result = /* @__PURE__ */ new Map();
534
+ if (shortcutByChoiceValue) return result;
535
+ const keys = getShortcutKeys(shortcuts);
536
+ for (const control of enabledControls) {
537
+ const key = keys[result.size];
538
+ if (!key) break;
539
+ if (control.type === "choice") result.set(control.id, key);
540
+ }
541
+ return result;
542
+ }, [
543
+ enabledControls,
544
+ shortcutByChoiceValue,
545
+ shortcuts
546
+ ]);
547
+ const registerAnswerControl = useCallback((control) => {
548
+ setAnswerControls((previous) => {
549
+ const result = [];
550
+ for (const current of previous) if (current.element !== control.element && current.id !== control.id) result.push(current);
551
+ result.push(control);
552
+ return result;
553
+ });
554
+ return () => {
555
+ setAnswerControls((previous) => previous.filter((current) => current !== control));
556
+ };
557
+ }, []);
558
+ useLayoutEffect(() => {
559
+ if (statusRef.current !== status) {
560
+ statusRef.current = status;
561
+ onStatusChange?.(status);
562
+ }
563
+ }, [onStatusChange, status]);
564
+ const updateSelection = useCallback((id, selected) => {
565
+ setSelectedAnswerIds((previous) => {
566
+ if (!selected) return previous.filter((current) => current !== id);
567
+ if (!multiple) return [id];
568
+ if (previous.includes(id)) return previous;
569
+ return [...previous, id];
570
+ });
571
+ }, [multiple]);
572
+ const setAnswerSelectionFromInteraction = useCallback((id, selected) => {
573
+ setSkipped(false);
574
+ updateSelection(id, selected);
575
+ }, [updateSelection]);
576
+ const syncControlledAnswerSelection = useCallback((id, selected) => {
577
+ if (selected) setSkipped(false);
578
+ updateSelection(id, selected);
579
+ }, [updateSelection]);
580
+ const registerAnswerSelection = useCallback((id, selected) => {
581
+ if (selected) {
582
+ defaultAnswerIdsRef.current = [...defaultAnswerIdsRef.current.filter((current) => current !== id), id];
583
+ setSelectedAnswerIds((previous) => {
584
+ if (multipleRef.current) {
585
+ if (previous.includes(id)) return previous;
586
+ return [...previous, id];
587
+ }
588
+ if (previous.length) return previous;
589
+ return [id];
590
+ });
591
+ }
592
+ return () => {
593
+ defaultAnswerIdsRef.current = defaultAnswerIdsRef.current.filter((current) => current !== id);
594
+ setSelectedAnswerIds((previous) => previous.filter((current) => current !== id));
595
+ };
596
+ }, []);
597
+ const setAnswerDefault = useCallback((id, selected) => {
598
+ const defaults = defaultAnswerIdsRef.current;
599
+ if (selected) {
600
+ if (!defaults.includes(id)) defaultAnswerIdsRef.current = [...defaults, id];
601
+ return;
602
+ }
603
+ defaultAnswerIdsRef.current = defaults.filter((current) => current !== id);
604
+ }, []);
605
+ const registerDescription = useCallback((id) => {
606
+ setDescriptionIds((previous) => {
607
+ if (previous.includes(id)) return previous;
608
+ return [...previous, id];
609
+ });
610
+ return () => {
611
+ setDescriptionIds((previous) => previous.filter((current) => current !== id));
612
+ };
613
+ }, []);
614
+ const registerError = useCallback((id) => {
615
+ setErrorIds((previous) => {
616
+ if (previous.includes(id)) return previous;
617
+ return [...previous, id];
618
+ });
619
+ return () => {
620
+ setErrorIds((previous) => previous.filter((current) => current !== id));
621
+ };
622
+ }, []);
623
+ const validate = useCallback(() => {
624
+ setAttempted(true);
625
+ if (!valid) return false;
626
+ if (!nativeValidation) return true;
627
+ const invalidControl = enabledControls.find((control) => isAnswered(control) && control.element.willValidate && !control.element.validity.valid);
628
+ if (invalidControl) {
629
+ invalidControl.element.focus();
630
+ invalidControl.element.reportValidity();
631
+ return false;
632
+ }
633
+ return true;
634
+ }, [
635
+ enabledControls,
636
+ nativeValidation,
637
+ valid
638
+ ]);
639
+ const focus = useCallback(() => {
640
+ itemElement?.focus();
641
+ }, [itemElement]);
642
+ const focusInvalid = useCallback(() => {
643
+ const filledInput = itemElement?.querySelector("input[data-filled][name]:not(:disabled)");
644
+ const firstField = itemElement?.querySelector("input:not([type=hidden]):not(:disabled), textarea:not(:disabled)");
645
+ (filledInput ?? firstField ?? itemElement)?.focus();
646
+ }, [itemElement]);
647
+ const reset = useCallback(() => {
648
+ const defaults = defaultAnswerIdsRef.current;
649
+ const nextSelection = multiple ? [...defaults] : defaults.slice(0, 1);
650
+ setAttempted(false);
651
+ setSkipped(false);
652
+ setSelectedAnswerIds(nextSelection);
653
+ setResetVersion((previous) => previous + 1);
654
+ }, [multiple]);
655
+ const skip = useCallback(() => {
656
+ if (required) return;
657
+ setSelectedAnswerIds([]);
658
+ setSkipped(true);
659
+ }, [required]);
660
+ useLayoutEffect(() => {
661
+ const wasMultiple = previousMultipleRef.current;
662
+ previousMultipleRef.current = multiple;
663
+ if (!wasMultiple || multiple) return;
664
+ setSelectedAnswerIds((previous) => {
665
+ const firstSelected = enabledControls.find((control) => previous.includes(control.id));
666
+ if (firstSelected) return [firstSelected.id];
667
+ return [];
668
+ });
669
+ }, [enabledControls, multiple]);
670
+ const getAnswerByElement = useCallback((element) => enabledControls.find((control) => control.element === element) ?? null, [enabledControls]);
671
+ const getAnswerByShortcut = useCallback((shortcut) => {
672
+ if (shortcutByChoiceValue) {
673
+ let choiceValue;
674
+ for (const [value, key] of shortcutByChoiceValue) if (key === shortcut) {
675
+ choiceValue = value;
676
+ break;
677
+ }
678
+ return enabledControls.find((control) => control.type === "choice" && control.value === choiceValue) ?? null;
679
+ }
680
+ let answerId;
681
+ for (const [id, key] of shortcutByAnswerId) if (key === shortcut) {
682
+ answerId = id;
683
+ break;
684
+ }
685
+ return enabledControls.find((control) => control.id === answerId) ?? null;
686
+ }, [
687
+ enabledControls,
688
+ shortcutByAnswerId,
689
+ shortcutByChoiceValue
690
+ ]);
691
+ const moveAnswerFocus = useCallback((target, direction) => {
692
+ const index = enabledControls.findIndex((control) => control.element === target);
693
+ const current = enabledControls[index] ?? null;
694
+ const count = enabledControls.length;
695
+ if (!count || isTextEntry(target) && !isEmptyTextInput(current) || index < 0 && target !== itemElement) return false;
696
+ let nextControl;
697
+ if (index < 0) {
698
+ const edgeControl = direction === "next" ? enabledControls[0] : enabledControls[count - 1];
699
+ nextControl = enabledControls.find(isAnswered) ?? edgeControl;
700
+ } else nextControl = enabledControls[(index + (direction === "next" ? 1 : -1) + count) % count];
701
+ if (!nextControl || nextControl.element === target || index >= 0 && isRadio(target) && isRadio(nextControl.element)) return false;
702
+ nextControl.element.focus();
703
+ if (nextControl.type === "choice" && isRadio(nextControl.element)) nextControl.element.click();
704
+ return true;
705
+ }, [enabledControls, itemElement]);
706
+ const choicesKey = getChoicesKey(sortedControls);
707
+ const choices = useMemo(() => JSON.parse(choicesKey), [choicesKey]);
708
+ useLayoutEffect(() => {
709
+ itemApiRef.current = {
710
+ focus,
711
+ focusInvalid,
712
+ getAnswerByElement,
713
+ getAnswerByShortcut,
714
+ moveAnswerFocus,
715
+ reset,
716
+ skip,
717
+ validate
718
+ };
719
+ });
720
+ useLayoutEffect(() => {
721
+ if (!itemElement) return;
722
+ return registerItem({
723
+ choices,
724
+ disabled,
725
+ element: itemElement,
726
+ focus: () => itemApiRef.current.focus(),
727
+ focusInvalid: () => itemApiRef.current.focusInvalid(),
728
+ getAnswerByElement: (element) => itemApiRef.current.getAnswerByElement(element),
729
+ getAnswerByShortcut: (shortcut) => itemApiRef.current.getAnswerByShortcut(shortcut),
730
+ moveAnswerFocus: (target, direction) => itemApiRef.current.moveAnswerFocus(target, direction),
731
+ name,
732
+ required,
733
+ reset: () => itemApiRef.current.reset(),
734
+ skip: () => itemApiRef.current.skip(),
735
+ status,
736
+ validate: () => itemApiRef.current.validate()
737
+ });
738
+ }, [
739
+ choices,
740
+ disabled,
741
+ itemElement,
742
+ name,
743
+ registerItem,
744
+ required,
745
+ status
746
+ ]);
747
+ const context = useMemo(() => ({
748
+ active,
749
+ disabled,
750
+ hasInputAnswer,
751
+ invalid: showInvalid,
752
+ multiple,
753
+ name,
754
+ registerAnswerControl,
755
+ registerAnswerSelection,
756
+ registerDescription,
757
+ registerError,
758
+ required,
759
+ resetVersion,
760
+ selectedAnswerIds,
761
+ setAnswerDefault,
762
+ setAnswerSelectionFromInteraction,
763
+ shortcutByAnswerId,
764
+ shortcutByChoiceValue,
765
+ shortcuts,
766
+ status,
767
+ syncControlledAnswerSelection
768
+ }), [
769
+ active,
770
+ disabled,
771
+ hasInputAnswer,
772
+ showInvalid,
773
+ multiple,
774
+ name,
775
+ registerAnswerControl,
776
+ registerAnswerSelection,
777
+ registerDescription,
778
+ registerError,
779
+ required,
780
+ resetVersion,
781
+ selectedAnswerIds,
782
+ setAnswerDefault,
783
+ setAnswerSelectionFromInteraction,
784
+ shortcutByAnswerId,
785
+ shortcutByChoiceValue,
786
+ shortcuts,
787
+ status,
788
+ syncControlledAnswerSelection
789
+ ]);
790
+ const describedByIds = [...descriptionIds];
791
+ if (showInvalid) describedByIds.push(...errorIds);
792
+ describedByIds.push(ariaDescribedBy ?? "");
793
+ const keyShortcuts = [ariaKeyShortcuts];
794
+ if (active) {
795
+ keyShortcuts.push("Meta+Enter Control+Enter");
796
+ if (enabledControls.length) keyShortcuts.push("ArrowUp ArrowDown");
797
+ if (!first) keyShortcuts.push("ArrowLeft");
798
+ if (!last && status !== "unanswered") keyShortcuts.push("ArrowRight");
799
+ }
800
+ return {
801
+ context,
802
+ setItemElement,
803
+ itemProps: {
804
+ "aria-describedby": joinTokens(describedByIds),
805
+ "aria-invalid": showInvalid || void 0,
806
+ "aria-keyshortcuts": joinTokens(keyShortcuts),
807
+ "disabled": disabled,
808
+ "hidden": !active,
809
+ "inert": !active,
810
+ "tabIndex": -1
811
+ },
812
+ state: {
813
+ active,
814
+ disabled,
815
+ invalid: showInvalid,
816
+ multiple,
817
+ required,
818
+ status
819
+ }
820
+ };
821
+ }
822
+ /**
823
+ * Логика опроса: открытый вопрос, переходы, проверка, отправка и сброс, клавиатура
824
+ * @param parameters - параметры опроса
825
+ * @returns контекст, пропсы формы и состояние
826
+ */
827
+ function useRoot({ defaultItem, item, items, noValidate, onItemChange, onReset, onSubmit, shortcuts }) {
828
+ const collection = useMemo(() => createItemCollection(items), [items]);
829
+ const [registeredItems, setRegisteredItems] = useState([]);
830
+ const [uncontrolledItem, setUncontrolledItem] = useState(() => resolveItemName(collection, defaultItem));
831
+ const [formElement, setFormElement] = useState(null);
832
+ const [domVersion, setDomVersion] = useState(0);
833
+ const pendingFocusRef = useRef(null);
834
+ const isControlled = item !== void 0;
835
+ const activeItemName = isControlled ? item : uncontrolledItem;
836
+ const previousItemRef = useRef(activeItemName);
837
+ const nativeValidation = !noValidate;
838
+ const shortcutMode = shortcuts ?? null;
839
+ const warningsRef = useRef(/* @__PURE__ */ new Set());
840
+ useLayoutEffect(() => {
841
+ if (!collection || !formElement) {
842
+ warningsRef.current.clear();
843
+ return;
844
+ }
845
+ let cancelled = false;
846
+ queueMicrotask(() => {
847
+ if (cancelled) return;
848
+ const warnings = /* @__PURE__ */ new Set([...getDefinitionWarnings(collection, defaultItem), ...getRenderWarnings(collection, registeredItems, shortcutMode)]);
849
+ for (const warning of warnings) if (!warningsRef.current.has(warning)) console.warn(`[Questionnaire] ${warning}`);
850
+ warningsRef.current = warnings;
851
+ });
852
+ return () => {
853
+ cancelled = true;
854
+ };
855
+ }, [
856
+ collection,
857
+ defaultItem,
858
+ registeredItems,
859
+ formElement,
860
+ shortcutMode
861
+ ]);
862
+ useLayoutEffect(() => {
863
+ if (!formElement || typeof MutationObserver === "undefined") return;
864
+ const observer = new MutationObserver(() => {
865
+ setDomVersion((previous) => previous + 1);
866
+ });
867
+ observer.observe(formElement, {
868
+ childList: true,
869
+ subtree: true
870
+ });
871
+ return () => observer.disconnect();
872
+ }, [formElement]);
873
+ const enabledRegistered = useMemo(() => {
874
+ const result = [];
875
+ for (const registered of registeredItems) if (!registered.disabled) result.push(registered);
876
+ return result.sort(compareByDocumentPosition);
877
+ }, [domVersion, registeredItems]);
878
+ const registeredByName = useMemo(() => {
879
+ const result = /* @__PURE__ */ new Map();
880
+ for (const registered of enabledRegistered) result.set(registered.name, registered);
881
+ return result;
882
+ }, [enabledRegistered]);
883
+ const orderedItems = collection?.enabledItems ?? enabledRegistered;
884
+ const activeIndex = orderedItems.findIndex((current) => current.name === activeItemName);
885
+ const activeItem = activeIndex < 0 || !activeItemName ? null : registeredByName.get(activeItemName) ?? null;
886
+ const activeDefinition = activeItemName ? collection?.itemByName.get(activeItemName) : void 0;
887
+ let activeItemRequired = null;
888
+ let activeItemStatus = null;
889
+ if (activeIndex >= 0) {
890
+ activeItemRequired = activeDefinition ? Boolean(activeDefinition.required) : activeItem?.required ?? false;
891
+ const fallbackStatus = activeItemName ? "unanswered" : null;
892
+ activeItemStatus = activeItem?.status ?? fallbackStatus;
893
+ }
894
+ const validationItems = useMemo(() => {
895
+ if (!collection) return enabledRegistered;
896
+ const result = [];
897
+ for (const definition of collection.enabledItems) {
898
+ const registered = registeredByName.get(definition.name);
899
+ if (registered) result.push(registered);
900
+ }
901
+ return result;
902
+ }, [
903
+ collection,
904
+ registeredByName,
905
+ enabledRegistered
906
+ ]);
907
+ const total = orderedItems.length;
908
+ const current = activeIndex < 0 ? 0 : activeIndex + 1;
909
+ const first = total > 0 && activeIndex === 0;
910
+ const last = total > 0 && activeIndex === total - 1;
911
+ const openItem = useCallback((name, target = "item") => {
912
+ if (name === activeItemName) return;
913
+ pendingFocusRef.current = {
914
+ name,
915
+ target
916
+ };
917
+ if (!isControlled) setUncontrolledItem(name);
918
+ onItemChange?.(name);
919
+ }, [
920
+ activeItemName,
921
+ isControlled,
922
+ onItemChange
923
+ ]);
924
+ const openItemAt = useCallback((index) => {
925
+ const target = orderedItems[index];
926
+ if (target) openItem(target.name);
927
+ }, [orderedItems, openItem]);
928
+ useLayoutEffect(() => {
929
+ if (total === 0) return;
930
+ if (activeIndex < 0) {
931
+ const firstName = orderedItems[0]?.name ?? "";
932
+ if (!isControlled && activeItemName === null) {
933
+ setUncontrolledItem(firstName);
934
+ return;
935
+ }
936
+ openItem(firstName);
937
+ return;
938
+ }
939
+ const pending = pendingFocusRef.current;
940
+ const changed = previousItemRef.current !== activeItemName;
941
+ previousItemRef.current = activeItemName;
942
+ if (!pending || pending.name !== activeItemName) {
943
+ if (isControlled && changed) {
944
+ pendingFocusRef.current = null;
945
+ activeItem?.focus();
946
+ }
947
+ return;
948
+ }
949
+ if (pending.target === "invalid") activeItem?.focusInvalid();
950
+ else activeItem?.focus();
951
+ pendingFocusRef.current = null;
952
+ }, [
953
+ activeItem,
954
+ activeItemName,
955
+ isControlled,
956
+ activeIndex,
957
+ orderedItems,
958
+ openItem,
959
+ total
960
+ ]);
961
+ const registerItem = useCallback((registered) => {
962
+ setRegisteredItems((previous) => {
963
+ const result = [];
964
+ for (const current of previous) if (current.element !== registered.element && current.name !== registered.name) result.push(current);
965
+ result.push(registered);
966
+ return result;
967
+ });
968
+ return () => {
969
+ setRegisteredItems((previous) => previous.filter((current) => current !== registered));
970
+ };
971
+ }, []);
972
+ const goPrevious = useCallback(() => {
973
+ if (activeIndex > 0) openItemAt(activeIndex - 1);
974
+ }, [activeIndex, openItemAt]);
975
+ const goNext = useCallback(() => {
976
+ if (!activeItem || activeIndex >= total - 1) return;
977
+ if (!activeItem.validate()) {
978
+ activeItem.focusInvalid();
979
+ return;
980
+ }
981
+ openItemAt(activeIndex + 1);
982
+ }, [
983
+ activeItem,
984
+ activeIndex,
985
+ openItemAt,
986
+ total
987
+ ]);
988
+ const advance = useCallback(() => {
989
+ if (!activeItem) return;
990
+ if (!activeItem.validate()) {
991
+ activeItem.focusInvalid();
992
+ return;
993
+ }
994
+ if (last) {
995
+ formElement?.requestSubmit();
996
+ return;
997
+ }
998
+ openItemAt(activeIndex + 1);
999
+ }, [
1000
+ activeItem,
1001
+ activeIndex,
1002
+ last,
1003
+ formElement,
1004
+ openItemAt
1005
+ ]);
1006
+ const skipCurrent = useCallback(() => {
1007
+ if (!activeItem || activeItem.required) return;
1008
+ activeItem.skip();
1009
+ if (!last) {
1010
+ openItemAt(activeIndex + 1);
1011
+ return;
1012
+ }
1013
+ queueMicrotask(() => {
1014
+ formElement?.requestSubmit();
1015
+ });
1016
+ }, [
1017
+ activeItem,
1018
+ activeIndex,
1019
+ last,
1020
+ formElement,
1021
+ openItemAt
1022
+ ]);
1023
+ /**
1024
+ * Сброс формы: ответы к начальным, открыть начальный вопрос
1025
+ * @param event - событие reset
1026
+ */
1027
+ const handleReset = (event) => {
1028
+ onReset?.(event);
1029
+ if (event.defaultPrevented) return;
1030
+ for (const registered of registeredItems) registered.reset();
1031
+ let resetName;
1032
+ if (collection) resetName = resolveItemName(collection, defaultItem);
1033
+ else resetName = enabledRegistered.find((registered) => registered.name === defaultItem)?.name ?? enabledRegistered[0]?.name;
1034
+ if (resetName) openItem(resetName);
1035
+ };
1036
+ /**
1037
+ * Отправка формы: открыть первый вопрос с ошибкой или передать событие дальше
1038
+ * @param event - событие submit
1039
+ */
1040
+ const handleSubmit = (event) => {
1041
+ const invalidItem = validationItems.find((registered) => !registered.validate());
1042
+ if (invalidItem) {
1043
+ event.preventDefault();
1044
+ openItem(invalidItem.name, "invalid");
1045
+ if (invalidItem.name === activeItemName) {
1046
+ invalidItem.focusInvalid();
1047
+ pendingFocusRef.current = null;
1048
+ }
1049
+ return;
1050
+ }
1051
+ onSubmit?.(event);
1052
+ };
1053
+ /**
1054
+ * Клавиатура: Ctrl/Cmd+Enter и Enter — дальше, стрелки — ответы и вопросы, буквы/цифры — варианты
1055
+ * @param event - событие клавиатуры
1056
+ */
1057
+ const handleKeyDown = (event) => {
1058
+ const { key, target } = event;
1059
+ if (event.defaultPrevented || event.nativeEvent.isComposing || event.keyCode === 229) return;
1060
+ if (!activeItem || !(target instanceof Element)) return;
1061
+ if (key === "Enter" && (event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey) {
1062
+ event.preventDefault();
1063
+ if (!event.repeat) advance();
1064
+ return;
1065
+ }
1066
+ if (event.metaKey || event.ctrlKey || event.altKey) return;
1067
+ if (key === "ArrowUp" || key === "ArrowDown") {
1068
+ const direction = key === "ArrowDown" ? "next" : "previous";
1069
+ if (activeItem.moveAnswerFocus(target, direction)) {
1070
+ event.preventDefault();
1071
+ return;
1072
+ }
1073
+ }
1074
+ if ((key === "ArrowLeft" || key === "ArrowRight") && !isTextEntry(target) && !isRadio(target)) {
1075
+ event.preventDefault();
1076
+ if (event.repeat) return;
1077
+ if (key === "ArrowLeft") goPrevious();
1078
+ else if (activeItem.status !== "unanswered") goNext();
1079
+ return;
1080
+ }
1081
+ if (key === "Enter") {
1082
+ const answer = activeItem.getAnswerByElement(target);
1083
+ if (!answer) return;
1084
+ event.preventDefault();
1085
+ if (!event.repeat && isAnswered(answer)) advance();
1086
+ return;
1087
+ }
1088
+ if (!shortcutMode || isTextEntry(target)) return;
1089
+ const shortcut = getShortcutByKey(key, shortcutMode);
1090
+ const answer = shortcut ? activeItem.getAnswerByShortcut(shortcut) : null;
1091
+ if (!answer) return;
1092
+ event.preventDefault();
1093
+ if (event.repeat) return;
1094
+ answer.element.focus();
1095
+ if (answer.type === "choice") answer.element.click();
1096
+ };
1097
+ const state = {
1098
+ current,
1099
+ first,
1100
+ last,
1101
+ total
1102
+ };
1103
+ const itemDefinitionByName = collection?.itemByName ?? null;
1104
+ return {
1105
+ context: useMemo(() => ({
1106
+ current,
1107
+ first,
1108
+ last,
1109
+ total,
1110
+ activeItem,
1111
+ activeItemName,
1112
+ activeItemRequired,
1113
+ activeItemStatus,
1114
+ domVersion,
1115
+ goNext,
1116
+ goPrevious,
1117
+ itemDefinitionByName,
1118
+ nativeValidation,
1119
+ registerItem,
1120
+ shortcuts: shortcutMode,
1121
+ skipCurrent
1122
+ }), [
1123
+ activeItem,
1124
+ activeItemName,
1125
+ activeItemRequired,
1126
+ activeItemStatus,
1127
+ itemDefinitionByName,
1128
+ current,
1129
+ domVersion,
1130
+ first,
1131
+ goNext,
1132
+ goPrevious,
1133
+ last,
1134
+ nativeValidation,
1135
+ registerItem,
1136
+ shortcutMode,
1137
+ skipCurrent,
1138
+ total
1139
+ ]),
1140
+ setFormElement,
1141
+ rootProps: {
1142
+ "data-shortcuts": shortcutMode ?? void 0,
1143
+ "onKeyDown": handleKeyDown,
1144
+ "onReset": handleReset,
1145
+ "onSubmit": handleSubmit
1146
+ },
1147
+ state
1148
+ };
1149
+ }
1150
+ /**
1151
+ * Корень опроса: форма
1152
+ * @param props - пропсы корня
1153
+ * @returns форма с контекстом опроса
1154
+ */
1155
+ function Root({ defaultItem, item, items, noValidate = true, onItemChange, onReset, onSubmit, ref, shortcuts, ...props }) {
1156
+ const { context, setFormElement, rootProps, state } = useRoot({
1157
+ defaultItem,
1158
+ item,
1159
+ items,
1160
+ noValidate,
1161
+ onItemChange,
1162
+ onReset,
1163
+ onSubmit,
1164
+ shortcuts
1165
+ });
1166
+ const element = useRender({
1167
+ defaultTagName: "form",
1168
+ ref: [setFormElement, ref ?? null],
1169
+ state,
1170
+ props: mergeProps({
1171
+ ...rootProps,
1172
+ noValidate
1173
+ }, props)
1174
+ });
1175
+ return createElement(RootContext, { value: context }, element);
1176
+ }
1177
+ /**
1178
+ * Прогресс опроса: role=progressbar с номером вопроса
1179
+ * @param props - пропсы прогресса
1180
+ * @returns индикатор прогресса
1181
+ */
1182
+ function Progress({ render, ref, ...props }) {
1183
+ const { current, first, last, total } = useRootContext("QuestionnaireProgress");
1184
+ const hasItems = total > 0;
1185
+ return useRender({
1186
+ defaultTagName: "div",
1187
+ render,
1188
+ ref,
1189
+ state: {
1190
+ current,
1191
+ first,
1192
+ last,
1193
+ total
1194
+ },
1195
+ props: mergeProps({
1196
+ "aria-live": "polite",
1197
+ "aria-valuemax": hasItems ? total : void 0,
1198
+ "aria-valuemin": hasItems ? 1 : void 0,
1199
+ "aria-valuenow": hasItems ? current : void 0,
1200
+ "role": "progressbar"
1201
+ }, props)
1202
+ });
1203
+ }
1204
+ /**
1205
+ * Вопрос: fieldset, виден, только пока открыт
1206
+ * @param props - пропсы вопроса
1207
+ * @returns вопрос с контекстом
1208
+ */
1209
+ function Item({ "aria-describedby": ariaDescribedBy, "aria-keyshortcuts": ariaKeyShortcuts, disabled = false, invalid = false, multiple = false, name, onStatusChange, ref, required = false, ...props }) {
1210
+ const { context, setItemElement, itemProps, state } = useItem({
1211
+ ariaDescribedBy,
1212
+ ariaKeyShortcuts,
1213
+ disabled,
1214
+ invalid,
1215
+ multiple,
1216
+ name,
1217
+ onStatusChange,
1218
+ required
1219
+ });
1220
+ const element = useRender({
1221
+ defaultTagName: "fieldset",
1222
+ ref: [setItemElement, ref ?? null],
1223
+ state,
1224
+ stateAttributesMapping: { active: (value) => value ? { "data-active": "" } : null },
1225
+ props: mergeProps(itemProps, props)
1226
+ });
1227
+ return createElement(ItemContext, { value: context }, element);
1228
+ }
1229
+ /**
1230
+ * Заголовок вопроса: legend
1231
+ * @param props - пропсы заголовка
1232
+ * @returns legend
1233
+ */
1234
+ function Title({ render, ref, ...props }) {
1235
+ useItemContext("QuestionnaireTitle");
1236
+ return useRender({
1237
+ defaultTagName: "legend",
1238
+ render,
1239
+ ref,
1240
+ props
1241
+ });
1242
+ }
1243
+ /**
1244
+ * Описание вопроса: попадает в aria-describedby вопроса
1245
+ * @param props - пропсы описания
1246
+ * @returns абзац
1247
+ */
1248
+ function Description({ id, render, ref, ...props }) {
1249
+ const { registerDescription } = useItemContext("QuestionnaireDescription");
1250
+ const generatedId = useId();
1251
+ const descriptionId = id ?? generatedId;
1252
+ useLayoutEffect(() => registerDescription(descriptionId), [descriptionId, registerDescription]);
1253
+ return useRender({
1254
+ defaultTagName: "p",
1255
+ render,
1256
+ ref,
1257
+ props: mergeProps({ id: descriptionId }, props)
1258
+ });
1259
+ }
1260
+ /**
1261
+ * Список вариантов
1262
+ * @param props - пропсы списка
1263
+ * @returns div
1264
+ */
1265
+ function Choices({ render, ref, ...props }) {
1266
+ const { shortcuts } = useItemContext("QuestionnaireChoices");
1267
+ return useRender({
1268
+ defaultTagName: "div",
1269
+ render,
1270
+ ref,
1271
+ state: { shortcuts },
1272
+ props
1273
+ });
1274
+ }
1275
+ /**
1276
+ * Атрибуты выбранного и невыбранного варианта
1277
+ * @param checked - вариант выбран
1278
+ * @returns data-checked или data-unchecked
1279
+ */
1280
+ function getCheckedAttributes(checked) {
1281
+ if (checked) return { "data-checked": "" };
1282
+ return { "data-unchecked": "" };
1283
+ }
1284
+ /**
1285
+ * Вариант ответа: label вокруг input
1286
+ * @param props - пропсы варианта
1287
+ * @returns label с контекстом варианта
1288
+ */
1289
+ function Choice({ checked, defaultChecked = false, disabled = false, onChange, render, ref, value, ...props }) {
1290
+ const choice = useChoice({
1291
+ checked,
1292
+ defaultChecked,
1293
+ disabled,
1294
+ onChange,
1295
+ value
1296
+ });
1297
+ const element = useRender({
1298
+ defaultTagName: "label",
1299
+ render,
1300
+ ref,
1301
+ state: choice.state,
1302
+ stateAttributesMapping: { checked: getCheckedAttributes },
1303
+ props
1304
+ });
1305
+ return createElement(ChoiceContext, { value: choice }, element);
1306
+ }
1307
+ /**
1308
+ * input варианта: радиокнопка или флажок
1309
+ * @param props - пропсы input
1310
+ * @returns input
1311
+ */
1312
+ function ChoiceInput({ render, ref, ...props }) {
1313
+ const { inputProps, state } = useChoiceContext("QuestionnaireChoiceInput");
1314
+ const { ref: inputRef, ...ownProps } = inputProps;
1315
+ return useRender({
1316
+ defaultTagName: "input",
1317
+ render,
1318
+ ref: [inputRef, ref ?? null],
1319
+ state,
1320
+ stateAttributesMapping: { checked: getCheckedAttributes },
1321
+ props: mergeProps(ownProps, props)
1322
+ });
1323
+ }
1324
+ /**
1325
+ * Подпись варианта
1326
+ * @param props - пропсы подписи
1327
+ * @returns span
1328
+ */
1329
+ function ChoiceLabel({ render, ref, ...props }) {
1330
+ useChoiceContext("QuestionnaireChoiceLabel");
1331
+ return useRender({
1332
+ defaultTagName: "span",
1333
+ render,
1334
+ ref,
1335
+ props
1336
+ });
1337
+ }
1338
+ /**
1339
+ * Подсказка клавиши варианта: скрыта, если клавиши нет
1340
+ * @param props - пропсы подсказки
1341
+ * @returns span
1342
+ */
1343
+ function ChoiceShortcut({ children, render, ref, ...props }) {
1344
+ const { state } = useChoiceContext("QuestionnaireChoiceShortcut");
1345
+ const { shortcut } = state;
1346
+ return useRender({
1347
+ defaultTagName: "span",
1348
+ render,
1349
+ ref,
1350
+ state: { shortcut },
1351
+ props: mergeProps({
1352
+ "aria-hidden": true,
1353
+ "children": children ?? shortcut,
1354
+ "hidden": shortcut === null
1355
+ }, props)
1356
+ });
1357
+ }
1358
+ /**
1359
+ * Атрибуты заполненного и пустого поля
1360
+ * @param filled - в поле есть текст
1361
+ * @returns data-filled или data-empty
1362
+ */
1363
+ function getFilledAttributes(filled) {
1364
+ if (filled) return { "data-filled": "" };
1365
+ return { "data-empty": "" };
1366
+ }
1367
+ /**
1368
+ * Поле свободного ответа: ответ, только пока в нём есть текст
1369
+ * @param props - пропсы поля
1370
+ * @returns input
1371
+ */
1372
+ function Input({ defaultValue, disabled = false, onChange, ref, render, type = "text", value, ...props }) {
1373
+ const { inputRef, inputProps, state } = useInput({
1374
+ defaultValue,
1375
+ disabled,
1376
+ onChange,
1377
+ type,
1378
+ value
1379
+ });
1380
+ return useRender({
1381
+ defaultTagName: "input",
1382
+ render,
1383
+ ref: [inputRef, ref ?? null],
1384
+ state,
1385
+ stateAttributesMapping: { filled: getFilledAttributes },
1386
+ props: mergeProps(inputProps, props)
1387
+ });
1388
+ }
1389
+ /**
1390
+ * Ошибка вопроса: видна, когда ответ не прошёл проверку
1391
+ * @param props - пропсы ошибки
1392
+ * @returns абзац с role=alert
1393
+ */
1394
+ function ErrorMessage({ id, render, ref, ...props }) {
1395
+ const { invalid, registerError } = useItemContext("QuestionnaireError");
1396
+ const generatedId = useId();
1397
+ const errorId = id ?? generatedId;
1398
+ const role = invalid ? "alert" : void 0;
1399
+ useLayoutEffect(() => registerError(errorId), [errorId, registerError]);
1400
+ return useRender({
1401
+ defaultTagName: "p",
1402
+ render,
1403
+ ref,
1404
+ state: { invalid },
1405
+ props: mergeProps({
1406
+ hidden: !invalid,
1407
+ id: errorId,
1408
+ role
1409
+ }, props)
1410
+ });
1411
+ }
1412
+ /**
1413
+ * Атрибуты видимой и скрытой кнопки
1414
+ * @param visible - кнопка видна
1415
+ * @returns data-visible или data-hidden
1416
+ */
1417
+ function getVisibleAttributes(visible) {
1418
+ if (visible) return { "data-visible": "" };
1419
+ return { "data-hidden": "" };
1420
+ }
1421
+ /**
1422
+ * Кнопка навигации: скрыта и недоступна, когда неприменима
1423
+ * @param parameters - параметры кнопки
1424
+ * @returns кнопка
1425
+ */
1426
+ function useNavigationButton({ props, onClick, shortcut, status, visible }) {
1427
+ const { disabled = false, render, ref, tabIndex, ...ownProps } = props;
1428
+ const activeShortcut = visible && !disabled ? shortcut ?? null : null;
1429
+ return useRender({
1430
+ defaultTagName: "button",
1431
+ render,
1432
+ ref,
1433
+ state: {
1434
+ disabled,
1435
+ shortcut: activeShortcut,
1436
+ status,
1437
+ visible
1438
+ },
1439
+ stateAttributesMapping: { visible: getVisibleAttributes },
1440
+ props: mergeProps({
1441
+ "aria-hidden": !visible || void 0,
1442
+ "aria-keyshortcuts": activeShortcut ?? void 0,
1443
+ "disabled": disabled,
1444
+ "hidden": !visible,
1445
+ "inert": !visible,
1446
+ "onClick": onClick,
1447
+ "tabIndex": visible ? tabIndex : -1
1448
+ }, ownProps)
1449
+ });
1450
+ }
1451
+ /**
1452
+ * Кнопка «Назад»: видна, если открыт не первый вопрос
1453
+ * @param props - пропсы кнопки
1454
+ * @returns кнопка
1455
+ */
1456
+ function Previous({ onClick, type = "button", ...props }) {
1457
+ const root = useRootContext("QuestionnairePrevious");
1458
+ /**
1459
+ * Нажатие: к предыдущему вопросу
1460
+ * @param event - событие клика
1461
+ */
1462
+ const handleClick = (event) => {
1463
+ onClick?.(event);
1464
+ if (!event.defaultPrevented) root.goPrevious();
1465
+ };
1466
+ return useNavigationButton({
1467
+ props: {
1468
+ ...props,
1469
+ type
1470
+ },
1471
+ onClick: handleClick,
1472
+ status: root.activeItemStatus,
1473
+ visible: root.total > 1 && !root.first
1474
+ });
1475
+ }
1476
+ /**
1477
+ * Кнопка «Пропустить»: видна у необязательного вопроса
1478
+ * @param props - пропсы кнопки
1479
+ * @returns кнопка
1480
+ */
1481
+ function Skip({ onClick, type = "button", ...props }) {
1482
+ const root = useRootContext("QuestionnaireSkip");
1483
+ /**
1484
+ * Нажатие: пропустить вопрос
1485
+ * @param event - событие клика
1486
+ */
1487
+ const handleClick = (event) => {
1488
+ onClick?.(event);
1489
+ if (!event.defaultPrevented) root.skipCurrent();
1490
+ };
1491
+ return useNavigationButton({
1492
+ props: {
1493
+ ...props,
1494
+ type
1495
+ },
1496
+ onClick: handleClick,
1497
+ status: root.activeItemStatus,
1498
+ visible: root.activeItemRequired === false
1499
+ });
1500
+ }
1501
+ /**
1502
+ * Кнопка «Далее»: видна, если открыт не последний вопрос
1503
+ * @param props - пропсы кнопки
1504
+ * @returns кнопка
1505
+ */
1506
+ function Next({ onClick, type = "button", ...props }) {
1507
+ const root = useRootContext("QuestionnaireNext");
1508
+ /**
1509
+ * Нажатие: к следующему вопросу
1510
+ * @param event - событие клика
1511
+ */
1512
+ const handleClick = (event) => {
1513
+ onClick?.(event);
1514
+ if (!event.defaultPrevented) root.goNext();
1515
+ };
1516
+ return useNavigationButton({
1517
+ props: {
1518
+ ...props,
1519
+ type
1520
+ },
1521
+ onClick: handleClick,
1522
+ shortcut: "Enter",
1523
+ status: root.activeItemStatus,
1524
+ visible: root.total > 1 && !root.last
1525
+ });
1526
+ }
1527
+ /**
1528
+ * Кнопка отправки: видна на последнем вопросе
1529
+ * @param props - пропсы кнопки
1530
+ * @returns кнопка
1531
+ */
1532
+ function Submit({ type = "submit", ...props }) {
1533
+ const root = useRootContext("QuestionnaireSubmit");
1534
+ return useNavigationButton({
1535
+ props: {
1536
+ ...props,
1537
+ type
1538
+ },
1539
+ shortcut: "Enter",
1540
+ status: root.activeItemStatus,
1541
+ visible: root.total > 0 && root.last
1542
+ });
1543
+ }
1544
+ /** Части опроса без оформления */
1545
+ const QuestionnairePrimitive = {
1546
+ Root,
1547
+ Progress,
1548
+ Item,
1549
+ Title,
1550
+ Description,
1551
+ Choices,
1552
+ Choice,
1553
+ ChoiceInput,
1554
+ ChoiceLabel,
1555
+ ChoiceShortcut,
1556
+ Input,
1557
+ Error: ErrorMessage,
1558
+ Previous,
1559
+ Skip,
1560
+ Next,
1561
+ Submit
1562
+ };
1563
+ export { QuestionnairePrimitive };