adata-ui 2.1.40-beta.11 → 2.1.40-beta.13

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 (133) hide show
  1. package/assets/styles/index.scss +67 -5
  2. package/components/elements/a-select-row/ASelectRowV2.vue +8 -8
  3. package/components/elements/a-select-row/index.vue +3 -5
  4. package/components/elements/accordion/AAccordion.vue +5 -3
  5. package/components/elements/accordion/AAccordionGroup/AAccordionGroup.vue +3 -3
  6. package/components/elements/alert/AAlert.vue +34 -47
  7. package/components/elements/bg-block/ABgBlock.vue +1 -1
  8. package/components/elements/button/AButton.vue +3 -3
  9. package/components/elements/button/AButtonV2.vue +7 -5
  10. package/components/elements/button-login/index.vue +1 -1
  11. package/components/elements/chip-wrapper/AChipWrapper.vue +1 -1
  12. package/components/elements/chips/AChips.vue +2 -2
  13. package/components/elements/digit-badge/ADigitBadge.vue +21 -19
  14. package/components/elements/feature-description/AFeatureDescription.vue +1 -1
  15. package/components/elements/leave-note/ALeaveNote.vue +1 -1
  16. package/components/elements/pagination/APagination.vue +7 -7
  17. package/components/elements/segmented/ASegmentedV2.vue +103 -10
  18. package/components/elements/select/ASelect.vue +4 -4
  19. package/components/elements/select/ASelectMobile.vue +2 -2
  20. package/components/elements/select/ASelectV2.vue +10 -11
  21. package/components/elements/select/ui/select-button.vue +1 -1
  22. package/components/elements/status-badge/AStatusBadge.vue +22 -19
  23. package/components/elements/status-badge-v2/StatusBadgeV2.vue +1 -3
  24. package/components/elements/table/ATable.vue +1 -1
  25. package/components/elements/table/table.config.ts +2 -2
  26. package/components/elements/tree-select/ATreeSelect.vue +97 -113
  27. package/components/elements/tree-select/TreeService.ts +142 -207
  28. package/components/elements/tree-select/components/ATreeSelectNode.vue +104 -0
  29. package/components/elements/tree-select/types.ts +40 -11
  30. package/components/elements/tree-select/utils.ts +30 -0
  31. package/components/elements/tree-select-modal/ATreeSelectModal.vue +217 -0
  32. package/components/features/color-mode/AColorMode.client.vue +1 -1
  33. package/components/features/dropdown/ADropdownV2.vue +1 -2
  34. package/components/features/lang-switcher/lang-switcher.vue +9 -4
  35. package/components/features/payment/banner/BasicPlusLimitBanner.vue +3 -3
  36. package/components/features/payment/banner/PaymentBanner.vue +3 -3
  37. package/components/features/payment/banner/UpSellBanner.vue +3 -3
  38. package/components/features/payment/process/PaymentKaspiQrSidePanel.vue +3 -3
  39. package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +1 -1
  40. package/components/features/payment/process/PaymentMethodSidePanel.vue +3 -3
  41. package/components/features/payment/process/PaymentTopUpSidePanel.vue +2 -2
  42. package/components/features/pk-mobile-services/APkMobileServices.vue +1 -1
  43. package/components/features//321/201hange-version/AChangeVersion.vue +2 -2
  44. package/components/forms/checkbox/ACheckbox.vue +1 -1
  45. package/components/forms/checkbox/ACheckboxV2.vue +5 -5
  46. package/components/forms/input/AInputV2.vue +4 -4
  47. package/components/forms/input/date/AInputDate.vue +2 -2
  48. package/components/forms/input/password/AInputPassword.vue +8 -2
  49. package/components/forms/input/standard/AInputStandard.vue +3 -3
  50. package/components/forms/input/textarea/ATextarea.vue +2 -2
  51. package/components/forms/toggle/AToggle.vue +2 -3
  52. package/components/forms/toggle/AToggleV2.vue +3 -3
  53. package/components/modals/AnotherDeviceModal.vue +1 -1
  54. package/components/modals/ContactsMobileModel.vue +1 -1
  55. package/components/modals/ContentNavigationModal.vue +3 -3
  56. package/components/modals/Insufficient-funds-modal.vue +1 -1
  57. package/components/modals/PaymentMethodModal.vue +1 -1
  58. package/components/modals/id/IdBanner.vue +3 -3
  59. package/components/modals/id/IdRecoveryModal.vue +1 -1
  60. package/components/navigation/breadcrumbs/ABreadcrumbs.vue +106 -45
  61. package/components/navigation/breadcrumbs/types.ts +3 -0
  62. package/components/navigation/footer/AFooter.vue +39 -40
  63. package/components/navigation/footer/ui/a-footer-accordion.vue +9 -9
  64. package/components/navigation/header/AHeader.vue +2 -4
  65. package/components/navigation/header/AlmatyContacts.vue +78 -78
  66. package/components/navigation/header/CardGallery.vue +4 -4
  67. package/components/navigation/header/HeaderUsage.vue +29 -15
  68. package/components/navigation/header/ListItem.vue +2 -2
  69. package/components/navigation/header/NavCard.vue +1 -1
  70. package/components/navigation/header/NavList.vue +2 -2
  71. package/components/navigation/header/NotificationsMenu.vue +2 -3
  72. package/components/navigation/header/ProductMenu.vue +1 -1
  73. package/components/navigation/header/ProfileMenu.vue +26 -35
  74. package/components/navigation/header/SystemNotification.vue +1 -3
  75. package/components/navigation/pill-tabs/APillTabs.vue +2 -2
  76. package/components/navigation/pill-tabs/APillTabsV2.vue +5 -5
  77. package/components/overlays/drawer/ADrawerV2.vue +335 -0
  78. package/components/overlays/modal/AModal.vue +4 -4
  79. package/components/overlays/modal/AModalV2.vue +10 -4
  80. package/components/overlays/side-panel/ASidePanel.vue +6 -6
  81. package/components/overlays/sideover/ASlideover.vue +6 -6
  82. package/components/overlays/tooltip/ATooltip.vue +1 -1
  83. package/components/overlays/tooltip/ATooltipV2.vue +2 -12
  84. package/components/transitions/ATransitionHeight.vue +1 -1
  85. package/composables/useHeaderNavigationLinks.ts +100 -94
  86. package/icons/archive.vue +16 -0
  87. package/icons/arrow/arrows-in.vue +11 -0
  88. package/icons/arrow/arrows-out.vue +11 -0
  89. package/icons/arrow/arrows-resize-in.vue +38 -0
  90. package/icons/arrow/arrows-resize-out.vue +17 -0
  91. package/icons/bot.vue +52 -0
  92. package/icons/clock-time.vue +24 -0
  93. package/icons/compare-outlined.vue +8 -0
  94. package/icons/dots-horizontal.vue +31 -0
  95. package/icons/download-line.vue +13 -0
  96. package/icons/egov.vue +41 -0
  97. package/icons/elicense.vue +38 -0
  98. package/icons/favourite-bookmark-outline.vue +17 -0
  99. package/icons/file-pen.vue +31 -0
  100. package/icons/file-stack.vue +31 -0
  101. package/icons/file-types/excel.vue +131 -0
  102. package/icons/file-types/html.vue +13 -0
  103. package/icons/file-types/jpeg.vue +16 -0
  104. package/icons/file-types/pdf.vue +11 -0
  105. package/icons/file-types/png.vue +11 -0
  106. package/icons/file-types/rar.vue +11 -0
  107. package/icons/file-types/word.vue +50 -0
  108. package/icons/file-types/zip.vue +11 -0
  109. package/icons/git-compare.vue +38 -0
  110. package/icons/graduation-cap.vue +31 -0
  111. package/icons/graph-loading.vue +34 -0
  112. package/icons/help-question-circle.vue +26 -0
  113. package/icons/in-row-type.vue +46 -0
  114. package/icons/in-table-type.vue +29 -0
  115. package/icons/message-dotted.vue +30 -0
  116. package/icons/notification-error.vue +17 -0
  117. package/icons/person.vue +22 -0
  118. package/icons/settings.vue +26 -0
  119. package/icons/shield-alert.vue +31 -0
  120. package/icons/socials/facebook-color.vue +16 -0
  121. package/icons/socials/globe-color.vue +16 -0
  122. package/icons/socials/instagram-color.vue +46 -0
  123. package/icons/socials/linkedin-color.vue +14 -0
  124. package/icons/socials/telegram-color.vue +16 -0
  125. package/icons/socials/tiktok-color.vue +14 -0
  126. package/icons/socials/vk-color.vue +16 -0
  127. package/icons/socials/youtube-color.vue +16 -0
  128. package/icons/view-eye-open.vue +13 -0
  129. package/package.json +1 -1
  130. package/plugins/toast.client.ts +38 -55
  131. package/tailwind.config.ts +147 -116
  132. package/utils/twMerge.ts +34 -0
  133. package/components/elements/tree-select/components/tree-select-nodes.vue +0 -91
@@ -34,7 +34,7 @@ function isActive(itemPath: string) {
34
34
  class="group/title flex min-w-0 items-center gap-2 text-base font-semibold text-deepblue-900 transition-colors hover:text-blue-700 dark:text-gray-100 dark:hover:text-blue-500"
35
35
  >
36
36
  <span
37
- class="flex size-8 shrink-0 items-center justify-center rounded-lg bg-blue-50 text-blue-500 transition-colors group-hover/title:bg-blue-700 group-hover/title:text-white dark:bg-blue-500/10 dark:text-blue-500 dark:group-hover/title:bg-blue-500">
37
+ class="flex size-8 shrink-0 items-center justify-center rounded-md bg-blue-50 text-blue-500 transition-colors group-hover/title:bg-blue-700 group-hover/title:text-white dark:bg-blue-500/10 dark:text-blue-500 dark:group-hover/title:bg-blue-500">
38
38
  <component
39
39
  :is="icon"
40
40
  class="size-4"
@@ -53,7 +53,7 @@ function isActive(itemPath: string) {
53
53
  v-if="item.to"
54
54
  :to="item.to"
55
55
  itemprop="name"
56
- class="group/item flex items-center gap-2 rounded-lg px-2 py-1.5 transition-colors hover:bg-gray-50 dark:hover:bg-gray-200/10"
56
+ class="group/item flex items-center gap-2 rounded-md px-2 py-1.5 transition-colors hover:bg-gray-50 dark:hover:bg-gray-200/10"
57
57
  :class="{ 'bg-blue-100 dark:bg-gray-200/10': isActive(item.to) }"
58
58
  >
59
59
  <a-icon-chevron-right class="size-[14px] shrink-0 text-gray-300 group-hover/item:text-blue-500 dark:group-hover/item:text-blue-700" />
@@ -14,9 +14,8 @@ defineProps<{
14
14
  }>()
15
15
  </script>
16
16
  <template>
17
- <div class="flex w-[423px] flex-col gap-4 rounded-lg bg-white p-4 dark:bg-gray-900">
17
+ <div class="flex w-[423px] flex-col gap-4 rounded-md bg-white p-4 dark:bg-gray-900">
18
18
  <div class="flex items-center justify-between">
19
- <!-- <h4 class="heading-04">{{ $t('header.notifications.title') }}</h4>-->
20
19
  <h4 class="heading-04">Уведомления</h4>
21
20
  <ADigitBadge :value="`${notifications?.length} новых`" size="lg" />
22
21
  </div>
@@ -26,7 +25,7 @@ defineProps<{
26
25
  <li
27
26
  v-for="(notification, idx) in notifications"
28
27
  :key="idx"
29
- class="flex items-center justify-between gap-2.5 rounded-md bg-gray-50 px-4 py-2 dark:bg-gray-950"
28
+ class="flex items-center justify-between gap-2.5 rounded-sm bg-gray-50 px-4 py-2 dark:bg-gray-950"
30
29
  >
31
30
  <div class="rounded-full bg-blue-500 p-2">
32
31
  <icon-user />
@@ -62,7 +62,7 @@ function handleExportClick() {
62
62
  v-if="module.key === 'edo'"
63
63
  #bottom
64
64
  >
65
- <div class="mt-4 flex flex-col gap-4 rounded-xl bg-blue-700 p-4 normal-case text-white dark:bg-blue-500">
65
+ <div class="mt-4 flex flex-col gap-4 rounded-lg bg-blue-700 p-4 normal-case text-white dark:bg-blue-500">
66
66
  <div class="flex items-center gap-3">
67
67
  <span class="rounded-[10px] bg-white p-[10px] dark:bg-gray-950">
68
68
  <a-icon-download-rounded class="size-5 text-blue-700 dark:text-blue-500" />
@@ -39,23 +39,20 @@ function onReplenish() {
39
39
  </script>
40
40
 
41
41
  <template>
42
- <div class="w-full overflow-hidden bg-white text-deepblue-900 dark:bg-gray-900 dark:text-gray-200 lg:w-[320px] lg:rounded-xl lg:border lg:border-gray-200 lg:dark:border-gray-800">
43
- <!-- Subscription & balance card -->
42
+ <div class="w-full overflow-hidden bg-white text-deepblue-900 dark:bg-gray-900 dark:text-gray-200 lg:w-[320px] lg:rounded-lg lg:border lg:border-gray-200 lg:dark:border-gray-800">
44
43
  <div class="p-2">
45
- <div class="relative overflow-hidden rounded-xl bg-gradient-to-br from-blue-700/95 to-blue-500/85 p-3 text-white">
46
- <!-- decorative glow for a "living" feel -->
44
+ <div class="relative overflow-hidden rounded-lg bg-gradient-to-br from-blue-700/95 to-blue-500/85 p-3 text-white">
47
45
  <div class="pointer-events-none absolute -right-8 -top-10 size-28 rounded-full bg-white/20 blur-2xl" aria-hidden="true" />
48
46
 
49
47
  <div class="relative">
50
- <!-- Tariff -->
51
- <p class="text-[11px] font-medium uppercase tracking-wide text-white/70">
48
+ <p class="text-overline-medium uppercase text-white/70">
52
49
  {{ t('header.profile.tariff') }}
53
50
  </p>
54
51
  <div class="mt-0.5 flex items-center justify-between gap-2">
55
- <p class="truncate text-lg font-bold leading-tight">
52
+ <p class="truncate text-h4">
56
53
  {{ rate }}
57
54
  </p>
58
- <span class="inline-flex shrink-0 items-center gap-1.5 rounded-full px-2 py-0.5 text-[11px] font-medium text-white ring-1 ring-inset ring-white/20">
55
+ <span class="inline-flex shrink-0 items-center gap-1.5 rounded-full px-2 py-0.5 text-body-sm-medium text-white ring-1 ring-inset ring-white/20">
59
56
  <span class="relative flex size-1.5 items-center justify-center">
60
57
  <span class="absolute inline-flex size-full animate-ping rounded-full bg-green-400/80" />
61
58
  <span class="relative inline-flex size-1.5 rounded-full bg-green-400" />
@@ -64,10 +61,9 @@ function onReplenish() {
64
61
  </span>
65
62
  </div>
66
63
 
67
- <!-- Email (mobile only — desktop shows it in the header trigger) -->
68
64
  <p
69
65
  v-if="email"
70
- class="mt-1 truncate text-xs text-white/75 lg:hidden"
66
+ class="mt-1 truncate text-body-sm text-white/75 lg:hidden"
71
67
  data-test-id="profile-menu-email"
72
68
  >
73
69
  {{ email }}
@@ -75,42 +71,39 @@ function onReplenish() {
75
71
 
76
72
  <div class="my-2 h-px bg-white/15" />
77
73
 
78
- <!-- Balance -->
79
74
  <div class="flex items-center justify-between gap-2">
80
- <span class="text-xs text-white/80">{{ t('header.profile.currentBalance') }}</span>
81
- <span class="text-base font-bold tabular-nums">{{ formattedBalance }} ₸</span>
75
+ <span class="text-body-sm text-white/80">{{ t('header.profile.currentBalance') }}</span>
76
+ <span class="text-body-lg font-bold tabular-nums">{{ formattedBalance }} ₸</span>
82
77
  </div>
83
78
 
84
- <!-- Usage stats (mobile only — desktop shows them in the header bar) -->
85
79
  <div class="mt-2 grid grid-cols-2 gap-2 lg:hidden">
86
- <div class="flex items-center gap-2 rounded-lg px-2 py-1.5 ring-1 ring-inset ring-white/15">
80
+ <div class="flex items-center gap-2 rounded-md px-2 py-1.5 ring-1 ring-inset ring-white/15">
87
81
  <gauge class="size-4 shrink-0 text-white/80" />
88
82
  <div class="min-w-0">
89
- <p class="truncate text-[10px] uppercase leading-none text-white/70">
83
+ <p class="truncate text-body-sm uppercase leading-none text-white/70">
90
84
  {{ t('header.profile.requests') }}
91
85
  </p>
92
- <p class="mt-1 text-sm font-bold tabular-nums leading-none">
86
+ <p class="mt-1 text-body-md font-bold tabular-nums leading-none">
93
87
  {{ limitRemaining }}
94
88
  </p>
95
89
  </div>
96
90
  </div>
97
- <div class="flex items-center gap-2 rounded-lg px-2 py-1.5 ring-1 ring-inset ring-white/15">
91
+ <div class="flex items-center gap-2 rounded-md px-2 py-1.5 ring-1 ring-inset ring-white/15">
98
92
  <calendar class="size-4 shrink-0 text-white/80" />
99
93
  <div class="min-w-0">
100
- <p class="truncate text-[10px] uppercase leading-none text-white/70">
94
+ <p class="truncate text-body-sm uppercase leading-none text-white/70">
101
95
  {{ t('header.profile.daysLeft') }}
102
96
  </p>
103
- <p class="mt-1 text-sm font-bold tabular-nums leading-none">
97
+ <p class="mt-1 text-body-md font-bold tabular-nums leading-none">
104
98
  {{ daysRemaining }}
105
99
  </p>
106
100
  </div>
107
101
  </div>
108
102
  </div>
109
103
 
110
- <!-- Top up -->
111
104
  <button
112
105
  type="button"
113
- class="mt-2.5 inline-flex h-8 w-full cursor-pointer items-center justify-center gap-1.5 rounded-full bg-white px-3 text-sm font-semibold text-blue-700 shadow-sm transition-all duration-150 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 focus-visible:ring-offset-2 focus-visible:ring-offset-blue-600 active:scale-[0.98]"
106
+ class="mt-2.5 inline-flex h-8 w-full cursor-pointer items-center justify-center gap-1.5 rounded-full bg-white px-3 text-body-md-strong text-blue-700 shadow-sm transition-all duration-150 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 focus-visible:ring-offset-2 focus-visible:ring-offset-blue-600 active:scale-[0.98]"
114
107
  @click="onReplenish"
115
108
  >
116
109
  <a-icon-plus-circle class="size-4" />
@@ -122,31 +115,29 @@ function onReplenish() {
122
115
  </div>
123
116
  </div>
124
117
 
125
- <!-- Menu links -->
126
118
  <nav class="flex flex-col px-2 pb-2">
127
119
  <nuxt-link-locale
128
120
  v-for="item in items"
129
121
  :key="item.title"
130
- class="group flex items-center gap-3 rounded-lg px-2.5 py-2 text-sm transition-colors hover:bg-gray-50 dark:hover:bg-gray-800/60"
122
+ class="group flex items-center gap-3 rounded-md px-2.5 py-2 text-body-md transition-colors hover:bg-gray-50 dark:hover:bg-gray-800/60"
131
123
  :to="item.to"
132
124
  :data-test-id="item?.dataTestId"
133
125
  target="_blank"
134
126
  >
135
- <!-- <span-->
136
- <!-- class="flex size-8 shrink-0 items-center justify-center rounded-lg border border-blue-100 bg-blue-50 text-blue-700 dark:border-blue-900/40 dark:bg-blue-900/30 dark:text-blue-400"-->
137
- <!-- >-->
138
- <!-- <component-->
139
- <!-- :is="item.icon"-->
140
- <!-- class="size-4"-->
141
- <!-- />-->
142
- <!-- </span>-->
127
+ <!-- <span -->
128
+ <!-- class="flex size-8 shrink-0 items-center justify-center rounded-md border border-blue-100 bg-blue-50 text-blue-700 dark:border-blue-900/40 dark:bg-blue-900/30 dark:text-blue-400" -->
129
+ <!-- > -->
130
+ <!-- <component -->
131
+ <!-- :is="item.icon" -->
132
+ <!-- class="size-4" -->
133
+ <!-- /> -->
134
+ <!-- </span> -->
143
135
  <span class="truncate text-deepblue-900 group-hover:text-blue-700 dark:text-gray-200 dark:group-hover:text-blue-400">
144
136
  {{ item.title }}
145
137
  </span>
146
138
  </nuxt-link-locale>
147
139
  </nav>
148
140
 
149
- <!-- Footer: old version, theme (mobile), logout -->
150
141
  <div class="border-t border-gray-100 p-2 dark:border-gray-800">
151
142
  <a-change-version
152
143
  v-if="oldVersion"
@@ -156,7 +147,7 @@ function onReplenish() {
156
147
 
157
148
  <div
158
149
  v-if="!isLargeScreen"
159
- class="flex items-center justify-between rounded-lg px-2.5 py-2 text-sm text-deepblue-900 dark:text-gray-200"
150
+ class="flex items-center justify-between rounded-md px-2.5 py-2 text-body-md text-deepblue-900 dark:text-gray-200"
160
151
  >
161
152
  <span>{{ t('header.profile.colorScheme') }}</span>
162
153
  <a-color-mode />
@@ -164,7 +155,7 @@ function onReplenish() {
164
155
 
165
156
  <button
166
157
  type="button"
167
- class="flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 text-sm font-medium text-red-600 transition-colors hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20"
158
+ class="flex w-full cursor-pointer items-center gap-3 rounded-md px-2.5 py-2 text-body-md-medium text-red-600 transition-colors hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20"
168
159
  data-test-id="logout-button"
169
160
  @click="$emit('logout')"
170
161
  >
@@ -19,7 +19,6 @@ const message = useCookie('message')
19
19
  const systemMessage = ref<string | null>(null)
20
20
  const isOpen = ref(false)
21
21
 
22
- // API expects the legacy "counterparty" code instead of "pk".
23
22
  const moduleName = computed(() => (currentModule.value === 'pk' ? 'counterparty' : currentModule.value))
24
23
 
25
24
  function localizedMessage(detail: SystemMessageDetail): string {
@@ -34,7 +33,6 @@ function localizedMessage(detail: SystemMessageDetail): string {
34
33
  }
35
34
 
36
35
  async function fetchNotification() {
37
- // The cookie is set once the user dismisses the banner — skip the request afterwards.
38
36
  if (message.value !== undefined) return
39
37
 
40
38
  try {
@@ -74,7 +72,7 @@ onMounted(fetchNotification)
74
72
  <a-icon-info-circle
75
73
  class="max-h-[16px] w-full max-w-[16px] shrink-0 stroke-orange-500"
76
74
  />
77
- <span class="text-sm font-semibold text-orange-500">{{ systemMessage }}</span>
75
+ <span class="text-body-md-strong text-orange-500">{{ systemMessage }}</span>
78
76
  </div>
79
77
  <a-icon-x-mark
80
78
  class="max-h-[16px] w-full max-w-[16px] shrink-0 cursor-pointer stroke-orange-500"
@@ -115,14 +115,14 @@ const viewTypeValues: ViewType = {
115
115
  }
116
116
 
117
117
  const defaultClasses = (isDisabled: boolean) => [
118
- 'text-sm rounded-md outline-none select-none focus-visible:shadow-[0_0_0_4px#1B64B3]',
118
+ 'text-sm rounded-sm outline-none select-none focus-visible:shadow-[0_0_0_4px#1B64B3]',
119
119
  isDisabled ? 'bg-deepblue-900/5 dark:bg-gray-200/5 text-gray-500 dark:text-gray-500' : '',
120
120
  props.view === 'transparent' && isDisabled ? 'bg-transparent' : '',
121
121
  viewTypeValues[props.view]['default'],
122
122
  props.block ? 'w-full' : ''
123
123
  ]
124
124
  const selectedClasses = () => [
125
- 'text-sm rounded-md select-none focus-visible:shadow-[0_0_0_4px#1B64B3]',
125
+ 'text-sm rounded-sm select-none focus-visible:shadow-[0_0_0_4px#1B64B3]',
126
126
  viewTypeValues[props.view]['selected'],
127
127
  props.block ? 'w-full' : ''
128
128
  ]
@@ -32,8 +32,8 @@ const emit = defineEmits<{ (e: 'update:modelValue', value: string): void }>()
32
32
  const listRef = ref<HTMLElement | null>(null)
33
33
 
34
34
  const SIZE_CLASS: Record<'sm' | 'md', string> = {
35
- sm: 'text-xs',
36
- md: 'text-sm',
35
+ sm: 'text-body-sm',
36
+ md: 'text-body-md',
37
37
  }
38
38
 
39
39
  const COUNT_COLOR_CLASS: Record<CountColor, string> = {
@@ -74,7 +74,7 @@ onMounted(scrollActiveIntoView)
74
74
 
75
75
  <template>
76
76
  <div
77
- class="pill-tabs-v2 min-w-0 max-w-full rounded-xl border border-gray-200 bg-gray-50 p-1 dark:border-white/10 dark:bg-white/[0.03]"
77
+ class="pill-tabs-v2 min-w-0 max-w-full rounded-lg border border-gray-200 bg-gray-50 p-1 dark:border-white/10 dark:bg-white/[0.03]"
78
78
  :class="[
79
79
  block ? 'w-full' : 'w-fit',
80
80
  disabled ? 'pill-tabs-v2--disabled opacity-60' : '',
@@ -96,7 +96,7 @@ onMounted(scrollActiveIntoView)
96
96
  :aria-disabled="isDisabled(option) || undefined"
97
97
  :disabled="isDisabled(option)"
98
98
  :tabindex="modelValue === option.key ? 0 : -1"
99
- class="inline-flex items-center gap-1.5 whitespace-nowrap rounded-lg px-3 py-1.5 font-medium transition-colors duration-150"
99
+ class="inline-flex items-center gap-1.5 whitespace-nowrap rounded-md px-3 py-1.5 font-medium transition-colors duration-150"
100
100
  :class="[
101
101
  SIZE_CLASS[size],
102
102
  isDisabled(option)
@@ -113,7 +113,7 @@ onMounted(scrollActiveIntoView)
113
113
 
114
114
  <span
115
115
  v-if="showCount && !option.hideCount"
116
- class="inline-flex min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-xs font-semibold leading-5"
116
+ class="inline-flex min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 text-body-sm font-semibold leading-5"
117
117
  :class="COUNT_COLOR_CLASS[option.countColor ?? 'gray']"
118
118
  >
119
119
  {{ option.count }}
@@ -0,0 +1,335 @@
1
+ <script setup lang="ts">
2
+ import { onKeyStroke, useScrollLock } from '@vueuse/core'
3
+
4
+ const props = withDefaults(defineProps<{
5
+ title?: string
6
+ closeOnOverlay?: boolean
7
+ closeOnEsc?: boolean
8
+ lockScroll?: boolean
9
+ persistent?: boolean
10
+ transition?: boolean
11
+ contentClass?: string
12
+ hideHeader?: boolean
13
+ blur?: boolean | 'sm' | 'md' | 'lg' | 'xl'
14
+ swipeToClose?: boolean
15
+ swipeThreshold?: number
16
+ showDragHandle?: boolean
17
+ maxHeight?: string
18
+ }>(), {
19
+ closeOnOverlay: true,
20
+ closeOnEsc: true,
21
+ lockScroll: true,
22
+ persistent: false,
23
+ transition: true,
24
+ hideHeader: false,
25
+ blur: false,
26
+ swipeToClose: true,
27
+ swipeThreshold: 65,
28
+ showDragHandle: true,
29
+ maxHeight: '100dvh',
30
+ })
31
+
32
+ const emit = defineEmits<{
33
+ (e: 'close'): void
34
+ (e: 'open'): void
35
+ }>()
36
+
37
+ const open = defineModel<boolean>({ required: true })
38
+
39
+ const contentRef = ref<HTMLElement | null>(null)
40
+ const overlayRef = ref<HTMLElement | null>(null)
41
+ const swipeClosing = ref(false)
42
+ const bodyScrollLocked = useScrollLock(import.meta.client ? document.body : null)
43
+
44
+ const blurClass = computed(() => {
45
+ if (!props.blur) return ''
46
+
47
+ switch (props.blur) {
48
+ case 'sm': return 'backdrop-blur-sm'
49
+ case 'md': return 'backdrop-blur-md'
50
+ case 'lg': return 'backdrop-blur-lg'
51
+ case 'xl': return 'backdrop-blur-xl'
52
+ default: return 'backdrop-blur'
53
+ }
54
+ })
55
+
56
+ const overlayClass = computed(() => {
57
+ const base = ['drawer-v2-overlay fixed inset-0 z-[10000] flex items-end justify-stretch bg-black/40 dark:bg-black/60']
58
+ if (blurClass.value) base.push(blurClass.value)
59
+ return base.join(' ')
60
+ })
61
+
62
+ const contentStyle = computed(() => ({ maxHeight: props.maxHeight }))
63
+
64
+ const overlayTransitionName = computed(() => {
65
+ if (swipeClosing.value) return ''
66
+ return props.transition ? 'drawer-v2-overlay' : ''
67
+ })
68
+ const contentTransitionName = computed(() => {
69
+ if (swipeClosing.value || !props.transition) return ''
70
+ return 'drawer-v2-sheet'
71
+ })
72
+
73
+ function closeIfAllowed() {
74
+ if (props.persistent) return
75
+ open.value = false
76
+ }
77
+
78
+ function onOverlayClick(event: MouseEvent) {
79
+ if (!props.closeOnOverlay || props.persistent) return
80
+ if (event.target === event.currentTarget) {
81
+ open.value = false
82
+ }
83
+ }
84
+
85
+ onKeyStroke('Escape', () => {
86
+ if (!open.value) return
87
+ if (!props.closeOnEsc || props.persistent) return
88
+ open.value = false
89
+ })
90
+
91
+ function setScrollbarCompensation(active: boolean) {
92
+ if (!import.meta.client) return
93
+
94
+ if (active) {
95
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
96
+ document.body.style.paddingRight = scrollbarWidth > 0 ? `${scrollbarWidth}px` : ''
97
+ }
98
+ else {
99
+ document.body.style.paddingRight = ''
100
+ }
101
+ }
102
+
103
+ watch(open, (value) => {
104
+ if (props.lockScroll) {
105
+ if (value) setScrollbarCompensation(true)
106
+ bodyScrollLocked.value = value
107
+ if (!value) setScrollbarCompensation(false)
108
+ }
109
+
110
+ if (value) emit('open')
111
+ else emit('close')
112
+ })
113
+
114
+ onBeforeUnmount(() => {
115
+ if (bodyScrollLocked.value) {
116
+ bodyScrollLocked.value = false
117
+ }
118
+ setScrollbarCompensation(false)
119
+ })
120
+
121
+ const dragging = ref(false)
122
+ const dragStartY = ref(0)
123
+ const dragDelta = ref(0)
124
+
125
+ function canSwipe() {
126
+ return props.swipeToClose && !props.persistent
127
+ }
128
+
129
+ function setTransform(value: string) {
130
+ if (contentRef.value) {
131
+ contentRef.value.style.transform = value
132
+ }
133
+ }
134
+
135
+ function onTouchStart(event: TouchEvent) {
136
+ if (!canSwipe()) return
137
+ dragging.value = true
138
+ dragStartY.value = event.touches[0].clientY
139
+ dragDelta.value = 0
140
+
141
+ if (contentRef.value) {
142
+ contentRef.value.style.transition = 'none'
143
+ }
144
+ }
145
+
146
+ function onTouchMove(event: TouchEvent) {
147
+ if (!dragging.value || !canSwipe()) return
148
+
149
+ const delta = event.touches[0].clientY - dragStartY.value
150
+ if (delta < 0) {
151
+ dragDelta.value = 0
152
+ setTransform('')
153
+ return
154
+ }
155
+
156
+ dragDelta.value = delta
157
+ setTransform(`translateY(${delta}px)`)
158
+ }
159
+
160
+ function prefersReducedMotion() {
161
+ return import.meta.client && window.matchMedia('(prefers-reduced-motion: reduce)').matches
162
+ }
163
+
164
+ function animateClose() {
165
+ const content = contentRef.value
166
+
167
+ if (!content || prefersReducedMotion()) {
168
+ open.value = false
169
+ return
170
+ }
171
+
172
+ swipeClosing.value = true
173
+
174
+ let done = false
175
+ const finish = () => {
176
+ if (done) return
177
+ done = true
178
+ content.removeEventListener('transitionend', onTransitionEnd)
179
+ open.value = false
180
+ swipeClosing.value = false
181
+ }
182
+
183
+ function onTransitionEnd(event: TransitionEvent) {
184
+ if (event.target === content && event.propertyName === 'transform') {
185
+ finish()
186
+ }
187
+ }
188
+
189
+ content.addEventListener('transitionend', onTransitionEnd)
190
+ setTimeout(finish, 320)
191
+
192
+ requestAnimationFrame(() => {
193
+ content.style.transition = 'transform 260ms cubic-bezier(0.22, 1, 0.36, 1)'
194
+ content.style.transform = 'translateY(100%)'
195
+
196
+ if (overlayRef.value) {
197
+ overlayRef.value.style.transition = 'opacity 260ms ease'
198
+ overlayRef.value.style.opacity = '0'
199
+ }
200
+ })
201
+ }
202
+
203
+ function onTouchEnd() {
204
+ if (!dragging.value) return
205
+ dragging.value = false
206
+
207
+ const shouldClose = dragDelta.value > props.swipeThreshold && canSwipe()
208
+
209
+ if (shouldClose) {
210
+ animateClose()
211
+ }
212
+ else if (contentRef.value) {
213
+ contentRef.value.style.transition = 'transform 220ms cubic-bezier(0.22, 1, 0.36, 1)'
214
+ contentRef.value.style.transform = ''
215
+ }
216
+
217
+ dragDelta.value = 0
218
+ }
219
+ </script>
220
+
221
+ <template>
222
+ <client-only>
223
+ <teleport to="body">
224
+ <transition :name="overlayTransitionName">
225
+ <div
226
+ v-if="open"
227
+ ref="overlayRef"
228
+ :class="overlayClass"
229
+ @mousedown="onOverlayClick"
230
+ >
231
+ <transition :name="contentTransitionName" appear>
232
+ <div
233
+ v-if="open"
234
+ ref="contentRef"
235
+ class="drawer-v2-content flex w-full max-w-none flex-col overflow-hidden rounded-t-xl border-t border-gray-200 bg-white shadow-2xl dark:border-gray-800 dark:bg-gray-900"
236
+ :style="contentStyle"
237
+ @mousedown.stop
238
+ >
239
+ <div
240
+ v-if="showDragHandle"
241
+ class="flex shrink-0 cursor-grab touch-none justify-center py-2"
242
+ @touchstart.passive="onTouchStart"
243
+ @touchmove="onTouchMove"
244
+ @touchend="onTouchEnd"
245
+ @touchcancel="onTouchEnd"
246
+ >
247
+ <div class="h-1 w-10 rounded-full bg-gray-300 dark:bg-gray-600" />
248
+ </div>
249
+
250
+ <header
251
+ v-if="!hideHeader && (title || $slots.header)"
252
+ class="flex shrink-0 items-center justify-between gap-3 border-b border-gray-200 px-5 py-4 dark:border-gray-800"
253
+ @touchstart.passive="onTouchStart"
254
+ @touchmove="onTouchMove"
255
+ @touchend="onTouchEnd"
256
+ @touchcancel="onTouchEnd"
257
+ >
258
+ <slot name="header">
259
+ <h2 class="text-base font-semibold text-gray-900 dark:text-gray-100">
260
+ {{ title }}
261
+ </h2>
262
+ <button
263
+ type="button"
264
+ class="rounded-sm p-1 text-gray-500 transition hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-gray-200"
265
+ @click="closeIfAllowed"
266
+ >
267
+ <a-icon-x-mark class="size-4" />
268
+ </button>
269
+ </slot>
270
+ </header>
271
+
272
+ <div class="min-h-0 flex-1 overflow-auto" :class="contentClass">
273
+ <slot :close="closeIfAllowed" />
274
+ </div>
275
+
276
+ <footer
277
+ v-if="$slots.footer"
278
+ class="shrink-0 border-t border-gray-200 px-5 py-4 dark:border-gray-800"
279
+ >
280
+ <slot name="footer" :close="closeIfAllowed" />
281
+ </footer>
282
+ </div>
283
+ </transition>
284
+ </div>
285
+ </transition>
286
+ </teleport>
287
+ </client-only>
288
+ </template>
289
+
290
+ <style scoped lang="scss">
291
+ .drawer-v2-overlay {
292
+ will-change: opacity;
293
+ }
294
+
295
+ .drawer-v2-content {
296
+ will-change: transform;
297
+ transform: translateZ(0);
298
+ backface-visibility: hidden;
299
+ }
300
+
301
+ .drawer-v2-overlay-enter-active {
302
+ transition: opacity 240ms ease-out;
303
+ }
304
+
305
+ .drawer-v2-overlay-leave-active {
306
+ transition: opacity 220ms ease-in;
307
+ }
308
+
309
+ .drawer-v2-overlay-enter-from,
310
+ .drawer-v2-overlay-leave-to {
311
+ opacity: 0;
312
+ }
313
+
314
+ .drawer-v2-sheet-enter-active {
315
+ transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
316
+ }
317
+
318
+ .drawer-v2-sheet-leave-active {
319
+ transition: transform 240ms cubic-bezier(0.4, 0, 1, 1);
320
+ }
321
+
322
+ .drawer-v2-sheet-enter-from,
323
+ .drawer-v2-sheet-leave-to {
324
+ transform: translate3d(0, 100%, 0);
325
+ }
326
+
327
+ @media (prefers-reduced-motion: reduce) {
328
+ .drawer-v2-overlay-enter-active,
329
+ .drawer-v2-overlay-leave-active,
330
+ .drawer-v2-sheet-enter-active,
331
+ .drawer-v2-sheet-leave-active {
332
+ transition: none;
333
+ }
334
+ }
335
+ </style>
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
 
3
- import { twJoin, twMerge } from 'tailwind-merge'
3
+ import { twJoin, twMerge } from '#adata-ui/utils/twMerge'
4
4
  import { onClickOutside, useWindowSize } from '@vueuse/core'
5
5
  import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock'
6
6
 
@@ -22,13 +22,13 @@ const uiConfig = {
22
22
  position: 'top-1/2 left-1/2 translate-y-[-50%] translate-x-[-50%]',
23
23
  padding: "p-8",
24
24
  background: "bg-white dark:bg-gray-900",
25
- rounded: "rounded-lg",
25
+ rounded: "rounded-md",
26
26
  fullscreen: "w-screen h-screen rounded-none",
27
27
  mobile: {
28
28
  width: "w-screen",
29
29
  position: "bottom-0 left-0 transition-transform",
30
30
  padding: 'px-4 sm:pb-8 pb-4 pt-3',
31
- rounded: "rounded-t-2xl"
31
+ rounded: "rounded-t-xl"
32
32
  },
33
33
  transition: {
34
34
  desktop: {
@@ -297,7 +297,7 @@ watch(modelValue, async (newValue) => {
297
297
  >
298
298
  <h4
299
299
  v-if="title"
300
- class="heading-02 text-center"
300
+ class="text-h2 text-center"
301
301
  @touchmove="touchmove"
302
302
  @touchstart="touchstart"
303
303
  @touchend="touchend"