adata-ui 2.1.41-beta.6 → 2.1.41-beta.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 (587) hide show
  1. package/-error.vue +170 -170
  2. package/.editorconfig +12 -12
  3. package/.gitlab-ci.yml +16 -16
  4. package/.nuxtrc +1 -1
  5. package/.nvmrc +1 -1
  6. package/.playground/app.config.ts +5 -5
  7. package/.playground/app.vue +102 -102
  8. package/.playground/nuxt.config.ts +4 -4
  9. package/CLAUDE.md +63 -63
  10. package/README.md +75 -75
  11. package/app.config.ts +28 -28
  12. package/app.vue +8 -8
  13. package/assets/styles/main.css +12 -12
  14. package/assets/styles/theme.css +590 -583
  15. package/assets/styles/ui.css +247 -247
  16. package/components/elements/README.md +1 -1
  17. package/components/elements/a-select-row/ASelectRowV2.vue +210 -210
  18. package/components/elements/a-select-row/index.vue +139 -139
  19. package/components/elements/accordion/AAccordion.vue +199 -199
  20. package/components/elements/accordion/AAccordionGroup/AAccordionGroup.vue +145 -145
  21. package/components/elements/accordion/AAccordionGroup/types.ts +20 -20
  22. package/components/elements/accordion/AAccordionGroup/ui.config.ts +22 -22
  23. package/components/elements/accordion/types.ts +14 -14
  24. package/components/elements/accordion/ui.config.ts +22 -22
  25. package/components/elements/alert/AAlert.vue +52 -52
  26. package/components/elements/banner/ABanner.vue +49 -49
  27. package/components/elements/bg-block/ABgBlock.vue +11 -11
  28. package/components/elements/button/AButton.vue +191 -191
  29. package/components/elements/button/AButtonV2.vue +179 -179
  30. package/components/elements/button/types.ts +24 -24
  31. package/components/elements/button-login/index.vue +19 -19
  32. package/components/elements/calendar/ACalendar.vue +83 -83
  33. package/components/elements/carousel/ACarousel.vue +159 -159
  34. package/components/elements/carousel/config.ui.ts +15 -15
  35. package/components/elements/carousel/types.ts +18 -18
  36. package/components/elements/chip-wrapper/AChipWrapper.vue +81 -81
  37. package/components/elements/chip-wrapper/types.ts +12 -12
  38. package/components/elements/chips/AChips.vue +33 -33
  39. package/components/elements/companies/AOurClients.vue +47 -47
  40. package/components/elements/count-up/ACountUp.vue +54 -54
  41. package/components/elements/curve-block/ACurveBlock.vue +259 -259
  42. package/components/elements/curve-block-v2/ACurveBlockV2.vue +122 -122
  43. package/components/elements/digit-badge/ADigitBadge.vue +86 -86
  44. package/components/elements/empty-box/empty-box-V2.vue +92 -92
  45. package/components/elements/error-template/index.vue +121 -119
  46. package/components/elements/feature-description/AFeatureDescription.vue +112 -112
  47. package/components/elements/illustrations/noAccess.vue +71 -71
  48. package/components/elements/infinite-carousel/AInfiniteCarousel.vue +85 -85
  49. package/components/elements/infinite-scroll/AInfiniteScroll.vue +33 -33
  50. package/components/elements/leave-note/ALeaveNote.vue +102 -102
  51. package/components/elements/pagination/APagination.vue +110 -110
  52. package/components/elements/photos-animation/APhotosAnimation.vue +85 -85
  53. package/components/elements/section-heading/ASectionHeading.vue +35 -35
  54. package/components/elements/segmented/ASegmentedV2.vue +154 -154
  55. package/components/elements/select/ACascadeSelectV2.vue +710 -0
  56. package/components/elements/select/ASelect.vue +448 -448
  57. package/components/elements/select/ASelectMobile.vue +186 -186
  58. package/components/elements/select/ASelectV2.vue +507 -580
  59. package/components/elements/select/cascade-utils.ts +115 -0
  60. package/components/elements/select/types.ts +27 -0
  61. package/components/elements/select/ui/cascade-drawer.vue +146 -0
  62. package/components/elements/select/ui/cascade-panel.vue +237 -0
  63. package/components/elements/select/ui/select-button.vue +112 -112
  64. package/components/elements/select/ui/select-list.vue +99 -99
  65. package/components/elements/select/ui/select-trigger.vue +167 -0
  66. package/components/elements/show-more/AShowMore.vue +35 -35
  67. package/components/elements/show-more/AShowMoreV2.vue +26 -26
  68. package/components/elements/skeleton/ASkeleton.vue +44 -44
  69. package/components/elements/slider/ASlider.vue +175 -175
  70. package/components/elements/slider/ASliderItem.vue +31 -31
  71. package/components/elements/slider/ASliderV2.vue +182 -182
  72. package/components/elements/slider/types.ts +21 -21
  73. package/components/elements/star-rating/AStarRating.vue +189 -189
  74. package/components/elements/star-rating/types.ts +7 -7
  75. package/components/elements/star-rating/ui/Star.vue +74 -74
  76. package/components/elements/status-badge/AStatusBadge.vue +125 -125
  77. package/components/elements/step-slider/AStepSliderColCard.vue +270 -270
  78. package/components/elements/step-slider/AStepSliderColNumber.vue +199 -199
  79. package/components/elements/table/ATable.vue +93 -93
  80. package/components/elements/table/table.config.ts +32 -32
  81. package/components/elements/tree-select/ATreeSelect.vue +157 -157
  82. package/components/elements/tree-select/TreeService.ts +184 -184
  83. package/components/elements/tree-select/components/ATreeSelectNode.vue +104 -104
  84. package/components/elements/tree-select/types.ts +63 -63
  85. package/components/elements/tree-select/utils.ts +30 -30
  86. package/components/elements/tree-select-mobile/ATreeSelectMobile.vue +79 -79
  87. package/components/elements/tree-select-mobile/components/ATreeSelectNodesMobile.vue +175 -175
  88. package/components/elements/tree-select-modal/ATreeSelectModal.vue +217 -217
  89. package/components/features/color-mode/AColorMode.client.vue +74 -74
  90. package/components/features/dropdown/ADropdown.vue +127 -127
  91. package/components/features/dropdown/ADropdownV2.vue +140 -140
  92. package/components/features/go-top/GoTop.vue +67 -67
  93. package/components/features/lang-switcher/lang-switcher.vue +158 -158
  94. package/components/features/payment/banner/BasicPlusLimitBanner.vue +84 -84
  95. package/components/features/payment/banner/PaymentBanner.vue +88 -88
  96. package/components/features/payment/banner/UpSellBanner.vue +105 -105
  97. package/components/features/payment/process/PaymentKaspiQrSidePanel.vue +180 -180
  98. package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +123 -123
  99. package/components/features/payment/process/PaymentMethodSidePanel.vue +138 -138
  100. package/components/features/payment/process/PaymentProcess.vue +158 -158
  101. package/components/features/payment/process/PaymentTopUpSidePanel.vue +119 -119
  102. package/components/features/pk-mobile-services/APkMobileServices.vue +107 -107
  103. package/components/features//321/201hange-version/AChangeVersion.vue +20 -20
  104. package/components/forms/README.md +1 -1
  105. package/components/forms/checkbox/ACheckbox.vue +149 -149
  106. package/components/forms/checkbox/ACheckboxV2.vue +229 -229
  107. package/components/forms/feedback-form/FeedbackForm.vue +177 -177
  108. package/components/forms/feedback-form/FeedbackFormV2.vue +183 -183
  109. package/components/forms/input/AInputV2.vue +560 -560
  110. package/components/forms/input/button/AInputButton.vue +53 -53
  111. package/components/forms/input/date/AInputDate.vue +217 -217
  112. package/components/forms/input/password/AInputPassword.vue +59 -59
  113. package/components/forms/input/standard/AInputStandard.vue +424 -424
  114. package/components/forms/input/textarea/ATextarea.vue +207 -207
  115. package/components/forms/input/textarea/ATextareaV2.vue +253 -253
  116. package/components/forms/radio-button/ARadioButton.vue +95 -95
  117. package/components/forms/request-demo/ARequestDemo.vue +108 -108
  118. package/components/forms/toggle/AToggle.vue +157 -157
  119. package/components/forms/toggle/AToggleV2.vue +72 -72
  120. package/components/forms/toggle/types.ts +16 -16
  121. package/components/modals/AConfirmationEmail.vue +40 -40
  122. package/components/modals/AboutTariffModal.vue +115 -115
  123. package/components/modals/AnotherDeviceModal.vue +65 -65
  124. package/components/modals/ApplicationAcceptedModal.vue +45 -45
  125. package/components/modals/ConnectingTariffModal.vue +60 -60
  126. package/components/modals/ContactsMobileModel.vue +182 -182
  127. package/components/modals/ContentNavigationModal.vue +100 -100
  128. package/components/modals/Insufficient-funds-modal.vue +62 -62
  129. package/components/modals/LimitReachedModal.vue +44 -44
  130. package/components/modals/NoAccessModal.vue +39 -39
  131. package/components/modals/PaymentMethodModal.vue +101 -101
  132. package/components/modals/RedirectsBanksModal.vue +69 -69
  133. package/components/modals/ReplenishModal.vue +132 -132
  134. package/components/modals/ReportBugConfirmModal.vue +32 -32
  135. package/components/modals/ReportBugModal.vue +172 -172
  136. package/components/modals/Resend.vue +81 -81
  137. package/components/modals/SubmitApplicationModal.vue +126 -126
  138. package/components/modals/id/AuthModal.vue +72 -72
  139. package/components/modals/id/IdAutoLogoutModal.vue +44 -44
  140. package/components/modals/id/IdBanner.vue +41 -41
  141. package/components/modals/id/IdConfirmAccountOtpModal.vue +182 -182
  142. package/components/modals/id/IdConfirmSuccessfulModal.vue +41 -41
  143. package/components/modals/id/IdLoginModal.vue +336 -336
  144. package/components/modals/id/IdModals.vue +129 -129
  145. package/components/modals/id/IdNewPasswordModal.vue +129 -129
  146. package/components/modals/id/IdOtpInput.vue +156 -156
  147. package/components/modals/id/IdPasswordSuccessfulModal.vue +25 -25
  148. package/components/modals/id/IdRecoveryModal.vue +117 -117
  149. package/components/modals/id/IdRegistrationModal.vue +215 -215
  150. package/components/modals/id/IdResetPasswordOtpModal.vue +159 -159
  151. package/components/modals/id/IdTwoFactorModal.vue +126 -126
  152. package/components/navigation/README.md +1 -1
  153. package/components/navigation/bottom-navigation/ABottomNavigation.vue +106 -106
  154. package/components/navigation/breadcrumbs/ABreadcrumbs.vue +128 -128
  155. package/components/navigation/breadcrumbs/types.ts +8 -8
  156. package/components/navigation/footer/AFooter.vue +283 -283
  157. package/components/navigation/footer/ui/a-footer-accordion.vue +118 -118
  158. package/components/navigation/header/AHeader.vue +241 -241
  159. package/components/navigation/header/AlmatyContacts.vue +145 -145
  160. package/components/navigation/header/AstanaContacts.vue +67 -67
  161. package/components/navigation/header/CardGallery.vue +252 -252
  162. package/components/navigation/header/ContactMenu.vue +48 -48
  163. package/components/navigation/header/HeaderLink.vue +250 -250
  164. package/components/navigation/header/HeaderUsage.vue +139 -139
  165. package/components/navigation/header/ListItem.vue +32 -32
  166. package/components/navigation/header/NavCard.vue +42 -42
  167. package/components/navigation/header/NavList.vue +73 -73
  168. package/components/navigation/header/NotificationsMenu.vue +50 -50
  169. package/components/navigation/header/ProductMenu.vue +101 -101
  170. package/components/navigation/header/ProfileMenu.vue +168 -168
  171. package/components/navigation/header/SystemNotification.vue +108 -108
  172. package/components/navigation/header/types.ts +28 -28
  173. package/components/navigation/line-tabs/ALineTabs.vue +112 -112
  174. package/components/navigation/line-tabs/types.ts +21 -21
  175. package/components/navigation/mobile-navigation/AMobileNavigation.vue +112 -112
  176. package/components/navigation/pill-tabs/APillTabs.vue +179 -179
  177. package/components/navigation/pill-tabs/APillTabsV2.vue +241 -241
  178. package/components/navigation/pill-tabs/types.ts +24 -24
  179. package/components/navigation/side-navigation/ASideNavigation.vue +406 -406
  180. package/components/overlays/README.md +1 -1
  181. package/components/overlays/drawer/ADrawerV2.vue +342 -335
  182. package/components/overlays/modal/AModal.vue +335 -335
  183. package/components/overlays/modal/AModalV2.vue +401 -400
  184. package/components/overlays/side-panel/ASidePanel.vue +445 -445
  185. package/components/overlays/sideover/ASlideover.vue +351 -351
  186. package/components/overlays/tooltip/ATooltip.vue +238 -238
  187. package/components/overlays/tooltip/ATooltipV2.vue +226 -226
  188. package/components/overlays/tooltip/types.ts +26 -26
  189. package/components/overlays/tooltip/useTooltipTrigger.ts +101 -101
  190. package/components/transitions/ATransitionHeight.vue +67 -67
  191. package/components/utils/index.ts +19 -19
  192. package/components/utils/removeTrailingSlash.ts +7 -7
  193. package/composables/highlight.ts +19 -19
  194. package/composables/modalsState.ts +8 -8
  195. package/composables/projectState.ts +2 -2
  196. package/composables/useAccessTokenCookie.ts +25 -25
  197. package/composables/useActiveNavigation.ts +84 -84
  198. package/composables/useAdaptive.ts +46 -46
  199. package/composables/useBuyTariffs.ts +91 -91
  200. package/composables/useCarouselScroll.ts +49 -49
  201. package/composables/useChipOverflow.ts +92 -92
  202. package/composables/useHeaderNavigationLinks.ts +519 -519
  203. package/composables/useIdModals.ts +36 -36
  204. package/composables/usePayment.ts +78 -78
  205. package/composables/useUIValidation.ts +16 -16
  206. package/composables/useUrls.ts +23 -23
  207. package/eslint.config.mjs +58 -58
  208. package/i18n/i18n.config.ts +21 -21
  209. package/i18n.config.ts +21 -21
  210. package/icons/adata-logo.vue +17 -17
  211. package/icons/ai-assistant.vue +13 -13
  212. package/icons/akimat.vue +20 -20
  213. package/icons/archive.vue +16 -16
  214. package/icons/arrow/arrow-bottom-left-on-square.vue +5 -5
  215. package/icons/arrow/arrow-circle-down.vue +16 -16
  216. package/icons/arrow/arrow-circle-reset.vue +16 -16
  217. package/icons/arrow/arrow-corner.vue +9 -9
  218. package/icons/arrow/arrow-down-left.vue +6 -0
  219. package/icons/arrow/arrow-graph-down.vue +13 -13
  220. package/icons/arrow/arrow-graph-up.vue +14 -14
  221. package/icons/arrow/arrow-left-right.vue +8 -8
  222. package/icons/arrow/arrow-left.vue +14 -14
  223. package/icons/arrow/arrow-right.vue +14 -14
  224. package/icons/arrow/arrow-square-down.vue +15 -15
  225. package/icons/arrow/arrow-top-right-on-square.vue +6 -6
  226. package/icons/arrow/arrow-up-right.vue +6 -0
  227. package/icons/arrow/arrows-in.vue +11 -11
  228. package/icons/arrow/arrows-out.vue +11 -11
  229. package/icons/arrow/arrows-resize-in.vue +38 -38
  230. package/icons/arrow/arrows-resize-out.vue +17 -17
  231. package/icons/arrow-currency-gray.vue +5 -5
  232. package/icons/arrow-currency-green.vue +5 -5
  233. package/icons/arrow-currency-red.vue +5 -5
  234. package/icons/arrow-side-up.vue +6 -6
  235. package/icons/avatar.vue +12 -12
  236. package/icons/bank.vue +5 -5
  237. package/icons/bar-chart-3.vue +3 -3
  238. package/icons/bill.vue +3 -3
  239. package/icons/block.vue +16 -16
  240. package/icons/book-open.vue +3 -3
  241. package/icons/bookmark/bookmark-filled.vue +19 -19
  242. package/icons/bookmark/bookmark.vue +5 -5
  243. package/icons/bot.vue +52 -52
  244. package/icons/braces.vue +3 -3
  245. package/icons/briefcase.vue +14 -14
  246. package/icons/browsers/browser-duck.vue +65 -65
  247. package/icons/browsers/browser-google.vue +30 -30
  248. package/icons/browsers/browser-yandex.vue +13 -13
  249. package/icons/building-vector.vue +7 -7
  250. package/icons/building.vue +9 -9
  251. package/icons/calculator.vue +7 -7
  252. package/icons/calendar.vue +24 -24
  253. package/icons/car-side.vue +16 -16
  254. package/icons/car.vue +7 -7
  255. package/icons/cart.vue +15 -15
  256. package/icons/chart-bar.vue +5 -5
  257. package/icons/chart-no-axes-column.vue +3 -3
  258. package/icons/chart-pie.vue +16 -16
  259. package/icons/check/check-circle.vue +6 -6
  260. package/icons/check/check-outline.vue +3 -3
  261. package/icons/check/check-shield.vue +14 -14
  262. package/icons/check/check.vue +17 -17
  263. package/icons/check/checkmark-circle.vue +6 -6
  264. package/icons/check-sb.vue +7 -7
  265. package/icons/checkbox/checkbox-active.vue +17 -17
  266. package/icons/checkbox/checkbox-empty.vue +10 -10
  267. package/icons/checkbox/checkbox-intermediate.vue +7 -7
  268. package/icons/chevron/chevron-down-outline.vue +3 -3
  269. package/icons/chevron/chevron-down.vue +5 -5
  270. package/icons/chevron/chevron-left.vue +5 -5
  271. package/icons/chevron/chevron-right.vue +5 -5
  272. package/icons/chevron/chevron-up.vue +5 -5
  273. package/icons/chevron/double-chevron-right.vue +12 -12
  274. package/icons/clipboard-list.vue +3 -3
  275. package/icons/clipboard-text.vue +7 -7
  276. package/icons/clock-outline.vue +3 -3
  277. package/icons/clock-time.vue +24 -24
  278. package/icons/clock.vue +25 -25
  279. package/icons/close.vue +16 -16
  280. package/icons/cloud/cloud-upload.vue +3 -3
  281. package/icons/code.vue +9 -9
  282. package/icons/company/company-bazis.vue +21 -21
  283. package/icons/company/company-bereke.vue +25 -25
  284. package/icons/company/company-bigroup.vue +16 -16
  285. package/icons/company/company-erg.vue +17 -17
  286. package/icons/company/company-forte.vue +21 -21
  287. package/icons/company/company-halyk.vue +15 -15
  288. package/icons/company/company-jusan.vue +31 -31
  289. package/icons/company/company-kaspi.vue +14 -14
  290. package/icons/company/company-kazzinc.vue +22 -22
  291. package/icons/company/company-mycar.vue +13 -13
  292. package/icons/company/company-samruk.vue +21 -21
  293. package/icons/company-egov-small.vue +15 -15
  294. package/icons/company.vue +8 -8
  295. package/icons/compare-outlined.vue +8 -8
  296. package/icons/copy.vue +7 -7
  297. package/icons/crown.vue +6 -0
  298. package/icons/currency/currency-dollar.vue +16 -16
  299. package/icons/currency/currency-down.vue +22 -22
  300. package/icons/currency/currency-eur.vue +57 -57
  301. package/icons/currency/currency-rub.vue +7 -7
  302. package/icons/currency/currency-tenge.vue +9 -9
  303. package/icons/currency/currency-usd.vue +221 -221
  304. package/icons/currency/currency-yuan.vue +25 -25
  305. package/icons/database.vue +3 -3
  306. package/icons/default-avatar.vue +20 -20
  307. package/icons/delete.vue +7 -7
  308. package/icons/document.vue +5 -5
  309. package/icons/dots-horizontal.vue +31 -31
  310. package/icons/download-line.vue +13 -13
  311. package/icons/download-outline.vue +3 -3
  312. package/icons/download-rounded.vue +15 -15
  313. package/icons/download.vue +11 -11
  314. package/icons/edit.vue +7 -7
  315. package/icons/education.vue +16 -16
  316. package/icons/egov-small.vue +7 -7
  317. package/icons/egov.vue +41 -41
  318. package/icons/elicense.vue +38 -38
  319. package/icons/excel-icon.vue +14 -14
  320. package/icons/expand-window.vue +7 -7
  321. package/icons/eye-closed.vue +26 -26
  322. package/icons/eye-open.vue +7 -7
  323. package/icons/eye-opened.vue +23 -23
  324. package/icons/favourite-bookmark-outline.vue +17 -17
  325. package/icons/file/file-check-corner.vue +3 -3
  326. package/icons/file/file-spreadsheet.vue +3 -3
  327. package/icons/file/file.vue +16 -16
  328. package/icons/file/files.vue +16 -16
  329. package/icons/file-json.vue +3 -3
  330. package/icons/file-pen.vue +31 -31
  331. package/icons/file-search.vue +3 -3
  332. package/icons/file-stack.vue +31 -31
  333. package/icons/file-text.vue +3 -3
  334. package/icons/file-types/excel.vue +131 -131
  335. package/icons/file-types/html.vue +13 -13
  336. package/icons/file-types/jpeg.vue +16 -16
  337. package/icons/file-types/pdf.vue +11 -11
  338. package/icons/file-types/png.vue +11 -11
  339. package/icons/file-types/rar.vue +11 -11
  340. package/icons/file-types/word.vue +50 -50
  341. package/icons/file-types/zip.vue +11 -11
  342. package/icons/filter.vue +5 -5
  343. package/icons/flag.vue +7 -7
  344. package/icons/flask-conical.vue +3 -3
  345. package/icons/folder-open.vue +3 -3
  346. package/icons/gauge.vue +17 -17
  347. package/icons/gift-outline.vue +3 -3
  348. package/icons/gift.vue +8 -8
  349. package/icons/git-compare.vue +38 -38
  350. package/icons/git-fork.vue +3 -3
  351. package/icons/globe-2.vue +3 -3
  352. package/icons/globe.vue +16 -16
  353. package/icons/google.vue +41 -41
  354. package/icons/gradiend-icons/files-gradient.vue +22 -22
  355. package/icons/gradiend-icons/hammer-gradient.vue +62 -62
  356. package/icons/graduation-cap.vue +31 -31
  357. package/icons/graph-loading.vue +34 -34
  358. package/icons/graph-pie.vue +14 -14
  359. package/icons/hand/hand-thumb-up-filled.vue +5 -5
  360. package/icons/hand/hand-thumb-up.vue +5 -5
  361. package/icons/hand-with-phone/hand-with-phone-dark.vue +52 -52
  362. package/icons/hand-with-phone/hand-with-phone-light.vue +52 -52
  363. package/icons/handshake.vue +5 -5
  364. package/icons/hcheck.vue +14 -14
  365. package/icons/hdocument.vue +7 -7
  366. package/icons/headset.vue +6 -6
  367. package/icons/help-question-circle.vue +26 -26
  368. package/icons/history.vue +5 -5
  369. package/icons/home.vue +14 -14
  370. package/icons/horizontal-more.vue +13 -13
  371. package/icons/hot-line.vue +6 -6
  372. package/icons/hummer.vue +16 -16
  373. package/icons/id.vue +7 -7
  374. package/icons/in-row-type.vue +46 -46
  375. package/icons/in-table-type.vue +29 -29
  376. package/icons/info/info-circle.vue +29 -29
  377. package/icons/invoice.vue +8 -8
  378. package/icons/judge.vue +14 -14
  379. package/icons/kaspi-qr.vue +13 -13
  380. package/icons/key-round.vue +3 -3
  381. package/icons/landmark.vue +3 -3
  382. package/icons/layers.vue +3 -3
  383. package/icons/letter/letter-a.vue +8 -8
  384. package/icons/letter/letter-b.vue +6 -6
  385. package/icons/letter/letter-c.vue +6 -6
  386. package/icons/letter/letter-d.vue +6 -6
  387. package/icons/letter/letter-e.vue +6 -6
  388. package/icons/link-chain.vue +28 -28
  389. package/icons/link-url.vue +3 -3
  390. package/icons/link.vue +7 -7
  391. package/icons/linkedin.vue +24 -24
  392. package/icons/list-tree.vue +3 -3
  393. package/icons/loader-circle.vue +27 -27
  394. package/icons/location.vue +8 -8
  395. package/icons/lock.vue +5 -5
  396. package/icons/log-out.vue +21 -0
  397. package/icons/logo.vue +15 -15
  398. package/icons/logout.vue +16 -16
  399. package/icons/magnify/magnifying-glass-minus.vue +8 -8
  400. package/icons/magnify/magnifying-glass-plus.vue +9 -9
  401. package/icons/magnify/magnifying-glass.vue +9 -9
  402. package/icons/mail-question-mark.vue +8 -8
  403. package/icons/mail.vue +7 -7
  404. package/icons/mailru.vue +34 -34
  405. package/icons/main-filter.vue +10 -10
  406. package/icons/map/map-pin-filled.vue +7 -7
  407. package/icons/map/map-pin-rect.vue +15 -15
  408. package/icons/map/map-pin.vue +7 -7
  409. package/icons/map-marker-start.vue +7 -7
  410. package/icons/map-paper.vue +5 -5
  411. package/icons/medal.vue +7 -7
  412. package/icons/menu-filled.vue +20 -20
  413. package/icons/menu.vue +8 -8
  414. package/icons/message/message-square.vue +3 -3
  415. package/icons/message/message.vue +12 -12
  416. package/icons/message-dotted.vue +30 -30
  417. package/icons/messages-square.vue +3 -3
  418. package/icons/minus/minus-circle.vue +10 -10
  419. package/icons/money.vue +5 -5
  420. package/icons/monitoring.vue +10 -10
  421. package/icons/moon.vue +8 -8
  422. package/icons/more.vue +29 -29
  423. package/icons/network.vue +3 -3
  424. package/icons/newspaper.vue +3 -3
  425. package/icons/note-pencil.vue +14 -14
  426. package/icons/note.vue +14 -14
  427. package/icons/notification-error.vue +17 -17
  428. package/icons/notification.vue +16 -16
  429. package/icons/paperclip.vue +7 -7
  430. package/icons/payment/payment-card.vue +6 -6
  431. package/icons/payment/payment-kaspi.vue +11 -11
  432. package/icons/person-vector.vue +7 -7
  433. package/icons/person-with-briefcase.vue +10 -10
  434. package/icons/person.vue +22 -22
  435. package/icons/phone-call.vue +6 -6
  436. package/icons/phone-filled.vue +5 -5
  437. package/icons/phone-reversed.vue +3 -3
  438. package/icons/phone.vue +7 -7
  439. package/icons/plus/plus-circle.vue +8 -8
  440. package/icons/plus/plus.vue +13 -13
  441. package/icons/profile.vue +5 -5
  442. package/icons/radio/radio-check.vue +8 -8
  443. package/icons/radio/radio-empty.vue +10 -10
  444. package/icons/receipt.vue +8 -8
  445. package/icons/refresh-cw.vue +8 -0
  446. package/icons/robot-outline.vue +3 -3
  447. package/icons/robot.vue +14 -14
  448. package/icons/route.vue +7 -7
  449. package/icons/sanctions.vue +8 -8
  450. package/icons/scales/scale-outline.vue +3 -3
  451. package/icons/scales/scale.vue +16 -16
  452. package/icons/scales/scales.vue +5 -5
  453. package/icons/scales/standing-scales.vue +15 -15
  454. package/icons/scan-search.vue +3 -3
  455. package/icons/search-outline.vue +3 -3
  456. package/icons/search.vue +27 -27
  457. package/icons/send.vue +3 -3
  458. package/icons/settings.vue +26 -26
  459. package/icons/share/share-alt.vue +5 -5
  460. package/icons/share/share.vue +14 -14
  461. package/icons/shield-alert.vue +31 -31
  462. package/icons/shield-check.vue +20 -0
  463. package/icons/shield-verified.vue +14 -14
  464. package/icons/ship.vue +8 -8
  465. package/icons/socials/face-book.vue +15 -15
  466. package/icons/socials/facebook-color.vue +16 -16
  467. package/icons/socials/globe-color.vue +16 -16
  468. package/icons/socials/instagram-color.vue +46 -46
  469. package/icons/socials/instagram.vue +19 -19
  470. package/icons/socials/linkedin-color.vue +14 -14
  471. package/icons/socials/telegram-color.vue +16 -16
  472. package/icons/socials/telegram.vue +15 -15
  473. package/icons/socials/tik-tok.vue +15 -15
  474. package/icons/socials/tiktok-color.vue +14 -14
  475. package/icons/socials/vk-color.vue +16 -16
  476. package/icons/socials/youtube-color.vue +16 -16
  477. package/icons/socials/youtube.vue +16 -16
  478. package/icons/sort/sort-asc.vue +15 -15
  479. package/icons/sort/sort-desc.vue +15 -15
  480. package/icons/sparkles.vue +3 -3
  481. package/icons/splitting-arrows.vue +8 -8
  482. package/icons/star/star-filled.vue +41 -41
  483. package/icons/star/star-half-filled.vue +21 -21
  484. package/icons/star/star.vue +25 -25
  485. package/icons/sun.vue +25 -25
  486. package/icons/sviazi.vue +5 -5
  487. package/icons/tag.vue +30 -30
  488. package/icons/tasks.vue +10 -10
  489. package/icons/tender-search.vue +11 -11
  490. package/icons/terminal.vue +3 -3
  491. package/icons/toasts/check-circle-toast.vue +6 -6
  492. package/icons/toasts/warning-triangle-toast.vue +8 -8
  493. package/icons/trash.vue +13 -13
  494. package/icons/triangle-alert.vue +3 -3
  495. package/icons/triangle.vue +10 -10
  496. package/icons/truck.vue +7 -7
  497. package/icons/user-plus.vue +3 -3
  498. package/icons/user-square.vue +14 -14
  499. package/icons/user.vue +22 -22
  500. package/icons/users-focus.vue +8 -8
  501. package/icons/users-outline.vue +3 -3
  502. package/icons/users-round.vue +3 -3
  503. package/icons/users-three.vue +7 -7
  504. package/icons/users.vue +8 -8
  505. package/icons/view-eye-open.vue +13 -13
  506. package/icons/warning/warning-circle.vue +29 -29
  507. package/icons/warning/warning-triangle-filled.vue +12 -12
  508. package/icons/warning/warning-triangle.vue +28 -28
  509. package/icons/webhook.vue +3 -3
  510. package/icons/whatsapp.vue +8 -8
  511. package/icons/work-bag.vue +11 -11
  512. package/icons/work-case.vue +9 -9
  513. package/icons/work-search.vue +10 -10
  514. package/icons/work.vue +5 -5
  515. package/icons/workflow.vue +3 -3
  516. package/icons/x-mark.vue +15 -15
  517. package/icons/x-outline.vue +3 -3
  518. package/icons/yandex.vue +29 -29
  519. package/icons/zap.vue +3 -3
  520. package/illustrations/address-location.vue +38 -38
  521. package/illustrations/award-badge.vue +105 -105
  522. package/illustrations/ball-with-chain.vue +120 -120
  523. package/illustrations/bill.vue +133 -133
  524. package/illustrations/buildings.vue +82 -82
  525. package/illustrations/calendar.vue +156 -156
  526. package/illustrations/chains.vue +152 -152
  527. package/illustrations/coin-percent.vue +126 -126
  528. package/illustrations/coins-stack.vue +202 -202
  529. package/illustrations/delete-dark.vue +32 -32
  530. package/illustrations/delete.vue +33 -33
  531. package/illustrations/doc-with-stamp.vue +126 -126
  532. package/illustrations/document.vue +64 -64
  533. package/illustrations/door.vue +85 -85
  534. package/illustrations/empty-badge-check.vue +20 -20
  535. package/illustrations/empty-ban.vue +18 -18
  536. package/illustrations/empty-box.vue +77 -77
  537. package/illustrations/empty-filter.vue +20 -20
  538. package/illustrations/empty-inbox.vue +19 -19
  539. package/illustrations/empty-scale.vue +22 -22
  540. package/illustrations/empty-search-x.vue +21 -21
  541. package/illustrations/empty-shopping-cart.vue +21 -21
  542. package/illustrations/empty-users-round.vue +20 -20
  543. package/illustrations/empty-wallet.vue +127 -127
  544. package/illustrations/graph-in-coin.vue +119 -119
  545. package/illustrations/hammer.vue +156 -156
  546. package/illustrations/hand-cash.vue +108 -108
  547. package/illustrations/info.vue +52 -52
  548. package/illustrations/mail-active.vue +86 -86
  549. package/illustrations/mail.vue +68 -68
  550. package/illustrations/no-access.vue +92 -92
  551. package/illustrations/notes-search.vue +20 -20
  552. package/illustrations/notes-sticky.vue +20 -20
  553. package/illustrations/ok.vue +90 -90
  554. package/illustrations/people-group.vue +237 -237
  555. package/illustrations/person-with-phone.vue +187 -187
  556. package/illustrations/person.vue +159 -159
  557. package/illustrations/phone-check.vue +110 -110
  558. package/illustrations/phone-payment-method.vue +223 -223
  559. package/illustrations/scheme-notes.vue +21 -0
  560. package/illustrations/scheme-waypoints.vue +25 -0
  561. package/illustrations/search.vue +14 -14
  562. package/illustrations/shield-user.vue +94 -94
  563. package/illustrations/stop-hand.vue +84 -84
  564. package/illustrations/stop-sign.vue +34 -34
  565. package/illustrations/suit.vue +97 -97
  566. package/illustrations/suitcase.vue +105 -105
  567. package/illustrations/terminal.vue +100 -100
  568. package/illustrations/trash-can.vue +66 -66
  569. package/illustrations/turn-on-tariff.vue +38 -38
  570. package/illustrations/two-persons.vue +169 -169
  571. package/lang/en.ts +878 -878
  572. package/lang/kk.ts +880 -880
  573. package/lang/ru.ts +880 -880
  574. package/layouts/default.vue +13 -13
  575. package/nuxt.config.ts +95 -95
  576. package/package.json +64 -64
  577. package/plugins/maska.ts +4 -4
  578. package/plugins/toast.client.ts +41 -41
  579. package/public/kaspi/logo.svg +4 -4
  580. package/shared/constans/pages.ts +96 -96
  581. package/tsconfig.json +6 -6
  582. package/utils/getMaxZindex.ts +25 -25
  583. package/utils/localizedNavigation.ts +36 -36
  584. package/utils/toCssSize.ts +11 -0
  585. package/utils/twMerge.ts +34 -34
  586. package/vitest.config.ts +14 -14
  587. package/.claude/settings.local.json +0 -19
@@ -0,0 +1,710 @@
1
+ <script generic="T" lang="ts" setup>
2
+ import type { CascadeEntry, CascadeKeys, CascadeMode } from './types'
3
+ import { toCssSize } from '#adata-ui/utils/toCssSize'
4
+ import { autoUpdate, flip, size as floatingSize, offset, shift, useFloating } from '@floating-ui/vue'
5
+ import { onClickOutside, useMediaQuery, useToggle, useWindowSize } from '@vueuse/core'
6
+ import { isEqual } from 'lodash-es'
7
+ import { buildCascadeIndex, isDisabled, isLeaf, nodeKey, nodeLabel, pathLabel, resolvePanels } from './cascade-utils'
8
+
9
+ interface Props {
10
+ label: string
11
+ options: T[]
12
+ required?: boolean
13
+ keyToShow?: string
14
+ keyToSelect?: string
15
+ childrenKey?: string
16
+ disabledKey?: string
17
+ /** `true` — в v-model лежит ключ листа, `false` — сам объект листа. */
18
+ getOnlyKey?: boolean
19
+ clearable?: boolean
20
+ disabled?: boolean
21
+ size?: 'sm' | 'md'
22
+ error?: string | string[]
23
+ startIcon?: object
24
+ buttonBg?: string
25
+ /** Куда растут подпанели. `auto` — вправо, а если не влезает, влево. */
26
+ panelsSide?: 'auto' | 'right' | 'left'
27
+ /** Принудительный режим раскрытия вместо авто-выбора по ширине экрана. */
28
+ submenuTrigger?: 'auto' | 'hover' | 'click'
29
+ hoverOpenDelay?: number
30
+ hoverCloseDelay?: number
31
+ /** Ширина колонки. По умолчанию — ширина триггера. */
32
+ panelWidth?: number | string
33
+ /** Нижняя граница ширины колонки — когда триггер узкий, а подписи длинные. */
34
+ panelMinWidth?: number | string
35
+ panelMaxHeight?: number | string
36
+ /** Подпись первой колонки. По умолчанию — `label`. */
37
+ panelTitle?: string
38
+ /** Показывать в триггере весь путь («Кварталы / 1 квартал 2026»). */
39
+ showPathInTrigger?: boolean
40
+ drawerTitle?: string
41
+ /** Фиксированная общая высота мобильного дровера («70dvh»). */
42
+ drawerHeight?: number | string
43
+ drawerMaxHeight?: number | string
44
+ backLabel?: string
45
+ emptyLabel?: string
46
+ mobileBreakpoint?: number
47
+ tabletBreakpoint?: number
48
+ teleport?: boolean
49
+ /** Слой выпадашки и мобильного дровера. */
50
+ zIndex?: number | string
51
+ }
52
+
53
+ interface Emits {
54
+ (e: 'onChange', option: T, path: T[]): void
55
+ (e: 'onClear'): void
56
+ }
57
+
58
+ /**
59
+ * Каскадный селект: опция-родитель раскрывает своих детей отдельной колонкой сбоку.
60
+ *
61
+ * Три режима, выбираются по ширине экрана (или принудительно через `submenuTrigger`):
62
+ * • `columns-hover` (десктоп) — подпанель по наведению;
63
+ * • `columns-click` (планшет) — подпанель по клику, у дочерней колонки есть «Назад»;
64
+ * • `drawer` (мобильный) — `a-drawer-v2` с drill-down по одной колонке.
65
+ *
66
+ * Выбирается ТОЛЬКО лист: узел с детьми лишь раскрывает ветку и значение не меняет.
67
+ */
68
+ defineOptions({ name: 'ACascadeSelectV2' })
69
+
70
+ const props = withDefaults(defineProps<Props>(), {
71
+ required: false,
72
+ keyToShow: 'name',
73
+ keyToSelect: 'id',
74
+ childrenKey: 'children',
75
+ disabledKey: undefined,
76
+ getOnlyKey: false,
77
+ clearable: true,
78
+ disabled: false,
79
+ size: 'sm',
80
+ error: undefined,
81
+ startIcon: undefined,
82
+ buttonBg: '',
83
+ panelsSide: 'auto',
84
+ submenuTrigger: 'auto',
85
+ hoverOpenDelay: 120,
86
+ hoverCloseDelay: 200,
87
+ panelWidth: undefined,
88
+ panelMinWidth: undefined,
89
+ panelMaxHeight: undefined,
90
+ panelTitle: undefined,
91
+ showPathInTrigger: false,
92
+ drawerTitle: undefined,
93
+ drawerHeight: undefined,
94
+ drawerMaxHeight: undefined,
95
+ backLabel: 'Назад',
96
+ emptyLabel: 'Ничего не найдено',
97
+ mobileBreakpoint: 768,
98
+ tabletBreakpoint: 1024,
99
+ teleport: true,
100
+ zIndex: 10000,
101
+ })
102
+
103
+ const emits = defineEmits<Emits>()
104
+
105
+ defineSlots<{
106
+ 'option'?: (props: { option: T, level: number, selected: boolean, hasChildren: boolean, active: boolean }) => unknown
107
+ 'single-selected'?: (props: { value: T, path: T[] }) => unknown
108
+ 'empty'?: () => unknown
109
+ }>()
110
+
111
+ const modelValue = defineModel<T | unknown | null>({ required: true })
112
+ const isOpen = defineModel<boolean>('open', { default: false })
113
+
114
+ const keys = computed<CascadeKeys>(() => ({
115
+ keyToShow: props.keyToShow,
116
+ keyToSelect: props.keyToSelect,
117
+ childrenKey: props.childrenKey,
118
+ disabledKey: props.disabledKey,
119
+ }))
120
+
121
+ /* Один обход дерева на смену options: дальше все выборки по ключу — O(1). */
122
+ const index = computed(() => buildCascadeIndex(props.options, keys.value))
123
+
124
+ const selectedKey = computed<unknown>(() => {
125
+ const value = modelValue.value
126
+ if (value == null || value === '') return undefined
127
+ return props.getOnlyKey ? value : nodeKey(value as T, keys.value)
128
+ })
129
+
130
+ const selectedEntry = computed<CascadeEntry<T> | null>(() =>
131
+ selectedKey.value === undefined ? null : index.value.get(selectedKey.value) ?? null,
132
+ )
133
+
134
+ /** Ключи предков выбранного листа — ими раскрываем ветку при открытии. */
135
+ const selectedAncestorKeys = computed<unknown[]>(() =>
136
+ selectedEntry.value ? selectedEntry.value.keyPath.slice(0, -1) : [],
137
+ )
138
+
139
+ // -- Навигация -------------------------------------------------------------
140
+
141
+ /** Цепочка ключей раскрытых родителей: activePath[i] раскрыт из колонки i. */
142
+ const activePath = ref<unknown[]>([])
143
+
144
+ const resolved = computed(() => resolvePanels(props.options, activePath.value, index.value, keys.value))
145
+ const panels = computed<T[][]>(() => resolved.value.panels)
146
+ /** Путь, обрезанный до реально разрешимого: options могли смениться под нами. */
147
+ const resolvedPath = computed<unknown[]>(() => resolved.value.keyPath)
148
+
149
+ // -- Режим -----------------------------------------------------------------
150
+
151
+ const isMobileViewport = useMediaQuery(computed(() => `(max-width: ${props.mobileBreakpoint - 1}px)`))
152
+ const isTabletViewport = useMediaQuery(
153
+ computed(() => `(min-width: ${props.mobileBreakpoint}px) and (max-width: ${props.tabletBreakpoint - 1}px)`),
154
+ )
155
+
156
+ const mode = computed<CascadeMode>(() => {
157
+ if (isMobileViewport.value) return 'drawer'
158
+ if (props.submenuTrigger === 'hover') return 'columns-hover'
159
+ if (props.submenuTrigger === 'click') return 'columns-click'
160
+ return isTabletViewport.value ? 'columns-click' : 'columns-hover'
161
+ })
162
+
163
+ const isDrawerMode = computed(() => mode.value === 'drawer')
164
+
165
+ /**
166
+ * Планшет и дровер показывают одну колонку — текущую: вернуться к родителю есть чем
167
+ * («Назад»), а вторая колонка только съедала бы ширину. Десктоп показывает все.
168
+ * Абсолютный `level` сохраняем, чтобы срез не ломал индексацию путей и рефов.
169
+ */
170
+ const panelViews = computed<{ options: T[], level: number }[]>(() => {
171
+ const all = panels.value.map((options, level) => ({ options, level }))
172
+ if (isDrawerMode.value || mode.value === 'columns-click') return all.slice(-1)
173
+ return all
174
+ })
175
+
176
+ /* undefined доезжает до a-cascade-drawer и там подхватывает его собственные дефолты. */
177
+ const drawerHeightStyle = computed(() => toCssSize(props.drawerHeight))
178
+ const drawerMaxHeightStyle = computed(() => toCssSize(props.drawerMaxHeight))
179
+
180
+ const drawerView = computed(() => panelViews.value[0] ?? { options: props.options, level: 0 })
181
+ const drawerParentLabel = computed(() => {
182
+ const level = drawerView.value.level
183
+ if (level === 0) return ''
184
+ const entry = index.value.get(resolvedPath.value[level - 1])
185
+ return entry ? nodeLabel(entry.node, keys.value) : ''
186
+ })
187
+
188
+ // -- Позиционирование ------------------------------------------------------
189
+
190
+ const wrapper = ref<HTMLElement | null>(null)
191
+ const triggerRef = ref<{ el: HTMLButtonElement | null, valueEl: HTMLElement | null } | null>(null)
192
+ const reference = computed<HTMLElement | null>(() => triggerRef.value?.el ?? null)
193
+ const floating = ref<HTMLElement | null>(null)
194
+
195
+ const panelsDir = ref<'right' | 'left'>('right')
196
+
197
+ const autoMaxHeight = ref('320px')
198
+ /* Ширина триггера, снятая floating-ui: по ней колонки выравниваются с полем, как в ASelectV2. */
199
+ const autoPanelWidth = ref<string>()
200
+
201
+ const panelMaxHeightStyle = computed(() => toCssSize(props.panelMaxHeight) ?? autoMaxHeight.value)
202
+ const panelWidthStyle = computed(() => toCssSize(props.panelWidth) ?? autoPanelWidth.value)
203
+ const panelMinWidthStyle = computed(() => toCssSize(props.panelMinWidth))
204
+
205
+ function pxOf(value?: string): number {
206
+ const match = /^([\d.]+)px$/.exec(value ?? '')
207
+ return match ? Number.parseFloat(match[1]) : 0
208
+ }
209
+
210
+ /** Фактическая ширина колонки в пикселях; 0 — размер задан нечисловой единицей, мерить нечем. */
211
+ const panelWidthPx = computed(() => {
212
+ const width = pxOf(panelWidthStyle.value)
213
+ if (!width) return 0
214
+ /* min-width может оказаться больше ширины триггера — тогда колонка шире, чем кажется. */
215
+ return Math.max(width, pxOf(panelMinWidthStyle.value))
216
+ })
217
+
218
+ const { width: viewportWidth } = useWindowSize()
219
+
220
+ /**
221
+ * Колонки фиксированной ширины перестают влезать в экран уже на третьем уровне,
222
+ * а `shift` умеет только сдвигать. Поэтому режем лишние слева — тем же приёмом,
223
+ * что планшет (`slice(-2)`) и дровер (`slice(-1)`).
224
+ */
225
+ const visiblePanelViews = computed(() => {
226
+ const all = panelViews.value
227
+ const panelPx = panelWidthPx.value
228
+ if (isDrawerMode.value || !panelPx || !viewportWidth.value) return all
229
+
230
+ const gap = 8 /* gap-2 между колонками */
231
+ const edges = 16 /* по 8px от краёв — столько же просит shift({ padding: 8 }) */
232
+ const fits = Math.max(1, Math.floor((viewportWidth.value - edges + gap) / (panelPx + gap)))
233
+ return all.length > fits ? all.slice(-fits) : all
234
+ })
235
+
236
+ /**
237
+ * «Назад» нужен на самой мелкой видимой колонке: если срез (планшетный или по ширине экрана)
238
+ * спрятал родителя, вернуться к нему больше нечем. На остальных колонках он был бы дублем.
239
+ */
240
+ function showBackFor(level: number) {
241
+ if (level === 0) return false
242
+ return level === (visiblePanelViews.value[0]?.level ?? 0)
243
+ }
244
+
245
+ const { x, y, strategy, update } = useFloating(reference, floating, {
246
+ /* Колонки растут вправо от левого края триггера; в зеркальном режиме — влево от правого. */
247
+ placement: computed(() => (panelsDir.value === 'left' ? 'bottom-end' : 'bottom-start')),
248
+ strategy: 'fixed',
249
+ middleware: computed(() => [
250
+ offset(6),
251
+ flip({ fallbackPlacements: panelsDir.value === 'left' ? ['top-end'] : ['top-start'] }),
252
+ shift({ padding: 8 }),
253
+ floatingSize({
254
+ /* Пишем в ref только на смену значения: apply зовётся на каждый кадр autoUpdate. */
255
+ apply({ availableHeight, rects }) {
256
+ const nextHeight = `${Math.max(160, Math.min(availableHeight, 360))}px`
257
+ if (nextHeight !== autoMaxHeight.value) autoMaxHeight.value = nextHeight
258
+
259
+ const nextWidth = `${Math.round(rects.reference.width)}px`
260
+ if (nextWidth !== autoPanelWidth.value) autoPanelWidth.value = nextWidth
261
+ },
262
+ }),
263
+ ]),
264
+ })
265
+
266
+ /* Только на смену: запись меняет placement, тот дёргает update — иначе петля. */
267
+ function setDirection(next: 'right' | 'left') {
268
+ if (next !== panelsDir.value) panelsDir.value = next
269
+ }
270
+
271
+ /**
272
+ * Сторону выбираем один раз на изменение набора колонок, а не внутри middleware:
273
+ * измерение в `apply` меняло бы placement, тот — измерение, и получалась бы петля.
274
+ */
275
+ function measureDirection() {
276
+ if (props.panelsSide !== 'auto') {
277
+ setDirection(props.panelsSide)
278
+ return
279
+ }
280
+ const rect = reference.value?.getBoundingClientRect()
281
+ const el = floating.value
282
+ if (!rect || !el) return
283
+ const needed = el.scrollWidth
284
+ const spaceRight = window.innerWidth - rect.left - 8
285
+ const spaceLeft = rect.right - 8
286
+ setDirection(needed > spaceRight && spaceLeft > spaceRight ? 'left' : 'right')
287
+ }
288
+
289
+ watch(() => props.panelsSide, () => measureDirection(), { immediate: true })
290
+ watch([visiblePanelViews, isOpen], () => {
291
+ if (!isOpen.value || isDrawerMode.value) return
292
+ nextTick(measureDirection)
293
+ })
294
+
295
+ let cleanup: (() => void) | null = null
296
+ watch([isOpen, isDrawerMode], () => {
297
+ cleanup?.()
298
+ cleanup = null
299
+ if (!isOpen.value || isDrawerMode.value) return
300
+ nextTick(() => {
301
+ if (reference.value && floating.value) {
302
+ cleanup = autoUpdate(reference.value, floating.value, () => {
303
+ update()
304
+ measureDirection()
305
+ })
306
+ }
307
+ })
308
+ })
309
+
310
+ // -- Таймеры hover-intent (по образцу useTooltipTrigger) --------------------
311
+
312
+ let openTimer: ReturnType<typeof setTimeout> | null = null
313
+ let collapseTimer: ReturnType<typeof setTimeout> | null = null
314
+
315
+ function clearOpenTimer() {
316
+ if (openTimer) {
317
+ clearTimeout(openTimer)
318
+ openTimer = null
319
+ }
320
+ }
321
+
322
+ function clearCollapseTimer() {
323
+ if (collapseTimer) {
324
+ clearTimeout(collapseTimer)
325
+ collapseTimer = null
326
+ }
327
+ }
328
+
329
+ onUnmounted(() => {
330
+ clearOpenTimer()
331
+ clearCollapseTimer()
332
+ cleanup?.()
333
+ })
334
+
335
+ // -- Действия --------------------------------------------------------------
336
+
337
+ const toggleOpen = useToggle(isOpen)
338
+
339
+ function openBranch(level: number, node: T) {
340
+ activePath.value = [...resolvedPath.value.slice(0, level), nodeKey(node, keys.value)]
341
+ }
342
+
343
+ function collapseTo(level: number) {
344
+ activePath.value = resolvedPath.value.slice(0, level)
345
+ }
346
+
347
+ function selectLeaf(node: T) {
348
+ const entry = index.value.get(nodeKey(node, keys.value))
349
+ modelValue.value = props.getOnlyKey ? nodeKey(node, keys.value) : node
350
+ emits('onChange', node, entry?.path ?? [node])
351
+ toggleOpen(false)
352
+ }
353
+
354
+ function activate(level: number, node: T) {
355
+ if (isDisabled(node, keys.value)) return
356
+ clearOpenTimer()
357
+ clearCollapseTimer()
358
+ if (isLeaf(node, keys.value)) {
359
+ selectLeaf(node)
360
+ return
361
+ }
362
+ openBranch(level, node)
363
+ focusRow(level + 1, firstEnabledKey(level + 1))
364
+ }
365
+
366
+ function goBackFrom(level: number) {
367
+ activePath.value = resolvedPath.value.slice(0, Math.max(0, level - 1))
368
+ }
369
+
370
+ function goBack() {
371
+ goBackFrom(drawerView.value.level)
372
+ }
373
+
374
+ function onClear() {
375
+ modelValue.value = null
376
+ activePath.value = []
377
+ emits('onClear')
378
+ }
379
+
380
+ defineExpose({ onClear })
381
+
382
+ // -- Hover -----------------------------------------------------------------
383
+
384
+ function onRowHover(level: number, node: T) {
385
+ if (mode.value !== 'columns-hover') return
386
+ clearCollapseTimer()
387
+ clearOpenTimer()
388
+ openTimer = setTimeout(() => {
389
+ openTimer = null
390
+ if (isDisabled(node, keys.value)) return
391
+ if (isLeaf(node, keys.value)) collapseTo(level)
392
+ else openBranch(level, node)
393
+ }, props.hoverOpenDelay)
394
+ }
395
+
396
+ function onPanelsEnter() {
397
+ clearCollapseTimer()
398
+ }
399
+
400
+ /* Уводя мышь со всего каскада, схлопываем его до ветки выбранного значения. */
401
+ function onPanelsLeave() {
402
+ clearOpenTimer()
403
+ if (mode.value !== 'columns-hover') return
404
+ clearCollapseTimer()
405
+ collapseTimer = setTimeout(() => {
406
+ collapseTimer = null
407
+ activePath.value = selectedAncestorKeys.value
408
+ }, props.hoverCloseDelay)
409
+ }
410
+
411
+ // -- Клавиатура ------------------------------------------------------------
412
+
413
+ const focused = ref<{ level: number, key: unknown } | null>(null)
414
+ const panelRefs = ref<Record<number, { focusItem: (key: unknown) => void } | null>>({})
415
+
416
+ function setPanelRef(el: unknown, level: number) {
417
+ if (el == null) delete panelRefs.value[level]
418
+ else panelRefs.value[level] = el as { focusItem: (key: unknown) => void }
419
+ }
420
+
421
+ function enabledRows(level: number): T[] {
422
+ return (panels.value[level] ?? []).filter(node => !isDisabled(node, keys.value))
423
+ }
424
+
425
+ function firstEnabledKey(level: number): unknown {
426
+ const rows = enabledRows(level)
427
+ return rows.length ? nodeKey(rows[0], keys.value) : undefined
428
+ }
429
+
430
+ function focusRow(level: number, key: unknown) {
431
+ if (key === undefined || isDrawerMode.value) return
432
+ focused.value = { level, key }
433
+ nextTick(() => panelRefs.value[level]?.focusItem(key))
434
+ }
435
+
436
+ function nodeByKey(level: number, key: unknown): T | undefined {
437
+ return (panels.value[level] ?? []).find(node => isEqual(nodeKey(node, keys.value), key))
438
+ }
439
+
440
+ function moveWithin(level: number, delta: number) {
441
+ const rows = enabledRows(level)
442
+ if (!rows.length) return
443
+ const current = rows.findIndex(node => isEqual(nodeKey(node, keys.value), focused.value?.key))
444
+ const next = current === -1
445
+ ? (delta > 0 ? 0 : rows.length - 1)
446
+ : (current + delta + rows.length) % rows.length
447
+ focusRow(level, nodeKey(rows[next], keys.value))
448
+ }
449
+
450
+ function focusInitial() {
451
+ if (isDrawerMode.value) return
452
+ const entry = selectedEntry.value
453
+ if (entry) {
454
+ focusRow(entry.level, selectedKey.value)
455
+ return
456
+ }
457
+ focusRow(0, firstEnabledKey(0))
458
+ }
459
+
460
+ function closeAndRestoreFocus() {
461
+ toggleOpen(false)
462
+ nextTick(() => reference.value?.focus())
463
+ }
464
+
465
+ function onKeydown(event: KeyboardEvent) {
466
+ if (props.disabled) return
467
+
468
+ if (!isOpen.value) {
469
+ if (event.key === 'ArrowDown') {
470
+ event.preventDefault()
471
+ toggleOpen(true)
472
+ }
473
+ return
474
+ }
475
+
476
+ if (event.key === 'Escape') {
477
+ event.preventDefault()
478
+ closeAndRestoreFocus()
479
+ return
480
+ }
481
+
482
+ if (event.key === 'Tab') {
483
+ toggleOpen(false)
484
+ return
485
+ }
486
+
487
+ if (isDrawerMode.value) return
488
+
489
+ const level = focused.value?.level ?? 0
490
+
491
+ switch (event.key) {
492
+ case 'ArrowDown':
493
+ event.preventDefault()
494
+ moveWithin(level, 1)
495
+ break
496
+ case 'ArrowUp':
497
+ event.preventDefault()
498
+ moveWithin(level, -1)
499
+ break
500
+ case 'Home':
501
+ event.preventDefault()
502
+ focusRow(level, firstEnabledKey(level))
503
+ break
504
+ case 'End': {
505
+ event.preventDefault()
506
+ const rows = enabledRows(level)
507
+ if (rows.length) focusRow(level, nodeKey(rows[rows.length - 1], keys.value))
508
+ break
509
+ }
510
+ case 'ArrowRight': {
511
+ const node = focused.value ? nodeByKey(level, focused.value.key) : undefined
512
+ if (!node || isLeaf(node, keys.value)) break
513
+ event.preventDefault()
514
+ openBranch(level, node)
515
+ focusRow(level + 1, firstEnabledKey(level + 1))
516
+ break
517
+ }
518
+ case 'ArrowLeft': {
519
+ if (level === 0) break
520
+ event.preventDefault()
521
+ const parentKey = resolvedPath.value[level - 1]
522
+ goBackFrom(level)
523
+ focusRow(level - 1, parentKey)
524
+ break
525
+ }
526
+ }
527
+ }
528
+
529
+ // -- Открытие / закрытие ---------------------------------------------------
530
+
531
+ watch(isOpen, (open) => {
532
+ clearOpenTimer()
533
+ clearCollapseTimer()
534
+ if (open) {
535
+ activePath.value = selectedAncestorKeys.value
536
+ /* Ширину берём с триггера сразу: иначе колонка мигнёт фолбэком min-w-45 и прыгнет. */
537
+ const triggerWidth = reference.value?.offsetWidth
538
+ if (triggerWidth) autoPanelWidth.value = `${Math.round(triggerWidth)}px`
539
+ nextTick(focusInitial)
540
+ }
541
+ else {
542
+ focused.value = null
543
+ }
544
+ })
545
+
546
+ onClickOutside(floating, () => {
547
+ if (isOpen.value && !isDrawerMode.value) toggleOpen(false)
548
+ }, { ignore: [reference] })
549
+
550
+ // -- Отображение в триггере ------------------------------------------------
551
+
552
+ const displayValue = computed(() => {
553
+ const entry = selectedEntry.value
554
+ if (!entry) return ''
555
+ return props.showPathInTrigger
556
+ ? pathLabel(entry.path, keys.value)
557
+ : nodeLabel(entry.node, keys.value)
558
+ })
559
+
560
+ const rootPanelTitle = computed(() => props.panelTitle ?? props.label)
561
+ </script>
562
+
563
+ <template>
564
+ <div
565
+ ref="wrapper"
566
+ class="cascade-select relative w-full text-body-md"
567
+ @keydown="onKeydown"
568
+ >
569
+ <a-select-trigger
570
+ ref="triggerRef"
571
+ :label="label"
572
+ :required="required"
573
+ :size="size"
574
+ :disabled="disabled"
575
+ :error="error"
576
+ :is-open="isOpen"
577
+ :start-icon="startIcon"
578
+ :button-bg="buttonBg"
579
+ :clearable="clearable"
580
+ :show-clear="clearable && !!selectedEntry"
581
+ floating-label
582
+ :has-value="!!selectedEntry"
583
+ @toggle="toggleOpen()"
584
+ @clear="onClear"
585
+ >
586
+ <span v-if="selectedEntry" class="block truncate leading-tight">
587
+ <slot
588
+ name="single-selected"
589
+ :value="selectedEntry.node"
590
+ :path="selectedEntry.path"
591
+ >{{ displayValue }}</slot>
592
+ </span>
593
+ <span v-else class="text-secondary">&#8203;</span>
594
+ </a-select-trigger>
595
+
596
+ <client-only>
597
+ <teleport
598
+ to="body"
599
+ :disabled="!teleport"
600
+ >
601
+ <transition
602
+ enter-active-class="cascade-select__enter-active"
603
+ enter-from-class="cascade-select__enter-from"
604
+ enter-to-class="cascade-select__enter-to"
605
+ leave-active-class="cascade-select__leave-active"
606
+ leave-from-class="cascade-select__leave-from"
607
+ leave-to-class="cascade-select__leave-to"
608
+ >
609
+ <div
610
+ v-if="isOpen && !isDrawerMode"
611
+ ref="floating"
612
+ class="cascade-select__panels flex items-start gap-2"
613
+ :class="{ 'flex-row-reverse': panelsDir === 'left' }"
614
+ :style="{
615
+ position: strategy,
616
+ left: x != null ? `${x}px` : '',
617
+ top: y != null ? `${y}px` : '',
618
+ zIndex,
619
+ }"
620
+ @pointerenter="onPanelsEnter"
621
+ @pointerleave="onPanelsLeave"
622
+ @keydown="onKeydown"
623
+ >
624
+ <a-cascade-panel
625
+ v-for="view in visiblePanelViews"
626
+ :key="view.level"
627
+ :ref="(el: any) => setPanelRef(el, view.level)"
628
+ :options="view.options"
629
+ :keys="keys"
630
+ :level="view.level"
631
+ :active-key="resolvedPath[view.level]"
632
+ :selected-key="selectedKey"
633
+ :focused-key="focused?.level === view.level ? focused?.key : undefined"
634
+ :title="view.level === 0 ? rootPanelTitle : ''"
635
+ :show-back="showBackFor(view.level)"
636
+ :mirrored="panelsDir === 'left'"
637
+ :back-label="backLabel"
638
+ :width="panelWidthStyle"
639
+ :min-width="panelMinWidthStyle"
640
+ :max-height="panelMaxHeightStyle"
641
+ :empty-label="emptyLabel"
642
+ @activate="activate(view.level, $event)"
643
+ @hover="onRowHover(view.level, $event)"
644
+ @back="goBackFrom(view.level)"
645
+ >
646
+ <template #option="slotProps">
647
+ <slot name="option" v-bind="slotProps" />
648
+ </template>
649
+ <template #empty>
650
+ <slot name="empty" />
651
+ </template>
652
+ </a-cascade-panel>
653
+ </div>
654
+ </transition>
655
+ </teleport>
656
+
657
+ <a-cascade-drawer
658
+ v-if="isDrawerMode"
659
+ v-model="isOpen"
660
+ :options="drawerView.options"
661
+ :keys="keys"
662
+ :level="drawerView.level"
663
+ :title="drawerTitle ?? label"
664
+ :parent-label="drawerParentLabel"
665
+ :back-label="backLabel"
666
+ :selected-key="selectedKey"
667
+ :height="drawerHeightStyle"
668
+ :max-height="drawerMaxHeightStyle"
669
+ :z-index="zIndex"
670
+ @activate="activate(drawerView.level, $event)"
671
+ @back="goBack"
672
+ >
673
+ <template #option="slotProps">
674
+ <slot name="option" v-bind="slotProps" />
675
+ </template>
676
+ <template #empty>
677
+ <slot name="empty" />
678
+ </template>
679
+ </a-cascade-drawer>
680
+ </client-only>
681
+ </div>
682
+ </template>
683
+
684
+ <style scoped>
685
+ .cascade-select__enter-active,
686
+ .cascade-select__leave-active {
687
+ transition:
688
+ opacity 180ms cubic-bezier(0.22, 1, 0.36, 1),
689
+ transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
690
+ }
691
+
692
+ .cascade-select__enter-from,
693
+ .cascade-select__leave-to {
694
+ opacity: 0;
695
+ transform: translateY(-4px);
696
+ }
697
+
698
+ .cascade-select__enter-to,
699
+ .cascade-select__leave-from {
700
+ opacity: 1;
701
+ transform: translateY(0);
702
+ }
703
+
704
+ @media (prefers-reduced-motion: reduce) {
705
+ .cascade-select__enter-active,
706
+ .cascade-select__leave-active {
707
+ transition: none;
708
+ }
709
+ }
710
+ </style>