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

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 (56) hide show
  1. package/.claude/settings.local.json +19 -0
  2. package/components/elements/a-select-row/ASelectRowV2.vue +214 -0
  3. package/components/elements/button/AButtonV2.vue +112 -0
  4. package/components/elements/leave-note/ALeaveNote.vue +38 -17
  5. package/components/elements/segmented/ASegmentedV2.vue +58 -0
  6. package/components/elements/select/ASelectV2.vue +589 -0
  7. package/components/elements/show-more/AShowMoreV2.vue +26 -0
  8. package/components/elements/star-rating/AStarRating.vue +12 -4
  9. package/components/elements/status-badge-v2/StatusBadgeV2.vue +78 -0
  10. package/components/features/payment/process/PaymentKaspiQrSidePanel.vue +43 -27
  11. package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +34 -23
  12. package/components/features/payment/process/PaymentMethodSidePanel.vue +61 -49
  13. package/components/features/payment/process/PaymentProcess.vue +6 -6
  14. package/components/features/payment/process/PaymentTopUpSidePanel.vue +44 -63
  15. package/components/forms/checkbox/ACheckboxV2.vue +229 -0
  16. package/components/forms/feedback-form/FeedbackForm.vue +8 -8
  17. package/components/forms/input/AInputV2.vue +542 -0
  18. package/components/forms/input/textarea/ATextareaV2.vue +248 -0
  19. package/components/forms/toggle/AToggleV2.vue +71 -0
  20. package/components/modals/AnotherDeviceModal.vue +22 -18
  21. package/components/modals/ApplicationAcceptedModal.vue +12 -15
  22. package/components/modals/ConnectingTariffModal.vue +13 -20
  23. package/components/modals/ContentNavigationModal.vue +77 -333
  24. package/components/modals/Insufficient-funds-modal.vue +18 -33
  25. package/components/modals/LimitReachedModal.vue +4 -3
  26. package/components/modals/NoAccessModal.vue +9 -11
  27. package/components/modals/PaymentMethodModal.vue +1 -1
  28. package/components/modals/ReportBugConfirmModal.vue +12 -10
  29. package/components/modals/ReportBugModal.vue +42 -36
  30. package/components/modals/Resend.vue +9 -10
  31. package/components/modals/SubmitApplicationModal.vue +27 -24
  32. package/components/modals/id/IdConfirmAccountOtpModal.vue +4 -9
  33. package/components/modals/id/IdLoginModal.vue +3 -8
  34. package/components/modals/id/IdModals.vue +23 -10
  35. package/components/modals/id/IdTwoFactorModal.vue +3 -8
  36. package/components/navigation/bottom-navigation/ABottomNavigation.vue +1 -1
  37. package/components/navigation/header/AHeader.vue +15 -19
  38. package/components/navigation/header/HeaderLink.vue +15 -21
  39. package/components/navigation/header/NavList.vue +8 -32
  40. package/components/navigation/header/ProductMenu.vue +52 -50
  41. package/components/navigation/pill-tabs/APillTabsV2.vue +134 -0
  42. package/components/overlays/modal/AModalV2.vue +391 -0
  43. package/composables/useAccessTokenCookie.ts +25 -0
  44. package/composables/useChipOverflow.ts +92 -0
  45. package/composables/useHeaderNavigationLinks.ts +53 -23
  46. package/icons/arrow/arrow-right.vue +14 -0
  47. package/icons/briefcase.vue +14 -0
  48. package/icons/car-side.vue +16 -0
  49. package/icons/cart.vue +15 -0
  50. package/icons/download-rounded.vue +15 -0
  51. package/icons/shield-verified.vue +14 -0
  52. package/lang/en.ts +35 -4
  53. package/lang/kk.ts +36 -5
  54. package/lang/ru.ts +37 -6
  55. package/package.json +1 -1
  56. package/shared/constans/pages.ts +8 -2
@@ -0,0 +1,248 @@
1
+ <script lang="ts" setup>
2
+ defineOptions({ inheritAttrs: false })
3
+
4
+ const props = withDefaults(defineProps<Props>(), {
5
+ label: '',
6
+ placeholder: '',
7
+ required: false,
8
+ disabled: false,
9
+ readonly: false,
10
+ error: undefined,
11
+ size: 'md',
12
+ variant: 'default',
13
+ rows: 4,
14
+ resize: 'vertical',
15
+ maxlength: undefined,
16
+ autoResize: false,
17
+ inputId: undefined,
18
+ })
19
+
20
+ const emit = defineEmits<{
21
+ (e: 'focus', ev: FocusEvent): void
22
+ (e: 'blur', ev: FocusEvent): void
23
+ }>()
24
+
25
+ interface Props {
26
+ label?: string
27
+ placeholder?: string
28
+ required?: boolean
29
+ disabled?: boolean
30
+ readonly?: boolean
31
+ error?: string | string[]
32
+ size?: 'sm' | 'md'
33
+ variant?: 'default' | 'gray' | 'blue'
34
+ rows?: number
35
+ resize?: 'none' | 'vertical' | 'both'
36
+ maxlength?: number
37
+ autoResize?: boolean
38
+ inputId?: string
39
+ }
40
+
41
+ const modelValue = defineModel<string>({ default: '' })
42
+
43
+ const textareaRef = ref<HTMLTextAreaElement | null>(null)
44
+ const isFocused = ref(false)
45
+
46
+ const internalId = useId()
47
+ const inputId = computed(() => props.inputId ?? `textarea-v2-${internalId}`)
48
+
49
+ const hasValue = computed(() => modelValue.value != null && modelValue.value !== '')
50
+ const hasError = computed(() => Array.isArray(props.error) ? props.error.length > 0 : !!props.error)
51
+ const isLabelFloated = computed(() => hasValue.value || isFocused.value)
52
+
53
+ const errorMessages = computed(() => {
54
+ if (!props.error) return [] as string[]
55
+ return Array.isArray(props.error) ? props.error : [props.error]
56
+ })
57
+
58
+ const fieldClass = computed(() => {
59
+ if (props.disabled) {
60
+ return 'border-gray-200 bg-gray-100/70 dark:border-gray-700 dark:bg-white/[0.03]'
61
+ }
62
+ if (props.variant === 'gray') {
63
+ return 'border-transparent bg-gray-50 dark:border-transparent dark:bg-white/[0.04]'
64
+ }
65
+ if (props.variant === 'blue') {
66
+ return 'border-blue-200 bg-blue-50 dark:border-blue-900/50 dark:bg-blue-900/20'
67
+ }
68
+ return 'border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900'
69
+ })
70
+
71
+ const resizeClass = computed(() => {
72
+ if (props.autoResize) return 'resize-none'
73
+ switch (props.resize) {
74
+ case 'none': return 'resize-none'
75
+ case 'both': return 'resize'
76
+ case 'vertical':
77
+ default: return 'resize-y'
78
+ }
79
+ })
80
+
81
+ const paddingClass = computed(() => {
82
+ // Паддинг постоянный (не зависит от фокуса/значения) — textarea не меняет размер при фокусе.
83
+ const pt = !props.label ? 'pt-2.5' : (props.size === 'md' ? 'pt-4' : 'pt-3.5')
84
+ const pb = props.maxlength ? 'pb-6' : (!props.label ? 'pb-2.5' : (props.size === 'md' ? 'pb-2' : 'pb-1.5'))
85
+ return `px-4 ${pt} ${pb}`
86
+ })
87
+
88
+ const attrs = useAttrs()
89
+ const wrapperAttrs = computed(() => ({ class: attrs.class, style: attrs.style }))
90
+ const inputAttrs = computed(() => {
91
+ const { class: _c, style: _s, ...rest } = attrs as Record<string, unknown>
92
+ return rest
93
+ })
94
+
95
+ function adjustHeight() {
96
+ const el = textareaRef.value
97
+ if (!props.autoResize || !el) return
98
+ el.style.height = 'auto'
99
+ el.style.height = `${el.scrollHeight}px`
100
+ }
101
+
102
+ function onFocus(ev: FocusEvent) {
103
+ isFocused.value = true
104
+ emit('focus', ev)
105
+ }
106
+ function onBlur(ev: FocusEvent) {
107
+ isFocused.value = false
108
+ emit('blur', ev)
109
+ }
110
+
111
+ function focus() {
112
+ textareaRef.value?.focus()
113
+ }
114
+ function blur() {
115
+ textareaRef.value?.blur()
116
+ }
117
+
118
+ watch(modelValue, () => nextTick(adjustHeight))
119
+ onMounted(adjustHeight)
120
+
121
+ defineExpose({ focus, blur, textareaRef })
122
+ </script>
123
+
124
+ <template>
125
+ <div class="textarea-v2 relative w-full text-sm" v-bind="wrapperAttrs">
126
+ <div
127
+ class="textarea-v2__field relative rounded-[10px] border border-solid transition-colors duration-200 focus-within:border-blue-600 focus-within:ring-2 focus-within:ring-blue-600/20 hover:border-blue-500 dark:focus-within:border-blue-400 dark:focus-within:ring-blue-400/20 dark:hover:border-blue-400"
128
+ :class="[fieldClass, { 'textarea-v2__field--error': hasError, 'textarea-v2__field--disabled': disabled }]"
129
+ >
130
+ <label
131
+ v-if="label"
132
+ :for="inputId"
133
+ :data-size="size"
134
+ class="textarea-v2__label pointer-events-none absolute left-4 max-w-[calc(100%-2rem)] truncate text-gray-500 dark:text-gray-400"
135
+ :class="{ 'textarea-v2__label--floated': isLabelFloated }"
136
+ >
137
+ {{ label }}<span v-if="required" class="text-red-500 dark:text-red-400">&nbsp;*</span>
138
+ </label>
139
+
140
+ <textarea
141
+ :id="inputId"
142
+ ref="textareaRef"
143
+ v-model="modelValue"
144
+ :rows="rows"
145
+ :maxlength="maxlength"
146
+ :placeholder="(isLabelFloated || !label) ? placeholder : ''"
147
+ :disabled="disabled"
148
+ :readonly="readonly"
149
+ :tabindex="disabled ? -1 : 0"
150
+ class="textarea-v2__input text-deepblue-900 block w-full bg-transparent leading-snug outline-none placeholder:text-gray-500 disabled:cursor-not-allowed dark:text-gray-200 dark:placeholder:text-gray-400"
151
+ :class="[resizeClass, paddingClass]"
152
+ v-bind="inputAttrs"
153
+ @focus="onFocus"
154
+ @blur="onBlur"
155
+ />
156
+
157
+ <span
158
+ v-if="maxlength"
159
+ class="textarea-v2__counter pointer-events-none absolute bottom-1.5 right-3 text-[10px] text-gray-400 dark:text-gray-500"
160
+ >
161
+ {{ modelValue?.length ?? 0 }}/{{ maxlength }}
162
+ </span>
163
+ </div>
164
+
165
+ <transition
166
+ enter-active-class="textarea-v2__error-enter-active"
167
+ enter-from-class="textarea-v2__error-enter-from"
168
+ enter-to-class="textarea-v2__error-enter-to"
169
+ leave-active-class="textarea-v2__error-leave-active"
170
+ leave-from-class="textarea-v2__error-leave-from"
171
+ leave-to-class="textarea-v2__error-leave-to"
172
+ >
173
+ <p v-if="hasError" class="mt-1 flex flex-col text-xs text-red-500 dark:text-red-400">
174
+ <span v-for="(err, idx) in errorMessages" :key="idx">{{ err }}</span>
175
+ </p>
176
+ </transition>
177
+ </div>
178
+ </template>
179
+
180
+ <style scoped>
181
+ .textarea-v2__field--error {
182
+ border-color: theme('colors.red.500');
183
+ }
184
+
185
+ .textarea-v2__field--disabled {
186
+ pointer-events: none;
187
+ cursor: not-allowed;
188
+ opacity: 0.7;
189
+ }
190
+
191
+ .textarea-v2__field--disabled .textarea-v2__input {
192
+ cursor: not-allowed;
193
+ }
194
+
195
+ .textarea-v2__input {
196
+ scrollbar-width: thin;
197
+ }
198
+
199
+ .textarea-v2__label {
200
+ top: 26px;
201
+ transform: translateY(-50%);
202
+ font-size: 14px;
203
+ line-height: 1.3;
204
+ transition:
205
+ transform 300ms cubic-bezier(0.22, 1, 0.36, 1),
206
+ font-size 300ms cubic-bezier(0.22, 1, 0.36, 1),
207
+ color 300ms ease-out;
208
+ }
209
+
210
+ .textarea-v2__label[data-size="sm"] {
211
+ top: 24px;
212
+ }
213
+
214
+ .textarea-v2__label--floated {
215
+ font-size: 10px;
216
+ transform: translateY(-21px);
217
+ }
218
+
219
+ .textarea-v2__label--floated[data-size="sm"] {
220
+ transform: translateY(-19px);
221
+ }
222
+
223
+ .textarea-v2__error-enter-active {
224
+ transition: transform 0.3s, opacity 0.3s;
225
+ }
226
+
227
+ .textarea-v2__error-enter-from {
228
+ opacity: 0;
229
+ transform: translateY(-4px);
230
+ }
231
+
232
+ .textarea-v2__error-leave-active {
233
+ transition: transform 0.2s, opacity 0.2s;
234
+ }
235
+
236
+ .textarea-v2__error-leave-to {
237
+ opacity: 0;
238
+ transform: translateY(-4px);
239
+ }
240
+
241
+ @media (prefers-reduced-motion: reduce) {
242
+ .textarea-v2__label,
243
+ .textarea-v2__error-enter-active,
244
+ .textarea-v2__error-leave-active {
245
+ transition: none;
246
+ }
247
+ }
248
+ </style>
@@ -0,0 +1,71 @@
1
+ <script setup lang="ts">
2
+ type Size = 'sm' | 'md'
3
+
4
+ const props = withDefaults(defineProps<{
5
+ label?: string
6
+ labelSide?: 'left' | 'right'
7
+ size?: Size
8
+ disabled?: boolean
9
+ }>(), {
10
+ labelSide: 'left',
11
+ size: 'md',
12
+ disabled: false,
13
+ })
14
+
15
+ const model = defineModel<boolean>({ default: false })
16
+
17
+ const SIZE: Record<Size, { track: string, knob: string, on: string, off: string, text: string }> = {
18
+ sm: { track: 'h-5 w-9', knob: 'size-4', on: 'translate-x-4', off: 'translate-x-0.5', text: 'text-xs' },
19
+ md: { track: 'h-6 w-11', knob: 'size-5', on: 'translate-x-5', off: 'translate-x-0.5', text: 'text-sm' },
20
+ }
21
+ const s = computed(() => SIZE[props.size])
22
+
23
+ function toggle() {
24
+ if (props.disabled) return
25
+ model.value = !model.value
26
+ }
27
+ </script>
28
+
29
+ <template>
30
+ <button
31
+ type="button"
32
+ role="switch"
33
+ :aria-checked="model"
34
+ :aria-disabled="disabled || undefined"
35
+ :disabled="disabled"
36
+ class="toggle-v2 group inline-flex select-none items-center gap-2 rounded-full focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
37
+ :class="labelSide === 'right' ? 'flex-row-reverse' : ''"
38
+ @click="toggle"
39
+ >
40
+ <span
41
+ v-if="label"
42
+ class="font-medium text-gray-600 transition-colors group-hover:text-gray-800 dark:text-gray-300 dark:group-hover:text-gray-100"
43
+ :class="s.text"
44
+ >
45
+ {{ label }}
46
+ </span>
47
+ <span
48
+ class="relative inline-flex shrink-0 items-center rounded-full ring-1 ring-inset transition-colors duration-200 group-focus-visible:ring-2 group-focus-visible:ring-blue-500/50"
49
+ :class="[
50
+ s.track,
51
+ model
52
+ ? 'bg-blue-600 ring-blue-600/30 dark:bg-blue-500 dark:ring-blue-400/30'
53
+ : 'bg-gray-200 ring-gray-300/60 dark:bg-gray-700 dark:ring-gray-600/60',
54
+ ]"
55
+ >
56
+ <span
57
+ class="toggle-v2__knob inline-block rounded-full bg-white shadow-sm transition-transform duration-200 will-change-transform dark:bg-gray-100"
58
+ :class="[s.knob, model ? s.on : s.off]"
59
+ />
60
+ </span>
61
+ </button>
62
+ </template>
63
+
64
+ <style scoped>
65
+ @media (prefers-reduced-motion: reduce) {
66
+ .toggle-v2,
67
+ .toggle-v2 * {
68
+ transition: none !important;
69
+ }
70
+ }
71
+ </style>
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
- import { useAnotherDeviceModal } from '#adata-ui/composables/modalsState'
3
2
  import { useUrls } from '#adata-ui/composables/useUrls'
3
+ import { navigateToLocalizedPage } from '#adata-ui/utils/localizedNavigation'
4
+
4
5
  const { t, locale } = useI18n()
5
6
  const localePath = useLocalePath()
6
7
 
@@ -11,50 +12,53 @@ function onClose() {
11
12
  isOpen.value = false
12
13
  return navigateTo(localePath('/'))
13
14
  }
15
+
16
+ function onToMain() {
17
+ return navigateToLocalizedPage({ locale, projectUrl: landing, target: '_self' })
18
+ }
14
19
  </script>
15
20
 
16
21
  <template>
17
- <a-modal
22
+ <a-modal-v2
18
23
  v-model="isOpen"
19
- :title="t('modals.another_device_modal.title')"
24
+ width="424px"
20
25
  @close="onClose"
21
26
  >
22
- <div class="flex flex-col items-center justify-center gap-5">
27
+ <div class="flex flex-col items-center justify-center gap-4 px-6 pb-4">
23
28
  <a-ill-phone-check />
24
29
  <p class="body-400 text-center">
25
30
  {{ t('modals.another_device_modal.info') }}
26
31
  </p>
27
32
  <div
28
- class="body-400 flex gap-2 rounded-lg border border-red-500 dark:border-red-400 bg-[#FDECEB] dark:bg-red-400/10 p-4 text-red-500 dark:text-red-400"
33
+ class="body-400 flex gap-2 rounded-lg border border-red-500 bg-[#FDECEB] p-3 text-red-500 dark:border-red-400 dark:bg-red-400/10 dark:text-red-400"
29
34
  >
30
35
  <a-icon-warning-triangle
31
- class="shrink-0"
32
- width="24"
33
- height="24"
36
+ class="size-4 shrink-0"
34
37
  />
35
- <p>
38
+ <span class="text-xs">
36
39
  {{ t('modals.another_device_modal.alert') }}
37
- </p>
40
+ </span>
38
41
  </div>
39
42
  </div>
40
43
  <template #footer>
41
44
  <div class="flex w-full gap-2">
42
- <a-button
45
+ <a-button-v2
43
46
  view="outline"
44
- class="w-full"
47
+ color="blue"
48
+ block
45
49
  @click="onClose"
46
50
  >
47
51
  {{ t('modals.buttons.close') }}
48
- </a-button>
49
- <a-button
50
- class="w-full"
51
- :to="buildLocalizedUrl(locale, landing)"
52
+ </a-button-v2>
53
+ <a-button-v2
54
+ block
55
+ @click="onToMain"
52
56
  >
53
57
  {{ t('modals.buttons.toMain') }}
54
- </a-button>
58
+ </a-button-v2>
55
59
  </div>
56
60
  </template>
57
- </a-modal>
61
+ </a-modal-v2>
58
62
  </template>
59
63
 
60
64
  <style scoped></style>
@@ -1,10 +1,9 @@
1
1
  <script setup lang="ts">
2
-
3
2
  const { t } = useI18n()
4
3
 
5
- const isOpen = defineModel<boolean>({default: false})
4
+ const isOpen = defineModel<boolean>({ default: false })
6
5
 
7
- const cancel = () => {
6
+ function cancel() {
8
7
  isOpen.value = false
9
8
  }
10
9
 
@@ -16,30 +15,28 @@ const modalText = {
16
15
  </script>
17
16
 
18
17
  <template>
19
- <a-modal
18
+ <a-modal-v2
20
19
  v-model="isOpen"
21
- name="application-accepted"
22
- :prevent-close="true"
20
+ width="424px"
21
+ :persistent="true"
23
22
  >
24
- <div class="flex flex-col gap-5">
25
- <p class="text-center heading-02">
26
- {{ modalText.title }}
27
- </p>
28
- <a-ill-ok class="w-full" />
29
- <p class="text-center body-400">
23
+ <div class="flex flex-col items-center justify-center gap-4 px-6 py-4">
24
+ <a-ill-ok />
25
+ <p class="body-400 text-center">
30
26
  {{ modalText.info }}
31
27
  </p>
32
28
  </div>
33
29
  <template #footer>
34
- <a-button
30
+ <a-button-v2
35
31
  view="outline"
32
+ color="blue"
36
33
  block
37
34
  @click="cancel"
38
35
  >
39
36
  {{ modalText.close }}
40
- </a-button>
37
+ </a-button-v2>
41
38
  </template>
42
- </a-modal>
39
+ </a-modal-v2>
43
40
  </template>
44
41
 
45
42
  <style scoped>
@@ -1,9 +1,7 @@
1
1
  <script setup lang="ts">
2
- import { handleElementPosition } from '#adata-ui/utils/getMaxZindex'
3
-
4
2
  const props = withDefaults(
5
3
  defineProps<{
6
- tariff: string
4
+ tariff?: string
7
5
  deleteFromDom?: boolean
8
6
 
9
7
  }>(),
@@ -24,42 +22,37 @@ function confirm() {
24
22
  function cancel() {
25
23
  isOpen.value = false
26
24
  }
27
-
28
- if (props.deleteFromDom) {
29
- handleElementPosition('connecting-tariff')
30
- }
25
+ // `deleteFromDom` сохранён для совместимости API; ручной z-index в AModalV2 не нужен.
31
26
  </script>
32
27
 
33
28
  <template>
34
- <a-modal
29
+ <a-modal-v2
35
30
  v-model="isOpen"
36
- name="connecting-tariff"
31
+ width="424px"
37
32
  >
38
- <div class="flex flex-col gap-5">
39
- <p class="heading-02 text-center">
40
- {{ t('modals.connecting.title') }}
41
- </p>
42
- <a-ill-turn-on-tariff class="w-full"/>
33
+ <div class="flex flex-col items-center justify-center gap-4 px-6 py-4">
34
+ <a-ill-turn-on-tariff />
43
35
  <p class="body-400 whitespace-pre-wrap text-center">
44
36
  {{ t('modals.connecting.questions') }}
45
37
  </p>
46
38
  </div>
47
39
 
48
40
  <template #footer>
49
- <div class="flex gap-2 ">
50
- <a-button
41
+ <div class="flex w-full gap-2">
42
+ <a-button-v2
51
43
  view="outline"
44
+ color="blue"
52
45
  block
53
46
  @click="cancel"
54
47
  >
55
48
  {{ t('modals.buttons.close') }}
56
- </a-button>
57
- <a-button block @click="confirm">
49
+ </a-button-v2>
50
+ <a-button-v2 block @click="confirm">
58
51
  {{ t('reuse.yes') }}
59
- </a-button>
52
+ </a-button-v2>
60
53
  </div>
61
54
  </template>
62
- </a-modal>
55
+ </a-modal-v2>
63
56
  </template>
64
57
 
65
58
  <style scoped>