adata-ui 2.1.41-beta.1 → 2.1.41-beta.3

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 (569) hide show
  1. package/-error.vue +170 -170
  2. package/.claude/settings.local.json +19 -0
  3. package/.editorconfig +12 -12
  4. package/.gitlab-ci.yml +16 -16
  5. package/.nuxtrc +1 -1
  6. package/.nvmrc +1 -1
  7. package/.playground/app.config.ts +5 -5
  8. package/.playground/app.vue +102 -102
  9. package/.playground/nuxt.config.ts +4 -4
  10. package/CLAUDE.md +63 -63
  11. package/README.md +75 -75
  12. package/app.config.ts +28 -28
  13. package/app.vue +8 -8
  14. package/assets/styles/main.css +12 -12
  15. package/assets/styles/theme.css +583 -583
  16. package/assets/styles/ui.css +247 -247
  17. package/components/elements/README.md +1 -1
  18. package/components/elements/a-select-row/ASelectRowV2.vue +210 -210
  19. package/components/elements/a-select-row/index.vue +139 -139
  20. package/components/elements/accordion/AAccordion.vue +199 -199
  21. package/components/elements/accordion/AAccordionGroup/AAccordionGroup.vue +145 -145
  22. package/components/elements/accordion/AAccordionGroup/types.ts +20 -20
  23. package/components/elements/accordion/AAccordionGroup/ui.config.ts +22 -22
  24. package/components/elements/accordion/types.ts +14 -14
  25. package/components/elements/accordion/ui.config.ts +22 -22
  26. package/components/elements/alert/AAlert.vue +52 -52
  27. package/components/elements/banner/ABanner.vue +49 -49
  28. package/components/elements/bg-block/ABgBlock.vue +11 -11
  29. package/components/elements/button/AButton.vue +191 -191
  30. package/components/elements/button/AButtonV2.vue +179 -177
  31. package/components/elements/button/types.ts +24 -24
  32. package/components/elements/button-login/index.vue +19 -19
  33. package/components/elements/calendar/ACalendar.vue +83 -83
  34. package/components/elements/carousel/ACarousel.vue +159 -159
  35. package/components/elements/carousel/config.ui.ts +15 -15
  36. package/components/elements/carousel/types.ts +18 -18
  37. package/components/elements/chip-wrapper/AChipWrapper.vue +81 -81
  38. package/components/elements/chip-wrapper/types.ts +12 -12
  39. package/components/elements/chips/AChips.vue +33 -33
  40. package/components/elements/companies/AOurClients.vue +47 -47
  41. package/components/elements/count-up/ACountUp.vue +54 -54
  42. package/components/elements/curve-block/ACurveBlock.vue +259 -259
  43. package/components/elements/curve-block-v2/ACurveBlockV2.vue +122 -120
  44. package/components/elements/digit-badge/ADigitBadge.vue +86 -86
  45. package/components/elements/empty-box/empty-box-V2.vue +92 -92
  46. package/components/elements/error-template/index.vue +116 -116
  47. package/components/elements/feature-description/AFeatureDescription.vue +112 -112
  48. package/components/elements/illustrations/noAccess.vue +71 -71
  49. package/components/elements/infinite-carousel/AInfiniteCarousel.vue +85 -85
  50. package/components/elements/infinite-scroll/AInfiniteScroll.vue +33 -33
  51. package/components/elements/leave-note/ALeaveNote.vue +102 -102
  52. package/components/elements/pagination/APagination.vue +110 -110
  53. package/components/elements/photos-animation/APhotosAnimation.vue +85 -85
  54. package/components/elements/section-heading/ASectionHeading.vue +35 -35
  55. package/components/elements/segmented/ASegmentedV2.vue +154 -154
  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 +580 -580
  59. package/components/elements/select/ui/select-button.vue +112 -112
  60. package/components/elements/select/ui/select-list.vue +99 -99
  61. package/components/elements/show-more/AShowMore.vue +35 -35
  62. package/components/elements/show-more/AShowMoreV2.vue +26 -26
  63. package/components/elements/skeleton/ASkeleton.vue +44 -44
  64. package/components/elements/slider/ASlider.vue +175 -175
  65. package/components/elements/slider/ASliderItem.vue +31 -31
  66. package/components/elements/slider/ASliderV2.vue +182 -182
  67. package/components/elements/slider/types.ts +21 -21
  68. package/components/elements/star-rating/AStarRating.vue +189 -189
  69. package/components/elements/star-rating/types.ts +7 -7
  70. package/components/elements/star-rating/ui/Star.vue +74 -74
  71. package/components/elements/status-badge/AStatusBadge.vue +125 -125
  72. package/components/elements/step-slider/AStepSliderColCard.vue +270 -270
  73. package/components/elements/step-slider/AStepSliderColNumber.vue +199 -199
  74. package/components/elements/table/ATable.vue +93 -93
  75. package/components/elements/table/table.config.ts +32 -32
  76. package/components/elements/tree-select/ATreeSelect.vue +157 -157
  77. package/components/elements/tree-select/TreeService.ts +184 -184
  78. package/components/elements/tree-select/components/ATreeSelectNode.vue +104 -104
  79. package/components/elements/tree-select/types.ts +63 -63
  80. package/components/elements/tree-select/utils.ts +30 -30
  81. package/components/elements/tree-select-mobile/ATreeSelectMobile.vue +79 -79
  82. package/components/elements/tree-select-mobile/components/ATreeSelectNodesMobile.vue +175 -175
  83. package/components/elements/tree-select-modal/ATreeSelectModal.vue +217 -217
  84. package/components/features/color-mode/AColorMode.client.vue +74 -74
  85. package/components/features/dropdown/ADropdown.vue +127 -127
  86. package/components/features/dropdown/ADropdownV2.vue +140 -140
  87. package/components/features/go-top/GoTop.vue +63 -63
  88. package/components/features/lang-switcher/lang-switcher.vue +158 -158
  89. package/components/features/payment/banner/BasicPlusLimitBanner.vue +84 -84
  90. package/components/features/payment/banner/PaymentBanner.vue +88 -88
  91. package/components/features/payment/banner/UpSellBanner.vue +105 -105
  92. package/components/features/payment/process/PaymentKaspiQrSidePanel.vue +180 -180
  93. package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +123 -123
  94. package/components/features/payment/process/PaymentMethodSidePanel.vue +138 -138
  95. package/components/features/payment/process/PaymentProcess.vue +158 -158
  96. package/components/features/payment/process/PaymentTopUpSidePanel.vue +119 -119
  97. package/components/features/pk-mobile-services/APkMobileServices.vue +107 -107
  98. package/components/features//321/201hange-version/AChangeVersion.vue +20 -20
  99. package/components/forms/README.md +1 -1
  100. package/components/forms/checkbox/ACheckbox.vue +149 -149
  101. package/components/forms/checkbox/ACheckboxV2.vue +229 -229
  102. package/components/forms/feedback-form/FeedbackForm.vue +177 -177
  103. package/components/forms/feedback-form/FeedbackFormV2.vue +186 -183
  104. package/components/forms/input/AInputV2.vue +560 -556
  105. package/components/forms/input/button/AInputButton.vue +53 -53
  106. package/components/forms/input/date/AInputDate.vue +217 -217
  107. package/components/forms/input/password/AInputPassword.vue +59 -59
  108. package/components/forms/input/standard/AInputStandard.vue +424 -424
  109. package/components/forms/input/textarea/ATextarea.vue +207 -207
  110. package/components/forms/input/textarea/ATextareaV2.vue +253 -253
  111. package/components/forms/radio-button/ARadioButton.vue +95 -95
  112. package/components/forms/request-demo/ARequestDemo.vue +108 -108
  113. package/components/forms/toggle/AToggle.vue +157 -157
  114. package/components/forms/toggle/AToggleV2.vue +72 -72
  115. package/components/forms/toggle/types.ts +16 -16
  116. package/components/modals/AConfirmationEmail.vue +40 -40
  117. package/components/modals/AboutTariffModal.vue +115 -115
  118. package/components/modals/AnotherDeviceModal.vue +65 -65
  119. package/components/modals/ApplicationAcceptedModal.vue +45 -45
  120. package/components/modals/ConnectingTariffModal.vue +60 -60
  121. package/components/modals/ContactsMobileModel.vue +182 -182
  122. package/components/modals/ContentNavigationModal.vue +100 -100
  123. package/components/modals/Insufficient-funds-modal.vue +62 -62
  124. package/components/modals/LimitReachedModal.vue +44 -44
  125. package/components/modals/NoAccessModal.vue +39 -39
  126. package/components/modals/PaymentMethodModal.vue +101 -101
  127. package/components/modals/RedirectsBanksModal.vue +69 -69
  128. package/components/modals/ReplenishModal.vue +132 -132
  129. package/components/modals/ReportBugConfirmModal.vue +32 -32
  130. package/components/modals/ReportBugModal.vue +172 -172
  131. package/components/modals/Resend.vue +81 -81
  132. package/components/modals/SubmitApplicationModal.vue +126 -126
  133. package/components/modals/id/AuthModal.vue +72 -72
  134. package/components/modals/id/IdAutoLogoutModal.vue +44 -44
  135. package/components/modals/id/IdBanner.vue +41 -41
  136. package/components/modals/id/IdConfirmAccountOtpModal.vue +182 -182
  137. package/components/modals/id/IdConfirmSuccessfulModal.vue +41 -41
  138. package/components/modals/id/IdLoginModal.vue +336 -336
  139. package/components/modals/id/IdModals.vue +125 -125
  140. package/components/modals/id/IdNewPasswordModal.vue +129 -129
  141. package/components/modals/id/IdOtpInput.vue +156 -156
  142. package/components/modals/id/IdPasswordSuccessfulModal.vue +25 -25
  143. package/components/modals/id/IdRecoveryModal.vue +117 -117
  144. package/components/modals/id/IdRegistrationModal.vue +215 -215
  145. package/components/modals/id/IdResetPasswordOtpModal.vue +159 -159
  146. package/components/modals/id/IdTwoFactorModal.vue +126 -126
  147. package/components/navigation/README.md +1 -1
  148. package/components/navigation/bottom-navigation/ABottomNavigation.vue +106 -106
  149. package/components/navigation/breadcrumbs/ABreadcrumbs.vue +128 -128
  150. package/components/navigation/breadcrumbs/types.ts +8 -8
  151. package/components/navigation/footer/AFooter.vue +283 -283
  152. package/components/navigation/footer/ui/a-footer-accordion.vue +118 -118
  153. package/components/navigation/header/AHeader.vue +237 -237
  154. package/components/navigation/header/AlmatyContacts.vue +145 -145
  155. package/components/navigation/header/AstanaContacts.vue +67 -67
  156. package/components/navigation/header/CardGallery.vue +252 -252
  157. package/components/navigation/header/ContactMenu.vue +48 -48
  158. package/components/navigation/header/HeaderLink.vue +250 -250
  159. package/components/navigation/header/HeaderUsage.vue +139 -139
  160. package/components/navigation/header/ListItem.vue +32 -32
  161. package/components/navigation/header/NavCard.vue +42 -42
  162. package/components/navigation/header/NavList.vue +73 -73
  163. package/components/navigation/header/NotificationsMenu.vue +50 -50
  164. package/components/navigation/header/ProductMenu.vue +101 -101
  165. package/components/navigation/header/ProfileMenu.vue +168 -168
  166. package/components/navigation/header/SystemNotification.vue +108 -108
  167. package/components/navigation/header/types.ts +28 -28
  168. package/components/navigation/line-tabs/ALineTabs.vue +112 -112
  169. package/components/navigation/line-tabs/types.ts +21 -21
  170. package/components/navigation/mobile-navigation/AMobileNavigation.vue +112 -112
  171. package/components/navigation/pill-tabs/APillTabs.vue +179 -179
  172. package/components/navigation/pill-tabs/APillTabsV2.vue +241 -241
  173. package/components/navigation/pill-tabs/types.ts +24 -24
  174. package/components/navigation/side-navigation/ASideNavigation.vue +406 -406
  175. package/components/overlays/README.md +1 -1
  176. package/components/overlays/drawer/ADrawerV2.vue +335 -335
  177. package/components/overlays/modal/AModal.vue +335 -335
  178. package/components/overlays/modal/AModalV2.vue +400 -400
  179. package/components/overlays/side-panel/ASidePanel.vue +445 -445
  180. package/components/overlays/sideover/ASlideover.vue +351 -351
  181. package/components/overlays/tooltip/ATooltip.vue +238 -238
  182. package/components/overlays/tooltip/ATooltipV2.vue +226 -226
  183. package/components/overlays/tooltip/types.ts +26 -26
  184. package/components/overlays/tooltip/useTooltipTrigger.ts +101 -101
  185. package/components/transitions/ATransitionHeight.vue +67 -67
  186. package/components/utils/index.ts +19 -19
  187. package/components/utils/removeTrailingSlash.ts +7 -7
  188. package/composables/highlight.ts +19 -19
  189. package/composables/modalsState.ts +8 -8
  190. package/composables/projectState.ts +2 -2
  191. package/composables/useAccessTokenCookie.ts +25 -25
  192. package/composables/useActiveNavigation.ts +84 -84
  193. package/composables/useAdaptive.ts +46 -46
  194. package/composables/useBuyTariffs.ts +91 -91
  195. package/composables/useCarouselScroll.ts +49 -49
  196. package/composables/useChipOverflow.ts +92 -92
  197. package/composables/useHeaderNavigationLinks.ts +519 -519
  198. package/composables/useIdModals.ts +36 -36
  199. package/composables/usePayment.ts +78 -78
  200. package/composables/useUIValidation.ts +16 -16
  201. package/composables/useUrls.ts +23 -23
  202. package/eslint.config.mjs +58 -58
  203. package/i18n/i18n.config.ts +21 -21
  204. package/i18n.config.ts +21 -21
  205. package/icons/adata-logo.vue +17 -17
  206. package/icons/ai-assistant.vue +13 -13
  207. package/icons/akimat.vue +20 -20
  208. package/icons/archive.vue +16 -16
  209. package/icons/arrow/arrow-bottom-left-on-square.vue +5 -5
  210. package/icons/arrow/arrow-circle-down.vue +16 -16
  211. package/icons/arrow/arrow-circle-reset.vue +16 -16
  212. package/icons/arrow/arrow-corner.vue +9 -9
  213. package/icons/arrow/arrow-graph-down.vue +13 -13
  214. package/icons/arrow/arrow-graph-up.vue +14 -14
  215. package/icons/arrow/arrow-left-right.vue +8 -8
  216. package/icons/arrow/arrow-left.vue +14 -14
  217. package/icons/arrow/arrow-right.vue +14 -14
  218. package/icons/arrow/arrow-square-down.vue +15 -15
  219. package/icons/arrow/arrow-top-right-on-square.vue +6 -6
  220. package/icons/arrow/arrows-in.vue +11 -11
  221. package/icons/arrow/arrows-out.vue +11 -11
  222. package/icons/arrow/arrows-resize-in.vue +38 -38
  223. package/icons/arrow/arrows-resize-out.vue +17 -17
  224. package/icons/arrow-currency-gray.vue +5 -5
  225. package/icons/arrow-currency-green.vue +5 -5
  226. package/icons/arrow-currency-red.vue +5 -5
  227. package/icons/arrow-side-up.vue +6 -6
  228. package/icons/avatar.vue +12 -12
  229. package/icons/bank.vue +5 -5
  230. package/icons/bar-chart-3.vue +3 -3
  231. package/icons/bill.vue +3 -3
  232. package/icons/block.vue +16 -16
  233. package/icons/book-open.vue +3 -3
  234. package/icons/bookmark/bookmark-filled.vue +19 -19
  235. package/icons/bookmark/bookmark.vue +5 -5
  236. package/icons/bot.vue +52 -52
  237. package/icons/braces.vue +3 -3
  238. package/icons/briefcase.vue +14 -14
  239. package/icons/browsers/browser-duck.vue +65 -65
  240. package/icons/browsers/browser-google.vue +30 -30
  241. package/icons/browsers/browser-yandex.vue +13 -13
  242. package/icons/building-vector.vue +7 -7
  243. package/icons/building.vue +9 -9
  244. package/icons/calculator.vue +7 -7
  245. package/icons/calendar.vue +24 -24
  246. package/icons/car-side.vue +16 -16
  247. package/icons/car.vue +7 -7
  248. package/icons/cart.vue +15 -15
  249. package/icons/chart-bar.vue +5 -5
  250. package/icons/chart-no-axes-column.vue +3 -3
  251. package/icons/chart-pie.vue +16 -16
  252. package/icons/check/check-circle.vue +6 -6
  253. package/icons/check/check-outline.vue +3 -3
  254. package/icons/check/check-shield.vue +14 -14
  255. package/icons/check/check.vue +17 -17
  256. package/icons/check/checkmark-circle.vue +6 -6
  257. package/icons/check-sb.vue +7 -7
  258. package/icons/checkbox/checkbox-active.vue +17 -17
  259. package/icons/checkbox/checkbox-empty.vue +10 -10
  260. package/icons/checkbox/checkbox-intermediate.vue +7 -7
  261. package/icons/chevron/chevron-down-outline.vue +3 -3
  262. package/icons/chevron/chevron-down.vue +5 -5
  263. package/icons/chevron/chevron-left.vue +5 -5
  264. package/icons/chevron/chevron-right.vue +5 -5
  265. package/icons/chevron/chevron-up.vue +5 -5
  266. package/icons/chevron/double-chevron-right.vue +12 -12
  267. package/icons/clipboard-list.vue +3 -3
  268. package/icons/clipboard-text.vue +7 -7
  269. package/icons/clock-outline.vue +3 -3
  270. package/icons/clock-time.vue +24 -24
  271. package/icons/clock.vue +25 -25
  272. package/icons/close.vue +16 -16
  273. package/icons/cloud/cloud-upload.vue +3 -3
  274. package/icons/code.vue +9 -9
  275. package/icons/company/company-bazis.vue +21 -21
  276. package/icons/company/company-bereke.vue +25 -25
  277. package/icons/company/company-bigroup.vue +16 -16
  278. package/icons/company/company-erg.vue +17 -17
  279. package/icons/company/company-forte.vue +21 -21
  280. package/icons/company/company-halyk.vue +15 -15
  281. package/icons/company/company-jusan.vue +31 -31
  282. package/icons/company/company-kaspi.vue +14 -14
  283. package/icons/company/company-kazzinc.vue +22 -22
  284. package/icons/company/company-mycar.vue +13 -13
  285. package/icons/company/company-samruk.vue +21 -21
  286. package/icons/company-egov-small.vue +15 -15
  287. package/icons/company.vue +8 -8
  288. package/icons/compare-outlined.vue +8 -8
  289. package/icons/copy.vue +7 -7
  290. package/icons/currency/currency-dollar.vue +16 -16
  291. package/icons/currency/currency-down.vue +22 -22
  292. package/icons/currency/currency-eur.vue +57 -57
  293. package/icons/currency/currency-rub.vue +7 -7
  294. package/icons/currency/currency-tenge.vue +9 -9
  295. package/icons/currency/currency-usd.vue +221 -221
  296. package/icons/currency/currency-yuan.vue +25 -25
  297. package/icons/database.vue +3 -3
  298. package/icons/default-avatar.vue +20 -20
  299. package/icons/delete.vue +7 -7
  300. package/icons/document.vue +5 -5
  301. package/icons/dots-horizontal.vue +31 -31
  302. package/icons/download-line.vue +13 -13
  303. package/icons/download-outline.vue +3 -3
  304. package/icons/download-rounded.vue +15 -15
  305. package/icons/download.vue +11 -11
  306. package/icons/edit.vue +7 -7
  307. package/icons/education.vue +16 -16
  308. package/icons/egov-small.vue +7 -7
  309. package/icons/egov.vue +41 -41
  310. package/icons/elicense.vue +38 -38
  311. package/icons/excel-icon.vue +14 -14
  312. package/icons/expand-window.vue +7 -7
  313. package/icons/eye-closed.vue +26 -26
  314. package/icons/eye-open.vue +7 -7
  315. package/icons/eye-opened.vue +23 -23
  316. package/icons/favourite-bookmark-outline.vue +17 -17
  317. package/icons/file/file-check-corner.vue +3 -3
  318. package/icons/file/file-spreadsheet.vue +3 -3
  319. package/icons/file/file.vue +16 -16
  320. package/icons/file/files.vue +16 -16
  321. package/icons/file-json.vue +3 -3
  322. package/icons/file-pen.vue +31 -31
  323. package/icons/file-search.vue +3 -3
  324. package/icons/file-stack.vue +31 -31
  325. package/icons/file-text.vue +3 -3
  326. package/icons/file-types/excel.vue +131 -131
  327. package/icons/file-types/html.vue +13 -13
  328. package/icons/file-types/jpeg.vue +16 -16
  329. package/icons/file-types/pdf.vue +11 -11
  330. package/icons/file-types/png.vue +11 -11
  331. package/icons/file-types/rar.vue +11 -11
  332. package/icons/file-types/word.vue +50 -50
  333. package/icons/file-types/zip.vue +11 -11
  334. package/icons/filter.vue +5 -5
  335. package/icons/flag.vue +7 -7
  336. package/icons/flask-conical.vue +3 -3
  337. package/icons/folder-open.vue +3 -3
  338. package/icons/gauge.vue +17 -17
  339. package/icons/gift-outline.vue +3 -3
  340. package/icons/gift.vue +8 -8
  341. package/icons/git-compare.vue +38 -38
  342. package/icons/git-fork.vue +3 -3
  343. package/icons/globe-2.vue +3 -3
  344. package/icons/globe.vue +16 -16
  345. package/icons/google.vue +41 -41
  346. package/icons/gradiend-icons/files-gradient.vue +22 -22
  347. package/icons/gradiend-icons/hammer-gradient.vue +62 -62
  348. package/icons/graduation-cap.vue +31 -31
  349. package/icons/graph-loading.vue +34 -34
  350. package/icons/graph-pie.vue +14 -14
  351. package/icons/hand/hand-thumb-up-filled.vue +5 -5
  352. package/icons/hand/hand-thumb-up.vue +5 -5
  353. package/icons/hand-with-phone/hand-with-phone-dark.vue +52 -52
  354. package/icons/hand-with-phone/hand-with-phone-light.vue +52 -52
  355. package/icons/handshake.vue +5 -5
  356. package/icons/hcheck.vue +14 -14
  357. package/icons/hdocument.vue +7 -7
  358. package/icons/headset.vue +6 -6
  359. package/icons/help-question-circle.vue +26 -26
  360. package/icons/history.vue +5 -5
  361. package/icons/home.vue +14 -14
  362. package/icons/horizontal-more.vue +13 -13
  363. package/icons/hot-line.vue +6 -6
  364. package/icons/hummer.vue +16 -16
  365. package/icons/id.vue +7 -7
  366. package/icons/in-row-type.vue +46 -46
  367. package/icons/in-table-type.vue +29 -29
  368. package/icons/info/info-circle.vue +29 -29
  369. package/icons/invoice.vue +8 -8
  370. package/icons/judge.vue +14 -14
  371. package/icons/kaspi-qr.vue +13 -13
  372. package/icons/key-round.vue +3 -3
  373. package/icons/landmark.vue +3 -3
  374. package/icons/layers.vue +3 -3
  375. package/icons/letter/letter-a.vue +8 -8
  376. package/icons/letter/letter-b.vue +6 -6
  377. package/icons/letter/letter-c.vue +6 -6
  378. package/icons/letter/letter-d.vue +6 -6
  379. package/icons/letter/letter-e.vue +6 -6
  380. package/icons/link-chain.vue +28 -28
  381. package/icons/link-url.vue +3 -3
  382. package/icons/link.vue +7 -7
  383. package/icons/linkedin.vue +24 -24
  384. package/icons/list-tree.vue +3 -3
  385. package/icons/loader-circle.vue +27 -27
  386. package/icons/location.vue +8 -8
  387. package/icons/lock.vue +5 -5
  388. package/icons/logo.vue +15 -15
  389. package/icons/logout.vue +16 -16
  390. package/icons/magnify/magnifying-glass-minus.vue +8 -8
  391. package/icons/magnify/magnifying-glass-plus.vue +9 -9
  392. package/icons/magnify/magnifying-glass.vue +9 -9
  393. package/icons/mail-question-mark.vue +8 -8
  394. package/icons/mail.vue +7 -7
  395. package/icons/mailru.vue +34 -34
  396. package/icons/main-filter.vue +5 -5
  397. package/icons/map/map-pin-filled.vue +7 -7
  398. package/icons/map/map-pin-rect.vue +15 -15
  399. package/icons/map/map-pin.vue +7 -7
  400. package/icons/map-marker-start.vue +7 -7
  401. package/icons/map-paper.vue +5 -5
  402. package/icons/medal.vue +7 -7
  403. package/icons/menu-filled.vue +20 -20
  404. package/icons/menu.vue +8 -8
  405. package/icons/message/message-square.vue +3 -3
  406. package/icons/message/message.vue +12 -12
  407. package/icons/message-dotted.vue +30 -30
  408. package/icons/messages-square.vue +3 -3
  409. package/icons/minus/minus-circle.vue +10 -10
  410. package/icons/money.vue +5 -5
  411. package/icons/monitoring.vue +10 -10
  412. package/icons/moon.vue +8 -8
  413. package/icons/more.vue +29 -29
  414. package/icons/network.vue +3 -3
  415. package/icons/newspaper.vue +3 -3
  416. package/icons/note-pencil.vue +14 -14
  417. package/icons/note.vue +14 -14
  418. package/icons/notification-error.vue +17 -17
  419. package/icons/notification.vue +16 -16
  420. package/icons/paperclip.vue +7 -7
  421. package/icons/payment/payment-card.vue +6 -6
  422. package/icons/payment/payment-kaspi.vue +11 -11
  423. package/icons/person-vector.vue +7 -7
  424. package/icons/person-with-briefcase.vue +10 -10
  425. package/icons/person.vue +22 -22
  426. package/icons/phone-call.vue +6 -6
  427. package/icons/phone-filled.vue +5 -5
  428. package/icons/phone-reversed.vue +3 -3
  429. package/icons/phone.vue +7 -7
  430. package/icons/plus/plus-circle.vue +8 -8
  431. package/icons/plus/plus.vue +13 -13
  432. package/icons/profile.vue +5 -5
  433. package/icons/radio/radio-check.vue +8 -8
  434. package/icons/radio/radio-empty.vue +10 -10
  435. package/icons/receipt.vue +8 -8
  436. package/icons/robot-outline.vue +3 -3
  437. package/icons/robot.vue +14 -14
  438. package/icons/route.vue +7 -7
  439. package/icons/sanctions.vue +8 -8
  440. package/icons/scales/scale-outline.vue +3 -3
  441. package/icons/scales/scale.vue +16 -16
  442. package/icons/scales/scales.vue +5 -5
  443. package/icons/scales/standing-scales.vue +15 -15
  444. package/icons/scan-search.vue +3 -3
  445. package/icons/search-outline.vue +3 -3
  446. package/icons/search.vue +27 -27
  447. package/icons/send.vue +3 -3
  448. package/icons/settings.vue +26 -26
  449. package/icons/share/share-alt.vue +5 -5
  450. package/icons/share/share.vue +14 -14
  451. package/icons/shield-alert.vue +31 -31
  452. package/icons/shield-verified.vue +14 -14
  453. package/icons/ship.vue +8 -8
  454. package/icons/socials/face-book.vue +15 -15
  455. package/icons/socials/facebook-color.vue +16 -16
  456. package/icons/socials/globe-color.vue +16 -16
  457. package/icons/socials/instagram-color.vue +46 -46
  458. package/icons/socials/instagram.vue +19 -19
  459. package/icons/socials/linkedin-color.vue +14 -14
  460. package/icons/socials/telegram-color.vue +16 -16
  461. package/icons/socials/telegram.vue +15 -15
  462. package/icons/socials/tik-tok.vue +15 -15
  463. package/icons/socials/tiktok-color.vue +14 -14
  464. package/icons/socials/vk-color.vue +16 -16
  465. package/icons/socials/youtube-color.vue +16 -16
  466. package/icons/socials/youtube.vue +16 -16
  467. package/icons/sort/sort-asc.vue +15 -15
  468. package/icons/sort/sort-desc.vue +15 -15
  469. package/icons/sparkles.vue +3 -3
  470. package/icons/splitting-arrows.vue +8 -8
  471. package/icons/star/star-filled.vue +41 -41
  472. package/icons/star/star-half-filled.vue +21 -21
  473. package/icons/star/star.vue +25 -25
  474. package/icons/sun.vue +25 -25
  475. package/icons/sviazi.vue +5 -5
  476. package/icons/tag.vue +30 -30
  477. package/icons/tasks.vue +10 -10
  478. package/icons/tender-search.vue +11 -11
  479. package/icons/terminal.vue +3 -3
  480. package/icons/toasts/check-circle-toast.vue +6 -6
  481. package/icons/toasts/warning-triangle-toast.vue +8 -8
  482. package/icons/trash.vue +13 -13
  483. package/icons/triangle-alert.vue +3 -3
  484. package/icons/triangle.vue +10 -10
  485. package/icons/truck.vue +7 -7
  486. package/icons/user-square.vue +14 -14
  487. package/icons/user.vue +22 -22
  488. package/icons/users-focus.vue +8 -8
  489. package/icons/users-outline.vue +3 -3
  490. package/icons/users-round.vue +3 -3
  491. package/icons/users-three.vue +7 -7
  492. package/icons/users.vue +8 -8
  493. package/icons/view-eye-open.vue +13 -13
  494. package/icons/warning/warning-circle.vue +29 -29
  495. package/icons/warning/warning-triangle-filled.vue +12 -12
  496. package/icons/warning/warning-triangle.vue +28 -28
  497. package/icons/webhook.vue +3 -3
  498. package/icons/whatsapp.vue +8 -8
  499. package/icons/work-bag.vue +11 -11
  500. package/icons/work-case.vue +9 -9
  501. package/icons/work-search.vue +10 -10
  502. package/icons/work.vue +5 -5
  503. package/icons/workflow.vue +3 -3
  504. package/icons/x-mark.vue +15 -15
  505. package/icons/x-outline.vue +3 -3
  506. package/icons/yandex.vue +29 -29
  507. package/icons/zap.vue +3 -3
  508. package/illustrations/address-location.vue +38 -38
  509. package/illustrations/award-badge.vue +105 -105
  510. package/illustrations/ball-with-chain.vue +120 -120
  511. package/illustrations/bill.vue +133 -133
  512. package/illustrations/buildings.vue +82 -82
  513. package/illustrations/calendar.vue +156 -156
  514. package/illustrations/chains.vue +152 -152
  515. package/illustrations/coin-percent.vue +126 -126
  516. package/illustrations/coins-stack.vue +202 -202
  517. package/illustrations/delete-dark.vue +32 -32
  518. package/illustrations/delete.vue +33 -33
  519. package/illustrations/doc-with-stamp.vue +126 -126
  520. package/illustrations/document.vue +64 -64
  521. package/illustrations/door.vue +85 -85
  522. package/illustrations/empty-badge-check.vue +20 -20
  523. package/illustrations/empty-ban.vue +18 -18
  524. package/illustrations/empty-box.vue +77 -77
  525. package/illustrations/empty-filter.vue +20 -20
  526. package/illustrations/empty-inbox.vue +19 -19
  527. package/illustrations/empty-scale.vue +22 -22
  528. package/illustrations/empty-search-x.vue +21 -21
  529. package/illustrations/empty-shopping-cart.vue +21 -21
  530. package/illustrations/empty-users-round.vue +20 -20
  531. package/illustrations/empty-wallet.vue +127 -127
  532. package/illustrations/graph-in-coin.vue +119 -119
  533. package/illustrations/hammer.vue +156 -156
  534. package/illustrations/hand-cash.vue +108 -108
  535. package/illustrations/info.vue +52 -52
  536. package/illustrations/mail-active.vue +86 -86
  537. package/illustrations/mail.vue +68 -68
  538. package/illustrations/no-access.vue +92 -92
  539. package/illustrations/ok.vue +90 -90
  540. package/illustrations/people-group.vue +237 -237
  541. package/illustrations/person-with-phone.vue +187 -187
  542. package/illustrations/person.vue +159 -159
  543. package/illustrations/phone-check.vue +110 -110
  544. package/illustrations/phone-payment-method.vue +223 -223
  545. package/illustrations/search.vue +14 -14
  546. package/illustrations/shield-user.vue +94 -94
  547. package/illustrations/stop-hand.vue +84 -84
  548. package/illustrations/stop-sign.vue +34 -34
  549. package/illustrations/suit.vue +97 -97
  550. package/illustrations/suitcase.vue +105 -105
  551. package/illustrations/terminal.vue +100 -100
  552. package/illustrations/trash-can.vue +66 -66
  553. package/illustrations/turn-on-tariff.vue +38 -38
  554. package/illustrations/two-persons.vue +169 -169
  555. package/lang/en.ts +877 -877
  556. package/lang/kk.ts +879 -879
  557. package/lang/ru.ts +879 -879
  558. package/layouts/default.vue +13 -13
  559. package/nuxt.config.ts +95 -95
  560. package/package.json +64 -64
  561. package/plugins/maska.ts +4 -4
  562. package/plugins/toast.client.ts +41 -41
  563. package/public/kaspi/logo.svg +4 -4
  564. package/shared/constans/pages.ts +96 -96
  565. package/tsconfig.json +6 -6
  566. package/utils/getMaxZindex.ts +25 -25
  567. package/utils/localizedNavigation.ts +36 -36
  568. package/utils/twMerge.ts +34 -34
  569. package/vitest.config.ts +14 -14
@@ -1,556 +1,560 @@
1
- <script generic="T, K extends keyof T" lang="ts" setup>
2
- import type { Component } from 'vue'
3
- import { autoUpdate, flip, size as floatingSize, offset, shift, useFloating } from '@floating-ui/vue'
4
- import { onClickOutside } from '@vueuse/core'
5
- import { MaskInput } from 'maska'
6
-
7
- defineOptions({ inheritAttrs: false })
8
-
9
- const props = withDefaults(defineProps<Props>(), {
10
- type: 'text',
11
- required: false,
12
- disabled: false,
13
- readonly: false,
14
- clearable: false,
15
- size: 'sm',
16
- error: undefined,
17
- placeholder: '',
18
- autocomplete: 'off',
19
- loading: false,
20
- variant: 'default',
21
- mask: undefined,
22
- autocompleteList: () => [] as T[],
23
- autocompleteKey: undefined,
24
- autocompleteOption: undefined,
25
- autocompleteFn: undefined,
26
- })
27
-
28
- const emit = defineEmits<{
29
- (e: 'clear'): void
30
- (e: 'focus', ev: FocusEvent): void
31
- (e: 'blur', ev: FocusEvent): void
32
- (e: 'keydown', ev: KeyboardEvent): void
33
- (e: 'enter', value: string | AutocompleteValue): void
34
- (e: 'selectOption', value: AutocompleteValue): void
35
- (e: 'updateValue', value: string | number): void
36
- }>()
37
-
38
- interface Props {
39
- label?: string
40
- type?: 'text' | 'search' | 'email' | 'password' | 'tel' | 'url' | 'number'
41
- placeholder?: string
42
- required?: boolean
43
- disabled?: boolean
44
- readonly?: boolean
45
- clearable?: boolean
46
- size?: 'sm' | 'md' | 'lg'
47
- error?: string | string[]
48
- startIcon?: Component
49
- startIconClass?: string
50
- endIcon?: Component
51
- loading?: boolean
52
- variant?: 'default' | 'gray' | 'blue' | 'canvas'
53
- mask?: string
54
- inputId?: string
55
- autocomplete?: string
56
- autocompleteList?: T[]
57
- autocompleteKey?: K
58
- autocompleteOption?: K
59
- autocompleteFn?: (item: T) => string
60
- }
61
-
62
- type AutocompleteValue = T | T[keyof T]
63
-
64
- function customTrim(input: string | number | null | undefined): string | number {
65
- if (input == null) return ''
66
- if (typeof input !== 'string') return input
67
- if (input === ' ' || /\d/.test(input)) return input.trim()
68
- return input
69
- }
70
-
71
- const [modelValue, modelModifiers] = defineModel<string | number | AutocompleteValue>({
72
- default: '',
73
- set(value) {
74
- if (modelModifiers['custom-trim']) return customTrim(value as string)
75
- return value
76
- },
77
- })
78
-
79
- const inputRef = ref<HTMLInputElement | null>(null)
80
- const isFocused = ref(false)
81
-
82
- let maskInput: MaskInput | null = null
83
- onMounted(() => {
84
- if (props.mask && inputRef.value) {
85
- maskInput = new MaskInput(inputRef.value, { mask: props.mask })
86
- }
87
- })
88
- onUnmounted(() => {
89
- maskInput?.destroy()
90
- maskInput = null
91
- })
92
-
93
- const internalId = useId()
94
- const inputId = computed(() => props.inputId ?? `input-v2-${internalId}`)
95
-
96
- const hasValue = computed(() => modelValue.value != null && modelValue.value !== '')
97
- const hasError = computed(() => Array.isArray(props.error) ? props.error.length > 0 : !!props.error)
98
- const isLabelFloated = computed(() => hasValue.value || isFocused.value)
99
-
100
- const hasAutocomplete = computed(() => props.autocompleteList && props.autocompleteList.length > 0)
101
- const isDropdownOpen = computed(() => hasAutocomplete.value && isFocused.value)
102
- const currentItemIndex = ref(-1)
103
-
104
- const fieldRef = ref<HTMLElement | null>(null)
105
- const floatingRef = ref<HTMLElement | null>(null)
106
- const { x, y, strategy, update } = useFloating(fieldRef, floatingRef, {
107
- placement: 'bottom-start',
108
- strategy: 'absolute',
109
- middleware: [
110
- offset(6),
111
- flip({ fallbackPlacements: ['top-start'] }),
112
- shift({ padding: 8 }),
113
- floatingSize({
114
- apply({ availableHeight, elements, rects }) {
115
- const el = elements.floating as HTMLElement
116
- el.style.width = `${rects.reference.width}px`
117
- el.style.maxHeight = `${Math.min(availableHeight, 320)}px`
118
- },
119
- }),
120
- ],
121
- })
122
-
123
- let cleanup: (() => void) | null = null
124
- watch(isDropdownOpen, (open) => {
125
- if (open && fieldRef.value && floatingRef.value) {
126
- cleanup = autoUpdate(fieldRef.value, floatingRef.value, update)
127
- }
128
- else {
129
- cleanup?.()
130
- cleanup = null
131
- currentItemIndex.value = -1
132
- }
133
- })
134
- onUnmounted(() => cleanup?.())
135
-
136
- const wrapperRef = ref<HTMLDivElement | null>(null)
137
- onClickOutside(wrapperRef, () => {
138
- if (isFocused.value) inputRef.value?.blur()
139
- })
140
-
141
- function resolveSelectedValue(item: T): AutocompleteValue {
142
- if (props.autocompleteFn) return props.autocompleteFn(item) as AutocompleteValue
143
- if (props.autocompleteKey) return item[props.autocompleteKey] as AutocompleteValue
144
- return item
145
- }
146
-
147
- function applyCurrentItem() {
148
- const item = props.autocompleteList[currentItemIndex.value]
149
- if (item == null) return
150
- modelValue.value = resolveSelectedValue(item)
151
- }
152
-
153
- function onSelectOption(item: T) {
154
- const value = resolveSelectedValue(item)
155
- modelValue.value = value
156
- emit('selectOption', value)
157
- inputRef.value?.blur()
158
- }
159
-
160
- const isTouchScrolling = ref(false)
161
- function onTouchEndOption(item: T) {
162
- if (!isTouchScrolling.value) onSelectOption(item)
163
- isTouchScrolling.value = false
164
- }
165
-
166
- function onInput(ev: Event) {
167
- const value = (ev.target as HTMLInputElement).value
168
- modelValue.value = value
169
- emit('updateValue', value)
170
- }
171
-
172
- function onFocus(ev: FocusEvent) {
173
- isFocused.value = true
174
- emit('focus', ev)
175
- }
176
-
177
- function onBlur(ev: FocusEvent) {
178
- isFocused.value = false
179
- emit('blur', ev)
180
- }
181
-
182
- const autocompleteListEl = ref<HTMLUListElement | null>(null)
183
- function scrollIntoView(idx: number) {
184
- const container = autocompleteListEl.value
185
- if (!container) return
186
- const item = container.children[idx] as HTMLElement | undefined
187
- if (!item) return
188
- const itemTop = item.offsetTop
189
- const itemBottom = itemTop + item.clientHeight
190
- if (itemTop < container.scrollTop) container.scrollTop = itemTop
191
- else if (itemBottom > container.scrollTop + container.clientHeight)
192
- container.scrollTop = itemBottom - container.clientHeight
193
- }
194
-
195
- function onKeydown(ev: KeyboardEvent) {
196
- emit('keydown', ev)
197
-
198
- if (hasAutocomplete.value) {
199
- const last = props.autocompleteList.length - 1
200
- if (ev.key === 'ArrowDown' && currentItemIndex.value < last) {
201
- ev.preventDefault()
202
- currentItemIndex.value++
203
- applyCurrentItem()
204
- nextTick(() => scrollIntoView(currentItemIndex.value))
205
- return
206
- }
207
- if (ev.key === 'ArrowUp' && currentItemIndex.value > 0) {
208
- ev.preventDefault()
209
- currentItemIndex.value--
210
- applyCurrentItem()
211
- nextTick(() => scrollIntoView(currentItemIndex.value))
212
- return
213
- }
214
- if (ev.key === 'Enter') {
215
- if (currentItemIndex.value !== -1) {
216
- applyCurrentItem()
217
- emit('enter', props.autocompleteList[currentItemIndex.value])
218
- }
219
- else {
220
- emit('enter', (ev.target as HTMLInputElement).value)
221
- }
222
- inputRef.value?.blur()
223
- return
224
- }
225
- if (ev.key === 'Escape') {
226
- inputRef.value?.blur()
227
- return
228
- }
229
- }
230
-
231
- if (ev.key === 'Enter') emit('enter', (ev.target as HTMLInputElement).value)
232
- }
233
-
234
- function onClear() {
235
- modelValue.value = ''
236
- emit('clear')
237
- emit('updateValue', '')
238
- inputRef.value?.focus()
239
- }
240
-
241
- function focus() {
242
- inputRef.value?.focus()
243
- }
244
-
245
- function blur() {
246
- inputRef.value?.blur()
247
- }
248
-
249
- function onFieldMousedown(ev: MouseEvent) {
250
- if (ev.target === inputRef.value) return
251
- ev.preventDefault()
252
- inputRef.value?.focus()
253
- }
254
-
255
- defineExpose({ focus, blur, inputRef })
256
-
257
- const layoutClass = computed(() => {
258
- if (!props.label) {
259
- if (props.size === 'lg') return 'h-11 py-2'
260
- return props.size === 'md' ? 'h-10 py-1.5' : 'h-9 py-1.5'
261
- }
262
- if (props.size === 'lg') return 'h-11 pt-4 pb-1'
263
- return props.size === 'md' ? 'h-10 pt-4 pb-0.5' : 'h-9 pt-3 pb-0.5'
264
- })
265
-
266
- /* Компенсация асимметричных паддингов поля с label: иконки возвращаются к
267
- визуальному центру. Величина = (pt + контент/2) - h/2:
268
- sm 12+11-18=5, md 16+11-20=7, lg 16+12-22=6. */
269
- const iconOffsetClass = computed(() => {
270
- if (!props.label) return ''
271
- if (props.size === 'lg') return '-translate-y-[6px]'
272
- return props.size === 'md' ? '-translate-y-[7px]' : '-translate-y-[5px]'
273
- })
274
-
275
- const showClearButton = computed(() =>
276
- props.clearable && hasValue.value && !props.disabled && !props.readonly && !props.loading,
277
- )
278
-
279
- const errorMessages = computed(() => {
280
- if (!props.error) return [] as string[]
281
- return Array.isArray(props.error) ? props.error : [props.error]
282
- })
283
-
284
- const attrs = useAttrs()
285
- const wrapperAttrs = computed(() => ({ class: attrs.class, style: attrs.style }))
286
- const inputAttrs = computed(() => {
287
- const { class: _c, style: _s, ...rest } = attrs as Record<string, unknown>
288
- return rest
289
- })
290
-
291
- const fieldClass = computed(() => {
292
- if (props.disabled) {
293
- return 'border-default bg-badge/70'
294
- }
295
- if (props.variant === 'gray') {
296
- /* по макету ДС-2026 у залитого поля бордер видимый, а не прозрачный */
297
- return 'border-default bg-muted'
298
- }
299
- if (props.variant === 'blue') {
300
- return 'border-brand-200 bg-brand-subtle'
301
- }
302
- if (props.variant === 'canvas') {
303
- /* «утопленная» зона ДС + hero-поиск лендинга: bg-canvas глубже bg-muted в дарке */
304
- return 'border-default bg-canvas'
305
- }
306
- return 'border-default bg-main'
307
- })
308
-
309
- function rowDisplay(item: T): unknown {
310
- if (props.autocompleteOption) return item[props.autocompleteOption]
311
- return item
312
- }
313
- </script>
314
-
315
- <template>
316
- <div
317
- ref="wrapperRef"
318
- class="input-v2 relative w-full text-body-md"
319
- v-bind="wrapperAttrs"
320
- >
321
- <div
322
- ref="fieldRef"
323
- class="input-v2__field relative flex w-full cursor-text items-center gap-2 rounded-10 border border-solid text-primary transition-colors duration-200 focus-within:border-focus focus-within:ring-2 focus-within:ring-brand-500/20 hover:border-brand"
324
- :class="[
325
- layoutClass,
326
- fieldClass,
327
- startIcon ? 'pl-3' : 'pl-4',
328
- clearable || endIcon || loading || $slots.endButton ? 'pr-3' : 'pr-4',
329
- { 'input-v2__field--error': hasError, 'input-v2__field--disabled': disabled },
330
- ]"
331
- @mousedown="onFieldMousedown"
332
- >
333
- <label
334
- v-if="label"
335
- :for="inputId"
336
- class="input-v2__label pointer-events-none absolute top-1/2 max-w-[calc(100%-32px)] truncate text-secondary"
337
- :data-size="size"
338
- :class="[
339
- startIcon ? 'left-9' : 'left-4',
340
- { 'input-v2__label--floated': isLabelFloated },
341
- ]"
342
- >
343
- {{ label }}<span v-if="required" class="text-danger">&nbsp;*</span>
344
- </label>
345
-
346
- <!-- startIconClass ЗАМЕНЯЕТ дефолтные размер+цвет, а не дописывается:
347
- конфликт одинаковых утилит решается порядком в stylesheet, не в class -->
348
- <component
349
- :is="startIcon"
350
- v-if="startIcon"
351
- class="shrink-0"
352
- :class="[startIconClass || 'size-4 text-icon-disabled', iconOffsetClass]"
353
- />
354
-
355
- <input
356
- :id="inputId"
357
- ref="inputRef"
358
- :type="type"
359
- :value="modelValue ?? ''"
360
- :placeholder="(isLabelFloated || !label) ? placeholder : ''"
361
- :disabled="disabled"
362
- :readonly="readonly"
363
- :autocomplete="autocomplete"
364
- :tabindex="disabled ? -1 : 0"
365
- class="input-v2__input w-full min-w-0 bg-transparent leading-tight text-primary outline-hidden placeholder:text-secondary disabled:cursor-not-allowed"
366
- v-bind="inputAttrs"
367
- @input="onInput"
368
- @focus="onFocus"
369
- @blur="onBlur"
370
- @keydown="onKeydown"
371
- >
372
-
373
- <a-icon-loader-circle
374
- v-if="loading"
375
- class="size-4 shrink-0 animate-spin text-icon-disabled"
376
- :class="iconOffsetClass"
377
- />
378
-
379
- <button
380
- v-else-if="showClearButton"
381
- type="button"
382
- tabindex="-1"
383
- class="flex shrink-0 items-center justify-center rounded-full p-0.5 transition-colors hover:bg-badge"
384
- :class="iconOffsetClass"
385
- @mousedown.prevent
386
- @click.stop="onClear"
387
- >
388
- <a-icon-x-mark class="m-0! size-3.5" />
389
- </button>
390
-
391
- <component
392
- :is="endIcon"
393
- v-else-if="endIcon"
394
- class="size-4 shrink-0 text-icon-disabled"
395
- :class="iconOffsetClass"
396
- />
397
-
398
- <slot name="endButton" />
399
- </div>
400
-
401
- <transition
402
- enter-active-class="input-v2__dropdown-enter-active"
403
- enter-from-class="input-v2__dropdown-enter-from"
404
- enter-to-class="input-v2__dropdown-enter-to"
405
- leave-active-class="input-v2__dropdown-leave-active"
406
- leave-from-class="input-v2__dropdown-leave-from"
407
- leave-to-class="input-v2__dropdown-leave-to"
408
- >
409
- <div
410
- v-if="isDropdownOpen"
411
- ref="floatingRef"
412
- class="input-v2__dropdown z-10000 flex flex-col gap-2 rounded-xl border border-default bg-main p-1.5 shadow-[0_24px_50px_-24px] shadow-brand-500/50"
413
- :style="{
414
- position: strategy,
415
- left: x != null ? `${x}px` : '',
416
- top: y != null ? `${y}px` : '',
417
- }"
418
- >
419
- <ul ref="autocompleteListEl" class="input-v2__list flex flex-col">
420
- <li
421
- v-for="(item, idx) in autocompleteList"
422
- :key="idx"
423
- class="cursor-pointer rounded-lg px-3.5 py-2.5 text-body-md transition-colors duration-100 hover:bg-brand-subtle"
424
- :class="{ 'input-v2__option--active': currentItemIndex === idx }"
425
- @mousedown.prevent="onSelectOption(item)"
426
- @touchend.prevent="onTouchEndOption(item)"
427
- @touchmove="isTouchScrolling = true"
428
- >
429
- <slot name="autocomplete-option" :item="item">
430
- {{ rowDisplay(item) }}
431
- </slot>
432
- </li>
433
- </ul>
434
-
435
- <slot name="autocomplete-buttons" :value="modelValue" />
436
- </div>
437
- </transition>
438
-
439
- <transition
440
- enter-active-class="input-v2__error-enter-active"
441
- enter-from-class="input-v2__error-enter-from"
442
- enter-to-class="input-v2__error-enter-to"
443
- leave-active-class="input-v2__error-leave-active"
444
- leave-from-class="input-v2__error-leave-from"
445
- leave-to-class="input-v2__error-leave-to"
446
- >
447
- <p v-if="hasError" class="mt-1 flex flex-col text-body-sm text-danger">
448
- <span v-for="(err, idx) in errorMessages" :key="idx">{{ err }}</span>
449
- </p>
450
- </transition>
451
- </div>
452
- </template>
453
-
454
- <style scoped>
455
- .input-v2__field--error {
456
- border-color: var(--border-color-danger);
457
- }
458
-
459
- .input-v2__field--disabled {
460
- pointer-events: none;
461
- cursor: not-allowed;
462
- opacity: 0.7;
463
- }
464
-
465
- .input-v2__field--disabled .input-v2__input {
466
- cursor: not-allowed;
467
- }
468
-
469
- .input-v2__label {
470
- font-size: 14px;
471
- line-height: 1.3;
472
- transform: translateY(-50%);
473
- transition:
474
- transform 300ms cubic-bezier(0.22, 1, 0.36, 1),
475
- font-size 300ms cubic-bezier(0.22, 1, 0.36, 1),
476
- color 300ms ease-out;
477
- }
478
-
479
- .input-v2__label--floated {
480
- font-size: 10px;
481
- transform: translateY(-17px);
482
- }
483
-
484
- .input-v2__label--floated[data-size="sm"] {
485
- transform: translateY(-14px);
486
- }
487
-
488
- .input-v2__label--floated[data-size="lg"] {
489
- transform: translateY(-19px);
490
- }
491
-
492
- .input-v2__dropdown-enter-active,
493
- .input-v2__dropdown-leave-active {
494
- transition:
495
- opacity 180ms cubic-bezier(0.22, 1, 0.36, 1),
496
- transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
497
- }
498
-
499
- .input-v2__dropdown-enter-from,
500
- .input-v2__dropdown-leave-to {
501
- opacity: 0;
502
- transform: translateY(-4px);
503
- }
504
-
505
- .input-v2__dropdown-enter-to,
506
- .input-v2__dropdown-leave-from {
507
- opacity: 1;
508
- transform: translateY(0);
509
- }
510
-
511
- .input-v2__list {
512
- max-height: 250px;
513
- overflow-y: auto;
514
- overflow-x: hidden;
515
- scrollbar-width: none;
516
- -ms-overflow-style: none;
517
- }
518
-
519
- .input-v2__list::-webkit-scrollbar {
520
- width: 0;
521
- height: 0;
522
- display: none;
523
- }
524
-
525
- .input-v2__option--active {
526
- background-color: var(--background-color-brand-subtle);
527
- }
528
-
529
- .input-v2__error-enter-active {
530
- transition: transform 0.3s, opacity 0.3s;
531
- }
532
-
533
- .input-v2__error-enter-from {
534
- opacity: 0;
535
- transform: translateY(-4px);
536
- }
537
-
538
- .input-v2__error-leave-active {
539
- transition: transform 0.2s, opacity 0.2s;
540
- }
541
-
542
- .input-v2__error-leave-to {
543
- opacity: 0;
544
- transform: translateY(-4px);
545
- }
546
-
547
- @media (prefers-reduced-motion: reduce) {
548
- .input-v2__label,
549
- .input-v2__dropdown-enter-active,
550
- .input-v2__dropdown-leave-active,
551
- .input-v2__error-enter-active,
552
- .input-v2__error-leave-active {
553
- transition: none;
554
- }
555
- }
556
- </style>
1
+ <script generic="T, K extends keyof T" lang="ts" setup>
2
+ import type { Component } from 'vue'
3
+ import { autoUpdate, flip, size as floatingSize, offset, shift, useFloating } from '@floating-ui/vue'
4
+ import { onClickOutside } from '@vueuse/core'
5
+ import { MaskInput } from 'maska'
6
+
7
+ defineOptions({ inheritAttrs: false })
8
+
9
+ const props = withDefaults(defineProps<Props>(), {
10
+ type: 'text',
11
+ required: false,
12
+ disabled: false,
13
+ readonly: false,
14
+ clearable: false,
15
+ size: 'sm',
16
+ error: undefined,
17
+ placeholder: '',
18
+ autocomplete: 'off',
19
+ loading: false,
20
+ variant: 'default',
21
+ mask: undefined,
22
+ dropdownStrategy: 'absolute',
23
+ autocompleteList: () => [] as T[],
24
+ autocompleteKey: undefined,
25
+ autocompleteOption: undefined,
26
+ autocompleteFn: undefined,
27
+ })
28
+
29
+ const emit = defineEmits<{
30
+ (e: 'clear'): void
31
+ (e: 'focus', ev: FocusEvent): void
32
+ (e: 'blur', ev: FocusEvent): void
33
+ (e: 'keydown', ev: KeyboardEvent): void
34
+ (e: 'enter', value: string | AutocompleteValue): void
35
+ (e: 'selectOption', value: AutocompleteValue): void
36
+ (e: 'updateValue', value: string | number): void
37
+ }>()
38
+
39
+ interface Props {
40
+ label?: string
41
+ type?: 'text' | 'search' | 'email' | 'password' | 'tel' | 'url' | 'number'
42
+ placeholder?: string
43
+ required?: boolean
44
+ disabled?: boolean
45
+ readonly?: boolean
46
+ clearable?: boolean
47
+ size?: 'sm' | 'md' | 'lg'
48
+ error?: string | string[]
49
+ startIcon?: Component
50
+ startIconClass?: string
51
+ endIcon?: Component
52
+ loading?: boolean
53
+ variant?: 'default' | 'gray' | 'blue' | 'canvas'
54
+ mask?: string
55
+ dropdownStrategy?: 'absolute' | 'fixed'
56
+ inputId?: string
57
+ autocomplete?: string
58
+ autocompleteList?: T[]
59
+ autocompleteKey?: K
60
+ autocompleteOption?: K
61
+ autocompleteFn?: (item: T) => string
62
+ }
63
+
64
+ type AutocompleteValue = T | T[keyof T]
65
+
66
+ function customTrim(input: string | number | null | undefined): string | number {
67
+ if (input == null) return ''
68
+ if (typeof input !== 'string') return input
69
+ if (input === ' ' || /\d/.test(input)) return input.trim()
70
+ return input
71
+ }
72
+
73
+ const [modelValue, modelModifiers] = defineModel<string | number | AutocompleteValue>({
74
+ default: '',
75
+ set(value) {
76
+ if (modelModifiers['custom-trim']) return customTrim(value as string)
77
+ return value
78
+ },
79
+ })
80
+
81
+ const inputRef = ref<HTMLInputElement | null>(null)
82
+ const isFocused = ref(false)
83
+
84
+ let maskInput: MaskInput | null = null
85
+ onMounted(() => {
86
+ if (props.mask && inputRef.value) {
87
+ maskInput = new MaskInput(inputRef.value, { mask: props.mask })
88
+ }
89
+ })
90
+ onUnmounted(() => {
91
+ maskInput?.destroy()
92
+ maskInput = null
93
+ })
94
+
95
+ const internalId = useId()
96
+ const inputId = computed(() => props.inputId ?? `input-v2-${internalId}`)
97
+
98
+ const hasValue = computed(() => modelValue.value != null && modelValue.value !== '')
99
+ const hasError = computed(() => Array.isArray(props.error) ? props.error.length > 0 : !!props.error)
100
+ const isLabelFloated = computed(() => hasValue.value || isFocused.value)
101
+
102
+ const hasAutocomplete = computed(() => props.autocompleteList && props.autocompleteList.length > 0)
103
+ const isDropdownOpen = computed(() => hasAutocomplete.value && isFocused.value)
104
+ const currentItemIndex = ref(-1)
105
+
106
+ const fieldRef = ref<HTMLElement | null>(null)
107
+ const floatingRef = ref<HTMLElement | null>(null)
108
+ const { x, y, strategy, update } = useFloating(fieldRef, floatingRef, {
109
+ placement: 'bottom-start',
110
+ strategy: computed(() => props.dropdownStrategy),
111
+ middleware: [
112
+ offset(6),
113
+ flip({ fallbackPlacements: ['top-start'] }),
114
+ shift({ padding: 8 }),
115
+ floatingSize({
116
+ apply({ availableHeight, elements, rects }) {
117
+ const el = elements.floating as HTMLElement
118
+ el.style.width = `${rects.reference.width}px`
119
+ el.style.maxHeight = `${Math.min(availableHeight, 320)}px`
120
+ },
121
+ }),
122
+ ],
123
+ })
124
+
125
+ let cleanup: (() => void) | null = null
126
+ watch(isDropdownOpen, (open) => {
127
+ if (open && fieldRef.value && floatingRef.value) {
128
+ cleanup = autoUpdate(fieldRef.value, floatingRef.value, update)
129
+ }
130
+ else {
131
+ cleanup?.()
132
+ cleanup = null
133
+ currentItemIndex.value = -1
134
+ }
135
+ })
136
+ onUnmounted(() => cleanup?.())
137
+
138
+ const wrapperRef = ref<HTMLDivElement | null>(null)
139
+ onClickOutside(wrapperRef, () => {
140
+ if (isFocused.value) inputRef.value?.blur()
141
+ }, { ignore: [floatingRef] })
142
+
143
+ function resolveSelectedValue(item: T): AutocompleteValue {
144
+ if (props.autocompleteFn) return props.autocompleteFn(item) as AutocompleteValue
145
+ if (props.autocompleteKey) return item[props.autocompleteKey] as AutocompleteValue
146
+ return item
147
+ }
148
+
149
+ function applyCurrentItem() {
150
+ const item = props.autocompleteList[currentItemIndex.value]
151
+ if (item == null) return
152
+ modelValue.value = resolveSelectedValue(item)
153
+ }
154
+
155
+ function onSelectOption(item: T) {
156
+ const value = resolveSelectedValue(item)
157
+ modelValue.value = value
158
+ emit('selectOption', value)
159
+ inputRef.value?.blur()
160
+ }
161
+
162
+ const isTouchScrolling = ref(false)
163
+ function onTouchEndOption(item: T) {
164
+ if (!isTouchScrolling.value) onSelectOption(item)
165
+ isTouchScrolling.value = false
166
+ }
167
+
168
+ function onInput(ev: Event) {
169
+ const value = (ev.target as HTMLInputElement).value
170
+ modelValue.value = value
171
+ emit('updateValue', value)
172
+ }
173
+
174
+ function onFocus(ev: FocusEvent) {
175
+ isFocused.value = true
176
+ emit('focus', ev)
177
+ }
178
+
179
+ function onBlur(ev: FocusEvent) {
180
+ isFocused.value = false
181
+ emit('blur', ev)
182
+ }
183
+
184
+ const autocompleteListEl = ref<HTMLUListElement | null>(null)
185
+ function scrollIntoView(idx: number) {
186
+ const container = autocompleteListEl.value
187
+ if (!container) return
188
+ const item = container.children[idx] as HTMLElement | undefined
189
+ if (!item) return
190
+ const itemTop = item.offsetTop
191
+ const itemBottom = itemTop + item.clientHeight
192
+ if (itemTop < container.scrollTop) container.scrollTop = itemTop
193
+ else if (itemBottom > container.scrollTop + container.clientHeight)
194
+ container.scrollTop = itemBottom - container.clientHeight
195
+ }
196
+
197
+ function onKeydown(ev: KeyboardEvent) {
198
+ emit('keydown', ev)
199
+
200
+ if (hasAutocomplete.value) {
201
+ const last = props.autocompleteList.length - 1
202
+ if (ev.key === 'ArrowDown' && currentItemIndex.value < last) {
203
+ ev.preventDefault()
204
+ currentItemIndex.value++
205
+ applyCurrentItem()
206
+ nextTick(() => scrollIntoView(currentItemIndex.value))
207
+ return
208
+ }
209
+ if (ev.key === 'ArrowUp' && currentItemIndex.value > 0) {
210
+ ev.preventDefault()
211
+ currentItemIndex.value--
212
+ applyCurrentItem()
213
+ nextTick(() => scrollIntoView(currentItemIndex.value))
214
+ return
215
+ }
216
+ if (ev.key === 'Enter') {
217
+ if (currentItemIndex.value !== -1) {
218
+ applyCurrentItem()
219
+ emit('enter', props.autocompleteList[currentItemIndex.value])
220
+ }
221
+ else {
222
+ emit('enter', (ev.target as HTMLInputElement).value)
223
+ }
224
+ inputRef.value?.blur()
225
+ return
226
+ }
227
+ if (ev.key === 'Escape') {
228
+ inputRef.value?.blur()
229
+ return
230
+ }
231
+ }
232
+
233
+ if (ev.key === 'Enter') emit('enter', (ev.target as HTMLInputElement).value)
234
+ }
235
+
236
+ function onClear() {
237
+ modelValue.value = ''
238
+ emit('clear')
239
+ emit('updateValue', '')
240
+ inputRef.value?.focus()
241
+ }
242
+
243
+ function focus() {
244
+ inputRef.value?.focus()
245
+ }
246
+
247
+ function blur() {
248
+ inputRef.value?.blur()
249
+ }
250
+
251
+ function onFieldMousedown(ev: MouseEvent) {
252
+ if (ev.target === inputRef.value) return
253
+ ev.preventDefault()
254
+ inputRef.value?.focus()
255
+ }
256
+
257
+ defineExpose({ focus, blur, inputRef })
258
+
259
+ const layoutClass = computed(() => {
260
+ if (!props.label) {
261
+ if (props.size === 'lg') return 'h-11 py-2'
262
+ return props.size === 'md' ? 'h-10 py-1.5' : 'h-9 py-1.5'
263
+ }
264
+ if (props.size === 'lg') return 'h-11 pt-4 pb-1'
265
+ return props.size === 'md' ? 'h-10 pt-4 pb-0.5' : 'h-9 pt-3 pb-0.5'
266
+ })
267
+
268
+ /* Компенсация асимметричных паддингов поля с label: иконки возвращаются к
269
+ визуальному центру. Величина = (pt + контент/2) - h/2:
270
+ sm 12+11-18=5, md 16+11-20=7, lg 16+12-22=6. */
271
+ const iconOffsetClass = computed(() => {
272
+ if (!props.label) return ''
273
+ if (props.size === 'lg') return '-translate-y-[6px]'
274
+ return props.size === 'md' ? '-translate-y-[7px]' : '-translate-y-[5px]'
275
+ })
276
+
277
+ const showClearButton = computed(() =>
278
+ props.clearable && hasValue.value && !props.disabled && !props.readonly && !props.loading,
279
+ )
280
+
281
+ const errorMessages = computed(() => {
282
+ if (!props.error) return [] as string[]
283
+ return Array.isArray(props.error) ? props.error : [props.error]
284
+ })
285
+
286
+ const attrs = useAttrs()
287
+ const wrapperAttrs = computed(() => ({ class: attrs.class, style: attrs.style }))
288
+ const inputAttrs = computed(() => {
289
+ const { class: _c, style: _s, ...rest } = attrs as Record<string, unknown>
290
+ return rest
291
+ })
292
+
293
+ const fieldClass = computed(() => {
294
+ if (props.disabled) {
295
+ return 'border-default bg-badge/70'
296
+ }
297
+ if (props.variant === 'gray') {
298
+ /* по макету ДС-2026 у залитого поля бордер видимый, а не прозрачный */
299
+ return 'border-default bg-muted'
300
+ }
301
+ if (props.variant === 'blue') {
302
+ return 'border-brand-200 bg-brand-subtle'
303
+ }
304
+ if (props.variant === 'canvas') {
305
+ /* «утопленная» зона ДС + hero-поиск лендинга: bg-canvas глубже bg-muted в дарке */
306
+ return 'border-default bg-canvas'
307
+ }
308
+ return 'border-default bg-main'
309
+ })
310
+
311
+ function rowDisplay(item: T): unknown {
312
+ if (props.autocompleteOption) return item[props.autocompleteOption]
313
+ return item
314
+ }
315
+ </script>
316
+
317
+ <template>
318
+ <div
319
+ ref="wrapperRef"
320
+ class="input-v2 relative w-full text-body-md"
321
+ v-bind="wrapperAttrs"
322
+ >
323
+ <div
324
+ ref="fieldRef"
325
+ class="input-v2__field relative flex w-full cursor-text items-center gap-2 rounded-10 border border-solid text-primary transition-colors duration-200 focus-within:border-focus focus-within:ring-2 focus-within:ring-brand-500/20 hover:border-brand"
326
+ :class="[
327
+ layoutClass,
328
+ fieldClass,
329
+ startIcon ? 'pl-3' : 'pl-4',
330
+ clearable || endIcon || loading || $slots.endButton ? 'pr-3' : 'pr-4',
331
+ { 'input-v2__field--error': hasError, 'input-v2__field--disabled': disabled },
332
+ ]"
333
+ @mousedown="onFieldMousedown"
334
+ >
335
+ <label
336
+ v-if="label"
337
+ :for="inputId"
338
+ class="input-v2__label pointer-events-none absolute top-1/2 max-w-[calc(100%-32px)] truncate text-secondary"
339
+ :data-size="size"
340
+ :class="[
341
+ startIcon ? 'left-9' : 'left-4',
342
+ { 'input-v2__label--floated': isLabelFloated },
343
+ ]"
344
+ >
345
+ {{ label }}<span v-if="required" class="text-danger">&nbsp;*</span>
346
+ </label>
347
+
348
+ <!-- startIconClass ЗАМЕНЯЕТ дефолтные размер+цвет, а не дописывается:
349
+ конфликт одинаковых утилит решается порядком в stylesheet, не в class -->
350
+ <component
351
+ :is="startIcon"
352
+ v-if="startIcon"
353
+ class="shrink-0"
354
+ :class="[startIconClass || 'size-4 text-icon-disabled', iconOffsetClass]"
355
+ />
356
+
357
+ <input
358
+ :id="inputId"
359
+ ref="inputRef"
360
+ :type="type"
361
+ :value="modelValue ?? ''"
362
+ :placeholder="(isLabelFloated || !label) ? placeholder : ''"
363
+ :disabled="disabled"
364
+ :readonly="readonly"
365
+ :autocomplete="autocomplete"
366
+ :tabindex="disabled ? -1 : 0"
367
+ class="input-v2__input w-full min-w-0 bg-transparent leading-tight text-primary outline-hidden placeholder:text-secondary disabled:cursor-not-allowed"
368
+ v-bind="inputAttrs"
369
+ @input="onInput"
370
+ @focus="onFocus"
371
+ @blur="onBlur"
372
+ @keydown="onKeydown"
373
+ >
374
+
375
+ <a-icon-loader-circle
376
+ v-if="loading"
377
+ class="size-4 shrink-0 animate-spin text-icon-disabled"
378
+ :class="iconOffsetClass"
379
+ />
380
+
381
+ <button
382
+ v-else-if="showClearButton"
383
+ type="button"
384
+ tabindex="-1"
385
+ class="flex shrink-0 items-center justify-center rounded-full p-0.5 transition-colors hover:bg-badge"
386
+ :class="iconOffsetClass"
387
+ @mousedown.prevent
388
+ @click.stop="onClear"
389
+ >
390
+ <a-icon-x-mark class="m-0! size-3.5" />
391
+ </button>
392
+
393
+ <component
394
+ :is="endIcon"
395
+ v-else-if="endIcon"
396
+ class="size-4 shrink-0 text-icon-disabled"
397
+ :class="iconOffsetClass"
398
+ />
399
+
400
+ <slot name="endButton" />
401
+ </div>
402
+
403
+ <teleport to="body">
404
+ <transition
405
+ enter-active-class="input-v2__dropdown-enter-active"
406
+ enter-from-class="input-v2__dropdown-enter-from"
407
+ enter-to-class="input-v2__dropdown-enter-to"
408
+ leave-active-class="input-v2__dropdown-leave-active"
409
+ leave-from-class="input-v2__dropdown-leave-from"
410
+ leave-to-class="input-v2__dropdown-leave-to"
411
+ >
412
+ <div
413
+ v-if="isDropdownOpen"
414
+ ref="floatingRef"
415
+ class="input-v2__dropdown z-10001 flex flex-col gap-2 rounded-xl border border-default bg-main p-1.5 shadow-[0_24px_50px_-24px] shadow-brand-500/50"
416
+ :style="{
417
+ position: strategy,
418
+ left: x != null ? `${x}px` : '',
419
+ top: y != null ? `${y}px` : '',
420
+ }"
421
+ >
422
+ <ul ref="autocompleteListEl" class="input-v2__list flex flex-col">
423
+ <li
424
+ v-for="(item, idx) in autocompleteList"
425
+ :key="idx"
426
+ class="cursor-pointer rounded-lg px-3.5 py-2.5 text-body-md transition-colors duration-100 hover:bg-brand-subtle"
427
+ :class="{ 'input-v2__option--active': currentItemIndex === idx }"
428
+ @mousedown.prevent="onSelectOption(item)"
429
+ @touchend.prevent="onTouchEndOption(item)"
430
+ @touchmove="isTouchScrolling = true"
431
+ >
432
+ <slot name="autocomplete-option" :item="item">
433
+ {{ rowDisplay(item) }}
434
+ </slot>
435
+ </li>
436
+ </ul>
437
+
438
+ <slot name="autocomplete-buttons" :value="modelValue" />
439
+ </div>
440
+ </transition>
441
+ </teleport>
442
+
443
+ <transition
444
+ enter-active-class="input-v2__error-enter-active"
445
+ enter-from-class="input-v2__error-enter-from"
446
+ enter-to-class="input-v2__error-enter-to"
447
+ leave-active-class="input-v2__error-leave-active"
448
+ leave-from-class="input-v2__error-leave-from"
449
+ leave-to-class="input-v2__error-leave-to"
450
+ >
451
+ <p v-if="hasError" class="mt-1 flex flex-col text-body-sm text-danger">
452
+ <span v-for="(err, idx) in errorMessages" :key="idx">{{ err }}</span>
453
+ </p>
454
+ </transition>
455
+ </div>
456
+ </template>
457
+
458
+ <style scoped>
459
+ .input-v2__field--error {
460
+ border-color: var(--border-color-danger);
461
+ }
462
+
463
+ .input-v2__field--disabled {
464
+ pointer-events: none;
465
+ cursor: not-allowed;
466
+ opacity: 0.7;
467
+ }
468
+
469
+ .input-v2__field--disabled .input-v2__input {
470
+ cursor: not-allowed;
471
+ }
472
+
473
+ .input-v2__label {
474
+ font-size: 14px;
475
+ line-height: 1.3;
476
+ transform: translateY(-50%);
477
+ transition:
478
+ transform 300ms cubic-bezier(0.22, 1, 0.36, 1),
479
+ font-size 300ms cubic-bezier(0.22, 1, 0.36, 1),
480
+ color 300ms ease-out;
481
+ }
482
+
483
+ .input-v2__label--floated {
484
+ font-size: 10px;
485
+ transform: translateY(-17px);
486
+ }
487
+
488
+ .input-v2__label--floated[data-size="sm"] {
489
+ transform: translateY(-14px);
490
+ }
491
+
492
+ .input-v2__label--floated[data-size="lg"] {
493
+ transform: translateY(-19px);
494
+ }
495
+
496
+ .input-v2__dropdown-enter-active,
497
+ .input-v2__dropdown-leave-active {
498
+ transition:
499
+ opacity 180ms cubic-bezier(0.22, 1, 0.36, 1),
500
+ transform 180ms cubic-bezier(0.22, 1, 0.36, 1);
501
+ }
502
+
503
+ .input-v2__dropdown-enter-from,
504
+ .input-v2__dropdown-leave-to {
505
+ opacity: 0;
506
+ transform: translateY(-4px);
507
+ }
508
+
509
+ .input-v2__dropdown-enter-to,
510
+ .input-v2__dropdown-leave-from {
511
+ opacity: 1;
512
+ transform: translateY(0);
513
+ }
514
+
515
+ .input-v2__list {
516
+ min-height: 0;
517
+ overflow-y: auto;
518
+ overflow-x: hidden;
519
+ scrollbar-width: none;
520
+ -ms-overflow-style: none;
521
+ }
522
+
523
+ .input-v2__list::-webkit-scrollbar {
524
+ width: 0;
525
+ height: 0;
526
+ display: none;
527
+ }
528
+
529
+ .input-v2__option--active {
530
+ background-color: var(--background-color-brand-subtle);
531
+ }
532
+
533
+ .input-v2__error-enter-active {
534
+ transition: transform 0.3s, opacity 0.3s;
535
+ }
536
+
537
+ .input-v2__error-enter-from {
538
+ opacity: 0;
539
+ transform: translateY(-4px);
540
+ }
541
+
542
+ .input-v2__error-leave-active {
543
+ transition: transform 0.2s, opacity 0.2s;
544
+ }
545
+
546
+ .input-v2__error-leave-to {
547
+ opacity: 0;
548
+ transform: translateY(-4px);
549
+ }
550
+
551
+ @media (prefers-reduced-motion: reduce) {
552
+ .input-v2__label,
553
+ .input-v2__dropdown-enter-active,
554
+ .input-v2__dropdown-leave-active,
555
+ .input-v2__error-enter-active,
556
+ .input-v2__error-leave-active {
557
+ transition: none;
558
+ }
559
+ }
560
+ </style>