adata-ui 2.1.40-beta.2 → 2.1.40-beta.20

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 (527) hide show
  1. package/-error.vue +170 -170
  2. package/.editorconfig +12 -12
  3. package/.gitlab-ci.yml +16 -16
  4. package/.nuxtrc +1 -1
  5. package/.nvmrc +1 -1
  6. package/.playground/app.config.ts +5 -5
  7. package/.playground/app.vue +102 -102
  8. package/.playground/nuxt.config.ts +3 -3
  9. package/README.md +75 -75
  10. package/app.config.ts +28 -28
  11. package/app.vue +8 -8
  12. package/assets/styles/index.scss +166 -104
  13. package/assets/styles/modules/_base.scss +5 -5
  14. package/assets/styles/modules/_typography.scss +152 -152
  15. package/components/elements/README.md +1 -1
  16. package/components/elements/a-select-row/ASelectRowV2.vue +214 -213
  17. package/components/elements/a-select-row/index.vue +138 -140
  18. package/components/elements/accordion/AAccordion.vue +199 -197
  19. package/components/elements/accordion/AAccordionGroup/AAccordionGroup.vue +145 -145
  20. package/components/elements/accordion/AAccordionGroup/types.ts +20 -20
  21. package/components/elements/accordion/AAccordionGroup/ui.config.ts +22 -22
  22. package/components/elements/accordion/types.ts +14 -14
  23. package/components/elements/accordion/ui.config.ts +22 -22
  24. package/components/elements/alert/AAlert.vue +52 -65
  25. package/components/elements/banner/ABanner.vue +49 -49
  26. package/components/elements/bg-block/ABgBlock.vue +11 -11
  27. package/components/elements/button/AButton.vue +188 -188
  28. package/components/elements/button/AButtonV2.vue +133 -89
  29. package/components/elements/button/types.ts +24 -24
  30. package/components/elements/button-login/index.vue +18 -18
  31. package/components/elements/calendar/ACalendar.vue +83 -83
  32. package/components/elements/carousel/ACarousel.vue +159 -159
  33. package/components/elements/carousel/config.ui.ts +15 -15
  34. package/components/elements/carousel/types.ts +18 -18
  35. package/components/elements/chip-wrapper/AChipWrapper.vue +80 -80
  36. package/components/elements/chip-wrapper/types.ts +12 -12
  37. package/components/elements/chips/AChips.vue +32 -32
  38. package/components/elements/companies/AOurClients.vue +47 -45
  39. package/components/elements/curve-block/ACurveBlock.vue +292 -292
  40. package/components/elements/curve-block-v2/ACurveBlockV2.vue +85 -0
  41. package/components/elements/digit-badge/ADigitBadge.vue +85 -83
  42. package/components/elements/error-template/index.vue +48 -48
  43. package/components/elements/error-template/types.ts +23 -23
  44. package/components/elements/feature-description/AFeatureDescription.vue +112 -112
  45. package/components/elements/illustrations/noAccess.vue +71 -71
  46. package/components/elements/infinite-carousel/AInfiniteCarousel.vue +57 -57
  47. package/components/elements/infinite-scroll/AInfiniteScroll.vue +33 -33
  48. package/components/elements/leave-note/ALeaveNote.vue +102 -78
  49. package/components/elements/pagination/APagination.vue +110 -110
  50. package/components/elements/photos-animation/APhotosAnimation.vue +85 -85
  51. package/components/elements/segmented/ASegmentedV2.vue +151 -58
  52. package/components/elements/select/ASelect.vue +447 -447
  53. package/components/elements/select/ASelectMobile.vue +186 -186
  54. package/components/elements/select/ASelectV2.vue +588 -581
  55. package/components/elements/select/ui/select-button.vue +112 -112
  56. package/components/elements/select/ui/select-list.vue +99 -99
  57. package/components/elements/show-more/AShowMore.vue +35 -35
  58. package/components/elements/show-more/AShowMoreV2.vue +26 -26
  59. package/components/elements/skeleton/ASkeleton.vue +44 -44
  60. package/components/elements/slider/ASlider.vue +174 -0
  61. package/components/elements/slider/ASliderItem.vue +31 -0
  62. package/components/elements/slider/types.ts +21 -0
  63. package/components/elements/star-rating/AStarRating.vue +189 -181
  64. package/components/elements/star-rating/types.ts +7 -7
  65. package/components/elements/star-rating/ui/Star.vue +74 -74
  66. package/components/elements/status-badge/AStatusBadge.vue +121 -74
  67. package/components/elements/step-slider/AStepSliderColCard.vue +232 -0
  68. package/components/elements/step-slider/AStepSliderColNumber.vue +178 -0
  69. package/components/elements/table/ATable.vue +93 -93
  70. package/components/elements/table/table.config.ts +32 -32
  71. package/components/elements/tree-select/ATreeSelect.vue +157 -173
  72. package/components/elements/tree-select/TreeService.ts +184 -249
  73. package/components/elements/tree-select/components/ATreeSelectNode.vue +104 -0
  74. package/components/elements/tree-select/types.ts +63 -34
  75. package/components/elements/tree-select/utils.ts +30 -0
  76. package/components/elements/tree-select-mobile/ATreeSelectMobile.vue +79 -79
  77. package/components/elements/tree-select-mobile/components/ATreeSelectNodesMobile.vue +175 -175
  78. package/components/elements/tree-select-modal/ATreeSelectModal.vue +217 -0
  79. package/components/features/color-mode/AColorMode.client.vue +96 -96
  80. package/components/features/dropdown/ADropdown.vue +127 -127
  81. package/components/features/dropdown/ADropdownV2.vue +140 -141
  82. package/components/features/go-top/GoTop.vue +63 -63
  83. package/components/features/lang-switcher/lang-switcher.vue +158 -153
  84. package/components/features/payment/banner/BasicPlusLimitBanner.vue +104 -104
  85. package/components/features/payment/banner/PaymentBanner.vue +108 -108
  86. package/components/features/payment/banner/UpSellBanner.vue +125 -125
  87. package/components/features/payment/process/PaymentKaspiQrSidePanel.vue +179 -161
  88. package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +123 -112
  89. package/components/features/payment/process/PaymentMethodSidePanel.vue +138 -121
  90. package/components/features/payment/process/PaymentProcess.vue +158 -158
  91. package/components/features/payment/process/PaymentTopUpSidePanel.vue +119 -133
  92. package/components/features/pk-mobile-services/APkMobileServices.vue +136 -136
  93. package/components/features//321/201hange-version/AChangeVersion.vue +20 -20
  94. package/components/forms/README.md +1 -1
  95. package/components/forms/checkbox/ACheckbox.vue +149 -149
  96. package/components/forms/checkbox/ACheckboxV2.vue +229 -229
  97. package/components/forms/feedback-form/FeedbackForm.vue +177 -177
  98. package/components/forms/feedback-form/FeedbackFormV2.vue +182 -0
  99. package/components/forms/input/AInputV2.vue +542 -542
  100. package/components/forms/input/button/AInputButton.vue +53 -53
  101. package/components/forms/input/date/AInputDate.vue +213 -213
  102. package/components/forms/input/password/AInputPassword.vue +59 -53
  103. package/components/forms/input/standard/AInputStandard.vue +415 -415
  104. package/components/forms/input/textarea/ATextarea.vue +203 -203
  105. package/components/forms/input/textarea/ATextareaV2.vue +248 -0
  106. package/components/forms/radio-button/ARadioButton.vue +95 -95
  107. package/components/forms/request-demo/ARequestDemo.vue +108 -108
  108. package/components/forms/toggle/AToggle.vue +156 -157
  109. package/components/forms/toggle/AToggleV2.vue +71 -71
  110. package/components/forms/toggle/types.ts +16 -16
  111. package/components/modals/AConfirmationEmail.vue +40 -40
  112. package/components/modals/AboutTariffModal.vue +114 -114
  113. package/components/modals/AnotherDeviceModal.vue +64 -60
  114. package/components/modals/ApplicationAcceptedModal.vue +44 -47
  115. package/components/modals/ConnectingTariffModal.vue +60 -67
  116. package/components/modals/ContactsMobileModel.vue +182 -182
  117. package/components/modals/ContentNavigationModal.vue +99 -355
  118. package/components/modals/Insufficient-funds-modal.vue +62 -77
  119. package/components/modals/LimitReachedModal.vue +33 -30
  120. package/components/modals/NoAccessModal.vue +35 -37
  121. package/components/modals/PaymentMethodModal.vue +101 -101
  122. package/components/modals/RedirectsBanksModal.vue +69 -69
  123. package/components/modals/ReplenishModal.vue +132 -132
  124. package/components/modals/ReportBugConfirmModal.vue +32 -30
  125. package/components/modals/ReportBugModal.vue +172 -164
  126. package/components/modals/Resend.vue +81 -82
  127. package/components/modals/SubmitApplicationModal.vue +126 -123
  128. package/components/modals/id/AuthModal.vue +92 -92
  129. package/components/modals/id/IdAutoLogoutModal.vue +45 -45
  130. package/components/modals/id/IdBanner.vue +61 -61
  131. package/components/modals/id/IdConfirmAccountOtpModal.vue +181 -186
  132. package/components/modals/id/IdConfirmSuccessfulModal.vue +41 -41
  133. package/components/modals/id/IdLoginModal.vue +336 -341
  134. package/components/modals/id/IdModals.vue +127 -114
  135. package/components/modals/id/IdNewPasswordModal.vue +129 -129
  136. package/components/modals/id/IdOtpInput.vue +156 -156
  137. package/components/modals/id/IdPasswordSuccessfulModal.vue +25 -25
  138. package/components/modals/id/IdRecoveryModal.vue +117 -117
  139. package/components/modals/id/IdRegistrationModal.vue +215 -215
  140. package/components/modals/id/IdResetPasswordOtpModal.vue +158 -158
  141. package/components/modals/id/IdTwoFactorModal.vue +125 -130
  142. package/components/navigation/README.md +1 -1
  143. package/components/navigation/bottom-navigation/ABottomNavigation.vue +106 -106
  144. package/components/navigation/breadcrumbs/ABreadcrumbs.vue +128 -67
  145. package/components/navigation/breadcrumbs/types.ts +8 -5
  146. package/components/navigation/footer/AFooter.vue +283 -284
  147. package/components/navigation/footer/ui/a-footer-accordion.vue +118 -118
  148. package/components/navigation/header/AHeader.vue +237 -243
  149. package/components/navigation/header/AlmatyContacts.vue +145 -145
  150. package/components/navigation/header/AstanaContacts.vue +67 -67
  151. package/components/navigation/header/CardGallery.vue +251 -251
  152. package/components/navigation/header/ContactMenu.vue +48 -48
  153. package/components/navigation/header/HeaderLink.vue +250 -260
  154. package/components/navigation/header/HeaderUsage.vue +139 -125
  155. package/components/navigation/header/ListItem.vue +31 -31
  156. package/components/navigation/header/NavCard.vue +41 -41
  157. package/components/navigation/header/NavList.vue +72 -96
  158. package/components/navigation/header/NotificationsMenu.vue +50 -51
  159. package/components/navigation/header/ProductMenu.vue +100 -98
  160. package/components/navigation/header/ProfileMenu.vue +167 -176
  161. package/components/navigation/header/SystemNotification.vue +108 -110
  162. package/components/navigation/header/types.ts +28 -28
  163. package/components/navigation/line-tabs/ALineTabs.vue +111 -111
  164. package/components/navigation/line-tabs/types.ts +21 -21
  165. package/components/navigation/mobile-navigation/AMobileNavigation.vue +112 -112
  166. package/components/navigation/pill-tabs/APillTabs.vue +177 -177
  167. package/components/navigation/pill-tabs/APillTabsV2.vue +226 -118
  168. package/components/navigation/pill-tabs/types.ts +24 -24
  169. package/components/navigation/side-navigation/ASideNavigation.vue +406 -406
  170. package/components/overlays/README.md +1 -1
  171. package/components/overlays/drawer/ADrawerV2.vue +335 -0
  172. package/components/overlays/modal/AModal.vue +335 -335
  173. package/components/overlays/modal/AModalV2.vue +397 -388
  174. package/components/overlays/side-panel/ASidePanel.vue +445 -439
  175. package/components/overlays/sideover/ASlideover.vue +351 -351
  176. package/components/overlays/tooltip/ATooltip.vue +237 -237
  177. package/components/overlays/tooltip/ATooltipV2.vue +226 -233
  178. package/components/overlays/tooltip/types.ts +26 -26
  179. package/components/overlays/tooltip/useTooltipTrigger.ts +101 -101
  180. package/components/transitions/ATransitionHeight.vue +67 -67
  181. package/components/utils/index.ts +19 -19
  182. package/components/utils/removeTrailingSlash.ts +7 -7
  183. package/composables/highlight.ts +18 -18
  184. package/composables/modalsState.ts +8 -8
  185. package/composables/projectState.ts +2 -2
  186. package/composables/useAccessTokenCookie.ts +25 -0
  187. package/composables/useActiveNavigation.ts +84 -84
  188. package/composables/useAdaptive.ts +46 -46
  189. package/composables/useBuyTariffs.ts +91 -91
  190. package/composables/useCarouselScroll.ts +49 -49
  191. package/composables/useChipOverflow.ts +92 -82
  192. package/composables/useHeaderNavigationLinks.ts +519 -477
  193. package/composables/useIdModals.ts +36 -36
  194. package/composables/usePayment.ts +78 -78
  195. package/composables/useUIValidation.ts +16 -16
  196. package/composables/useUrls.ts +23 -23
  197. package/eslint.config.mjs +45 -45
  198. package/i18n/i18n.config.ts +21 -21
  199. package/i18n.config.ts +21 -21
  200. package/icons/adata-logo.vue +17 -17
  201. package/icons/ai-assistant.vue +13 -13
  202. package/icons/akimat.vue +20 -20
  203. package/icons/archive.vue +16 -0
  204. package/icons/arrow/arrow-bottom-left-on-square.vue +5 -5
  205. package/icons/arrow/arrow-circle-down.vue +16 -16
  206. package/icons/arrow/arrow-circle-reset.vue +16 -16
  207. package/icons/arrow/arrow-corner.vue +9 -9
  208. package/icons/arrow/arrow-graph-down.vue +13 -13
  209. package/icons/arrow/arrow-graph-up.vue +14 -14
  210. package/icons/arrow/arrow-left-right.vue +8 -0
  211. package/icons/arrow/arrow-right.vue +14 -0
  212. package/icons/arrow/arrow-square-down.vue +15 -15
  213. package/icons/arrow/arrow-top-right-on-square.vue +6 -6
  214. package/icons/arrow/arrows-in.vue +11 -0
  215. package/icons/arrow/arrows-out.vue +11 -0
  216. package/icons/arrow/arrows-resize-in.vue +38 -0
  217. package/icons/arrow/arrows-resize-out.vue +17 -0
  218. package/icons/arrow-currency-gray.vue +5 -5
  219. package/icons/arrow-currency-green.vue +5 -5
  220. package/icons/arrow-currency-red.vue +5 -5
  221. package/icons/arrow-side-up.vue +6 -6
  222. package/icons/avatar.vue +12 -12
  223. package/icons/bank.vue +5 -5
  224. package/icons/bill.vue +3 -0
  225. package/icons/block.vue +16 -16
  226. package/icons/bookmark/bookmark-filled.vue +18 -18
  227. package/icons/bookmark/bookmark.vue +5 -5
  228. package/icons/bot.vue +52 -0
  229. package/icons/briefcase.vue +14 -0
  230. package/icons/browsers/browser-duck.vue +65 -65
  231. package/icons/browsers/browser-google.vue +30 -30
  232. package/icons/browsers/browser-yandex.vue +13 -13
  233. package/icons/building-vector.vue +7 -7
  234. package/icons/building.vue +9 -0
  235. package/icons/calculator.vue +7 -7
  236. package/icons/calendar.vue +24 -24
  237. package/icons/car-side.vue +16 -0
  238. package/icons/car.vue +7 -7
  239. package/icons/cart.vue +15 -0
  240. package/icons/chart-bar.vue +5 -5
  241. package/icons/chart-no-axes-column.vue +3 -0
  242. package/icons/chart-pie.vue +16 -16
  243. package/icons/check/check-circle.vue +6 -6
  244. package/icons/check/check-shield.vue +14 -14
  245. package/icons/check/check.vue +17 -17
  246. package/icons/check/checkmark-circle.vue +6 -6
  247. package/icons/check-sb.vue +7 -7
  248. package/icons/checkbox/checkbox-active.vue +17 -17
  249. package/icons/checkbox/checkbox-empty.vue +10 -10
  250. package/icons/checkbox/checkbox-intermediate.vue +7 -7
  251. package/icons/chevron/chevron-down.vue +5 -5
  252. package/icons/chevron/chevron-left.vue +5 -5
  253. package/icons/chevron/chevron-right.vue +5 -5
  254. package/icons/chevron/chevron-up.vue +5 -5
  255. package/icons/chevron/double-chevron-right.vue +12 -12
  256. package/icons/clipboard-text.vue +7 -7
  257. package/icons/clock-time.vue +24 -0
  258. package/icons/clock.vue +25 -25
  259. package/icons/close.vue +16 -16
  260. package/icons/cloud/cloud-upload.vue +3 -0
  261. package/icons/code.vue +9 -0
  262. package/icons/company/company-bazis.vue +21 -21
  263. package/icons/company/company-bereke.vue +25 -25
  264. package/icons/company/company-bigroup.vue +16 -16
  265. package/icons/company/company-erg.vue +17 -17
  266. package/icons/company/company-forte.vue +21 -21
  267. package/icons/company/company-halyk.vue +15 -15
  268. package/icons/company/company-jusan.vue +31 -31
  269. package/icons/company/company-kaspi.vue +14 -14
  270. package/icons/company/company-kazzinc.vue +22 -22
  271. package/icons/company/company-mycar.vue +13 -13
  272. package/icons/company/company-samruk.vue +21 -21
  273. package/icons/company-egov-small.vue +15 -15
  274. package/icons/company.vue +8 -8
  275. package/icons/compare-outlined.vue +8 -0
  276. package/icons/copy.vue +7 -7
  277. package/icons/currency/currency-dollar.vue +16 -16
  278. package/icons/currency/currency-down.vue +22 -22
  279. package/icons/currency/currency-eur.vue +57 -57
  280. package/icons/currency/currency-rub.vue +7 -7
  281. package/icons/currency/currency-tenge.vue +9 -9
  282. package/icons/currency/currency-usd.vue +221 -221
  283. package/icons/currency/currency-yuan.vue +25 -25
  284. package/icons/default-avatar.vue +20 -20
  285. package/icons/delete.vue +7 -7
  286. package/icons/document.vue +5 -5
  287. package/icons/dots-horizontal.vue +31 -0
  288. package/icons/download-line.vue +13 -0
  289. package/icons/download-rounded.vue +15 -0
  290. package/icons/download.vue +11 -11
  291. package/icons/edit.vue +7 -7
  292. package/icons/education.vue +16 -16
  293. package/icons/egov-small.vue +7 -7
  294. package/icons/egov.vue +41 -0
  295. package/icons/elicense.vue +38 -0
  296. package/icons/excel-icon.vue +14 -14
  297. package/icons/expand-window.vue +7 -7
  298. package/icons/eye-closed.vue +26 -26
  299. package/icons/eye-open.vue +7 -7
  300. package/icons/eye-opened.vue +23 -23
  301. package/icons/favourite-bookmark-outline.vue +17 -0
  302. package/icons/file/file-check-corner.vue +3 -0
  303. package/icons/file/file-spreadsheet.vue +3 -0
  304. package/icons/file/file.vue +16 -16
  305. package/icons/file/files.vue +16 -16
  306. package/icons/file-pen.vue +31 -0
  307. package/icons/file-stack.vue +31 -0
  308. package/icons/file-types/excel.vue +131 -0
  309. package/icons/file-types/html.vue +13 -0
  310. package/icons/file-types/jpeg.vue +16 -0
  311. package/icons/file-types/pdf.vue +11 -0
  312. package/icons/file-types/png.vue +11 -0
  313. package/icons/file-types/rar.vue +11 -0
  314. package/icons/file-types/word.vue +50 -0
  315. package/icons/file-types/zip.vue +11 -0
  316. package/icons/filter.vue +5 -5
  317. package/icons/flag.vue +7 -7
  318. package/icons/gauge.vue +17 -17
  319. package/icons/gift.vue +8 -8
  320. package/icons/git-compare.vue +38 -0
  321. package/icons/git-fork.vue +3 -0
  322. package/icons/globe.vue +16 -16
  323. package/icons/google.vue +41 -41
  324. package/icons/gradiend-icons/files-gradient.vue +22 -22
  325. package/icons/gradiend-icons/hammer-gradient.vue +62 -62
  326. package/icons/graduation-cap.vue +31 -0
  327. package/icons/graph-loading.vue +34 -0
  328. package/icons/graph-pie.vue +14 -14
  329. package/icons/hand/hand-thumb-up-filled.vue +5 -5
  330. package/icons/hand/hand-thumb-up.vue +5 -5
  331. package/icons/hand-with-phone/hand-with-phone-dark.vue +52 -52
  332. package/icons/hand-with-phone/hand-with-phone-light.vue +52 -52
  333. package/icons/handshake.vue +5 -5
  334. package/icons/hcheck.vue +14 -14
  335. package/icons/hdocument.vue +7 -7
  336. package/icons/headset.vue +6 -0
  337. package/icons/help-question-circle.vue +26 -0
  338. package/icons/history.vue +5 -5
  339. package/icons/horizontal-more.vue +13 -13
  340. package/icons/hot-line.vue +6 -6
  341. package/icons/hummer.vue +16 -16
  342. package/icons/id.vue +7 -7
  343. package/icons/in-row-type.vue +46 -0
  344. package/icons/in-table-type.vue +29 -0
  345. package/icons/info/info-circle.vue +29 -29
  346. package/icons/invoice.vue +7 -7
  347. package/icons/judge.vue +14 -14
  348. package/icons/kaspi-qr.vue +13 -13
  349. package/icons/landmark.vue +3 -0
  350. package/icons/layers.vue +3 -0
  351. package/icons/letter/letter-a.vue +6 -0
  352. package/icons/letter/letter-b.vue +6 -0
  353. package/icons/letter/letter-c.vue +6 -0
  354. package/icons/letter/letter-d.vue +6 -0
  355. package/icons/letter/letter-e.vue +6 -0
  356. package/icons/link-chain.vue +28 -28
  357. package/icons/link-url.vue +3 -0
  358. package/icons/link.vue +7 -7
  359. package/icons/linkedin.vue +24 -24
  360. package/icons/list-tree.vue +3 -0
  361. package/icons/loader-circle.vue +27 -27
  362. package/icons/location.vue +8 -8
  363. package/icons/lock.vue +5 -5
  364. package/icons/logo.vue +15 -15
  365. package/icons/logout.vue +16 -16
  366. package/icons/magnify/magnifying-glass-minus.vue +8 -8
  367. package/icons/magnify/magnifying-glass-plus.vue +9 -9
  368. package/icons/magnify/magnifying-glass.vue +9 -9
  369. package/icons/mail-question-mark.vue +8 -0
  370. package/icons/mail.vue +7 -7
  371. package/icons/mailru.vue +34 -34
  372. package/icons/main-filter.vue +5 -5
  373. package/icons/map/map-pin-filled.vue +7 -7
  374. package/icons/map/map-pin-rect.vue +15 -15
  375. package/icons/map/map-pin.vue +7 -7
  376. package/icons/map-marker-start.vue +7 -7
  377. package/icons/map-paper.vue +5 -5
  378. package/icons/medal.vue +7 -7
  379. package/icons/menu-filled.vue +20 -20
  380. package/icons/menu.vue +8 -8
  381. package/icons/message/message-square.vue +3 -0
  382. package/icons/message/message.vue +12 -12
  383. package/icons/message-dotted.vue +30 -0
  384. package/icons/minus/minus-circle.vue +10 -10
  385. package/icons/money.vue +5 -5
  386. package/icons/monitoring.vue +10 -10
  387. package/icons/moon.vue +8 -8
  388. package/icons/more.vue +29 -29
  389. package/icons/network.vue +3 -0
  390. package/icons/newspaper.vue +3 -0
  391. package/icons/note-pencil.vue +14 -14
  392. package/icons/note.vue +14 -14
  393. package/icons/notification-error.vue +17 -0
  394. package/icons/notification.vue +16 -16
  395. package/icons/paperclip.vue +7 -7
  396. package/icons/payment/payment-card.vue +6 -6
  397. package/icons/payment/payment-kaspi.vue +11 -11
  398. package/icons/person-vector.vue +7 -7
  399. package/icons/person-with-briefcase.vue +10 -10
  400. package/icons/person.vue +22 -0
  401. package/icons/phone-call.vue +6 -0
  402. package/icons/phone-filled.vue +5 -5
  403. package/icons/phone-reversed.vue +3 -0
  404. package/icons/phone.vue +7 -7
  405. package/icons/plus/plus-circle.vue +8 -8
  406. package/icons/plus/plus.vue +13 -13
  407. package/icons/profile.vue +5 -5
  408. package/icons/radio/radio-check.vue +8 -8
  409. package/icons/radio/radio-empty.vue +10 -10
  410. package/icons/receipt.vue +8 -8
  411. package/icons/robot.vue +14 -14
  412. package/icons/route.vue +7 -0
  413. package/icons/sanctions.vue +8 -8
  414. package/icons/scales/scale.vue +16 -16
  415. package/icons/scales/scales.vue +5 -5
  416. package/icons/scales/standing-scales.vue +15 -15
  417. package/icons/scan-search.vue +3 -0
  418. package/icons/search.vue +27 -27
  419. package/icons/settings.vue +26 -0
  420. package/icons/share/share-alt.vue +5 -5
  421. package/icons/share/share.vue +14 -14
  422. package/icons/shield-alert.vue +31 -0
  423. package/icons/shield-verified.vue +14 -0
  424. package/icons/ship.vue +8 -8
  425. package/icons/socials/face-book.vue +15 -15
  426. package/icons/socials/facebook-color.vue +16 -0
  427. package/icons/socials/globe-color.vue +16 -0
  428. package/icons/socials/instagram-color.vue +46 -0
  429. package/icons/socials/instagram.vue +19 -19
  430. package/icons/socials/linkedin-color.vue +14 -0
  431. package/icons/socials/telegram-color.vue +16 -0
  432. package/icons/socials/telegram.vue +15 -15
  433. package/icons/socials/tik-tok.vue +15 -15
  434. package/icons/socials/tiktok-color.vue +14 -0
  435. package/icons/socials/vk-color.vue +16 -0
  436. package/icons/socials/youtube-color.vue +16 -0
  437. package/icons/socials/youtube.vue +16 -16
  438. package/icons/sort/sort-asc.vue +15 -15
  439. package/icons/sort/sort-desc.vue +15 -15
  440. package/icons/sparkles.vue +3 -0
  441. package/icons/splitting-arrows.vue +8 -8
  442. package/icons/star/star-filled.vue +40 -40
  443. package/icons/star/star-half-filled.vue +20 -20
  444. package/icons/star/star.vue +25 -25
  445. package/icons/sun.vue +24 -24
  446. package/icons/sviazi.vue +5 -5
  447. package/icons/tag.vue +30 -30
  448. package/icons/tasks.vue +10 -10
  449. package/icons/tender-search.vue +11 -11
  450. package/icons/toasts/check-circle-toast.vue +6 -6
  451. package/icons/toasts/warning-triangle-toast.vue +7 -7
  452. package/icons/trash.vue +13 -13
  453. package/icons/triangle.vue +10 -10
  454. package/icons/truck.vue +7 -7
  455. package/icons/user-square.vue +14 -14
  456. package/icons/user.vue +22 -22
  457. package/icons/users-focus.vue +8 -8
  458. package/icons/users-round.vue +3 -0
  459. package/icons/users-three.vue +7 -7
  460. package/icons/users.vue +8 -8
  461. package/icons/view-eye-open.vue +13 -0
  462. package/icons/warning/warning-circle.vue +29 -29
  463. package/icons/warning/warning-triangle-filled.vue +12 -12
  464. package/icons/warning/warning-triangle.vue +28 -28
  465. package/icons/whatsapp.vue +8 -8
  466. package/icons/work-bag.vue +11 -11
  467. package/icons/work-case.vue +9 -9
  468. package/icons/work-search.vue +10 -10
  469. package/icons/work.vue +5 -5
  470. package/icons/workflow.vue +3 -0
  471. package/icons/x-mark.vue +15 -15
  472. package/icons/yandex.vue +28 -28
  473. package/icons/zap.vue +3 -0
  474. package/illustrations/address-location.vue +38 -38
  475. package/illustrations/ball-with-chain.vue +120 -120
  476. package/illustrations/bill.vue +133 -133
  477. package/illustrations/buildings.vue +82 -82
  478. package/illustrations/calendar.vue +156 -156
  479. package/illustrations/chains.vue +152 -152
  480. package/illustrations/coin-percent.vue +126 -126
  481. package/illustrations/coins-stack.vue +202 -202
  482. package/illustrations/delete-dark.vue +31 -31
  483. package/illustrations/delete.vue +32 -32
  484. package/illustrations/doc-with-stamp.vue +126 -126
  485. package/illustrations/document.vue +64 -64
  486. package/illustrations/door.vue +74 -74
  487. package/illustrations/empty-box.vue +77 -77
  488. package/illustrations/empty-wallet.vue +161 -161
  489. package/illustrations/graph-in-coin.vue +119 -119
  490. package/illustrations/hammer.vue +156 -156
  491. package/illustrations/hand-cash.vue +108 -108
  492. package/illustrations/info.vue +39 -39
  493. package/illustrations/mail.vue +68 -68
  494. package/illustrations/ok.vue +62 -62
  495. package/illustrations/people-group.vue +237 -237
  496. package/illustrations/person-with-phone.vue +187 -187
  497. package/illustrations/person.vue +159 -159
  498. package/illustrations/phone-check.vue +90 -90
  499. package/illustrations/phone-payment-method.vue +223 -223
  500. package/illustrations/search.vue +14 -14
  501. package/illustrations/stop-hand.vue +77 -77
  502. package/illustrations/stop-sign.vue +34 -34
  503. package/illustrations/suit.vue +111 -111
  504. package/illustrations/suitcase.vue +105 -105
  505. package/illustrations/terminal-dark.vue +48 -48
  506. package/illustrations/terminal.vue +234 -234
  507. package/illustrations/trash-can.vue +108 -108
  508. package/illustrations/turn-on-tariff.vue +38 -38
  509. package/illustrations/two-persons.vue +169 -169
  510. package/lang/en.ts +881 -843
  511. package/lang/kk.ts +883 -845
  512. package/lang/ru.ts +884 -846
  513. package/layouts/default.vue +13 -13
  514. package/nuxt.config.ts +88 -88
  515. package/package.json +69 -69
  516. package/plugins/maska.ts +4 -4
  517. package/plugins/toast.client.ts +41 -58
  518. package/public/kaspi/logo.svg +4 -4
  519. package/shared/constans/pages.ts +96 -90
  520. package/tailwind.config.ts +194 -163
  521. package/tests/AButton.test.ts +81 -81
  522. package/tsconfig.json +6 -6
  523. package/utils/getMaxZindex.ts +25 -25
  524. package/utils/localizedNavigation.ts +36 -36
  525. package/utils/twMerge.ts +34 -0
  526. package/vitest.config.ts +14 -14
  527. package/components/elements/tree-select/components/tree-select-nodes.vue +0 -91
@@ -1,542 +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'
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-3 pb-1' : 'h-9 pt-2 pb-1'
261
- })
262
-
263
- const iconOffsetClass = computed(() => {
264
- if (!props.label) return ''
265
- return props.size === 'md' ? '-translate-y-[4px]' : '-translate-y-[2px]'
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-sm"
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 pl-4 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' : '',
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 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 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-xl 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-lg px-3 py-2 text-sm 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-xs 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>
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>