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

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