adata-ui 2.1.41-beta.1 → 2.1.41

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