adata-ui 2.1.41-beta.0 → 2.1.41-beta.10
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.
- package/-error.vue +1 -1
- package/CLAUDE.md +3 -2
- package/assets/styles/main.css +4 -3
- package/assets/styles/theme.css +20 -6
- package/assets/styles/ui.css +77 -0
- package/components/elements/button/AButtonV2.vue +60 -15
- package/components/elements/count-up/ACountUp.vue +54 -0
- package/components/elements/curve-block-v2/ACurveBlockV2.vue +56 -18
- package/components/elements/empty-box/empty-box-V2.vue +1 -1
- package/components/elements/error-template/index.vue +8 -3
- package/components/elements/infinite-carousel/AInfiniteCarousel.vue +46 -8
- package/components/elements/section-heading/ASectionHeading.vue +35 -0
- package/components/elements/select/ACascadeSelectV2.vue +710 -0
- package/components/elements/select/ASelectV2.vue +70 -143
- package/components/elements/select/cascade-utils.ts +115 -0
- package/components/elements/select/types.ts +27 -0
- package/components/elements/select/ui/cascade-drawer.vue +146 -0
- package/components/elements/select/ui/cascade-panel.vue +237 -0
- package/components/elements/select/ui/select-trigger.vue +167 -0
- package/components/elements/step-slider/AStepSliderColCard.vue +75 -42
- package/components/elements/step-slider/AStepSliderColNumber.vue +74 -53
- package/components/features/color-mode/AColorMode.client.vue +1 -23
- package/components/features/go-top/GoTop.vue +4 -0
- package/components/forms/feedback-form/FeedbackFormV2.vue +18 -18
- package/components/forms/input/AInputV2.vue +69 -47
- package/components/forms/input/textarea/ATextareaV2.vue +10 -5
- package/components/modals/ReportBugModal.vue +1 -1
- package/components/modals/id/IdModals.vue +14 -10
- package/components/navigation/bottom-navigation/ABottomNavigation.vue +1 -1
- package/components/navigation/footer/AFooter.vue +2 -2
- package/components/navigation/header/AHeader.vue +8 -4
- package/components/navigation/pill-tabs/APillTabsV2.vue +29 -14
- package/components/overlays/drawer/ADrawerV2.vue +9 -2
- package/components/overlays/modal/AModalV2.vue +1 -0
- package/icons/ai/ai-diamond-unfilled.vue +26 -0
- package/icons/ai/ai-diamond.vue +11 -0
- package/icons/arrow/arrow-down-left.vue +6 -0
- package/icons/arrow/arrow-up-right.vue +6 -0
- package/icons/clock-rotate-ccw.vue +7 -0
- package/icons/crown.vue +6 -0
- package/icons/letter/letter-a.vue +4 -2
- package/icons/letter/letter-b.vue +2 -2
- package/icons/letter/letter-c.vue +2 -2
- package/icons/letter/letter-d.vue +2 -2
- package/icons/letter/letter-e.vue +2 -2
- package/icons/list-checks.vue +3 -0
- package/icons/log-out.vue +21 -0
- package/icons/main-filter.vue +6 -1
- package/icons/refresh-cw.vue +8 -0
- package/icons/shield-check.vue +20 -0
- package/icons/shuffle.vue +3 -0
- package/icons/user-plus.vue +3 -0
- package/icons/wallet.vue +6 -0
- package/illustrations/notes-search.vue +20 -0
- package/illustrations/notes-sticky.vue +20 -0
- package/illustrations/scheme-notes.vue +21 -0
- package/illustrations/scheme-waypoints.vue +25 -0
- package/lang/en.ts +2 -1
- package/lang/kk.ts +3 -2
- package/lang/ru.ts +2 -1
- package/nuxt.config.ts +7 -4
- package/package.json +2 -2
- package/utils/toCssSize.ts +11 -0
|
@@ -99,80 +99,101 @@ watch(() => [props.interval, props.autoplay, props.steps.length], () => {
|
|
|
99
99
|
|
|
100
100
|
<template>
|
|
101
101
|
<div
|
|
102
|
-
class="grid grid-cols-1 items-
|
|
102
|
+
class="grid grid-cols-1 items-start gap-[34px] lg:gap-13"
|
|
103
103
|
:class="colsClass"
|
|
104
104
|
>
|
|
105
|
-
<
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
@click="goTo(i)"
|
|
111
|
-
>
|
|
105
|
+
<div class="flex flex-col">
|
|
106
|
+
<ol class="relative flex flex-col gap-1.5">
|
|
107
|
+
<!-- Одна непрерывная линия с прогрессивной заливкой (как в макете),
|
|
108
|
+
а не дискретные сегменты между шагами. Линия проходит ЗА кружками,
|
|
109
|
+
поэтому их фон обязан быть непрозрачным (см. ниже). -->
|
|
112
110
|
<span
|
|
113
|
-
v-if="
|
|
114
|
-
class="
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
111
|
+
v-if="steps.length > 1"
|
|
112
|
+
class="absolute inset-y-9 left-[31px] z-0 w-0.5 overflow-hidden rounded bg-badge"
|
|
113
|
+
aria-hidden="true"
|
|
114
|
+
>
|
|
115
|
+
<span
|
|
116
|
+
class="ss-line-fill absolute inset-x-0 top-0 rounded bg-brand"
|
|
117
|
+
:style="{ height: `${(active / (steps.length - 1)) * 100}%` }"
|
|
118
|
+
/>
|
|
119
|
+
</span>
|
|
120
|
+
|
|
121
|
+
<li
|
|
122
|
+
v-for="(step, i) in steps"
|
|
123
|
+
:key="i"
|
|
124
|
+
class="ss-step relative z-10 flex cursor-pointer gap-[15px] rounded-14 px-4 py-3.5"
|
|
125
|
+
:class="i === active ? 'ss-step-on bg-main' : ''"
|
|
126
|
+
@click="goTo(i)"
|
|
127
|
+
>
|
|
128
|
+
<!-- Фон кружка только НЕПРОЗРАЧНЫМИ токенами: bg-brand-subtle в тёмной
|
|
129
|
+
теме = #068DFF1A (10% альфы), и линия прогресса просвечивала
|
|
130
|
+
сквозь пройденный шаг. Для дарка берём непрозрачный brand-900. -->
|
|
122
131
|
<span
|
|
123
|
-
class="
|
|
132
|
+
class="relative z-10 flex size-[34px] shrink-0 items-center justify-center rounded-full text-body-md font-bold transition-all duration-300"
|
|
124
133
|
:class="i === active
|
|
125
|
-
? 'ss-num-glow
|
|
134
|
+
? 'ss-num-glow scale-[1.08] bg-brand text-inverted'
|
|
126
135
|
: i < active
|
|
127
|
-
? '
|
|
128
|
-
: '
|
|
136
|
+
? 'bg-brand-100 text-brand dark:bg-brand-900 dark:text-brand-300'
|
|
137
|
+
: 'bg-canvas text-secondary'"
|
|
129
138
|
>
|
|
130
139
|
{{ i + 1 }}
|
|
131
140
|
</span>
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
</li>
|
|
149
|
-
</ol>
|
|
141
|
+
<div>
|
|
142
|
+
<p
|
|
143
|
+
class="text-body-md-strong transition-colors"
|
|
144
|
+
:class="i === active ? 'text-primary' : 'text-secondary'"
|
|
145
|
+
>
|
|
146
|
+
{{ step.title }}
|
|
147
|
+
</p>
|
|
148
|
+
<p class="mt-[3px] text-body-sm leading-normal text-secondary">
|
|
149
|
+
{{ step.description }}
|
|
150
|
+
</p>
|
|
151
|
+
</div>
|
|
152
|
+
</li>
|
|
153
|
+
</ol>
|
|
154
|
+
|
|
155
|
+
<slot name="steps-footer" />
|
|
156
|
+
</div>
|
|
150
157
|
|
|
151
158
|
<div
|
|
152
159
|
@mouseenter="onMouseEnter"
|
|
153
160
|
@mouseleave="onMouseLeave"
|
|
154
161
|
>
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
/>
|
|
165
|
-
</div>
|
|
162
|
+
<slot
|
|
163
|
+
name="panel"
|
|
164
|
+
:active="active"
|
|
165
|
+
:step="steps[active]"
|
|
166
|
+
:progress="progress"
|
|
167
|
+
:count="steps.length"
|
|
168
|
+
:fill-width="fillWidth"
|
|
169
|
+
:go-to="goTo"
|
|
170
|
+
/>
|
|
166
171
|
</div>
|
|
167
172
|
</div>
|
|
168
173
|
</template>
|
|
169
174
|
|
|
170
175
|
<style scoped>
|
|
176
|
+
.ss-line-fill {
|
|
177
|
+
transition: height .6s cubic-bezier(.4, 0, .2, 1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.ss-step {
|
|
181
|
+
background: transparent;
|
|
182
|
+
transition: background .4s, box-shadow .4s;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.ss-step-on {
|
|
186
|
+
box-shadow: 0 14px 34px -18px color-mix(in srgb, var(--color-brand-500) 30%, transparent);
|
|
187
|
+
}
|
|
188
|
+
|
|
171
189
|
.ss-num-glow {
|
|
172
|
-
box-shadow: 0 4px
|
|
190
|
+
box-shadow: 0 6px 16px -4px color-mix(in srgb, var(--color-brand-500) 55%, transparent);
|
|
173
191
|
}
|
|
174
192
|
|
|
175
|
-
|
|
176
|
-
|
|
193
|
+
@media (prefers-reduced-motion: reduce) {
|
|
194
|
+
.ss-line-fill,
|
|
195
|
+
.ss-step {
|
|
196
|
+
transition: none;
|
|
197
|
+
}
|
|
177
198
|
}
|
|
178
199
|
</style>
|
|
@@ -1,36 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import Moon from '#adata-ui/icons/moon.vue'
|
|
3
3
|
import Sun from '#adata-ui/icons/sun.vue'
|
|
4
|
-
import { useEventListener } from '@vueuse/core'
|
|
5
4
|
|
|
6
5
|
const colorMode = useColorMode()
|
|
7
|
-
const setColorMode = useCookie('colorMode')
|
|
8
|
-
|
|
9
|
-
if (setColorMode.value) {
|
|
10
|
-
colorMode.preference = setColorMode.value
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const ONE_YEAR_SECONDS = 60 * 60 * 24 * 365
|
|
14
|
-
|
|
15
|
-
function cookieDomainAttr(): string {
|
|
16
|
-
const parts = location.hostname.split('.').reverse()
|
|
17
|
-
if (parts.length < 2) return ''
|
|
18
|
-
return `; domain=.${parts[1]}.${parts[0]}`
|
|
19
|
-
}
|
|
20
6
|
|
|
21
7
|
const isDark = computed(() => colorMode.value === 'dark')
|
|
22
8
|
|
|
23
9
|
function toggle() {
|
|
24
|
-
|
|
25
|
-
colorMode.preference = value
|
|
26
|
-
document.cookie = `colorMode=${value}; max-age=${ONE_YEAR_SECONDS}${cookieDomainAttr()}; path=/`
|
|
10
|
+
colorMode.preference = isDark.value ? 'light' : 'dark'
|
|
27
11
|
}
|
|
28
|
-
|
|
29
|
-
useEventListener(window, 'storage', (event: StorageEvent) => {
|
|
30
|
-
if (event.key === 'nuxt-color-mode' && event.newValue) {
|
|
31
|
-
colorMode.preference = event.newValue
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
12
|
</script>
|
|
35
13
|
|
|
36
14
|
<template>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { AIconChevronUp } from "#components";
|
|
3
3
|
|
|
4
|
+
const { t } = useI18n()
|
|
5
|
+
|
|
4
6
|
const props = defineProps<{
|
|
5
7
|
bottomOffset?: number
|
|
6
8
|
}>()
|
|
@@ -49,9 +51,11 @@ onUnmounted(() => {
|
|
|
49
51
|
:class="[{ 'opacity-0': !isShow }]"
|
|
50
52
|
:style="{ bottom: footerHeight + 'px' }"
|
|
51
53
|
>
|
|
54
|
+
<!-- Кнопка только с иконкой: без aria-label скринридер читает её как "button". -->
|
|
52
55
|
<a-button
|
|
53
56
|
@click="goTop"
|
|
54
57
|
form="icon"
|
|
58
|
+
:aria-label="t('reuse.goTop')"
|
|
55
59
|
:icon="AIconChevronUp"
|
|
56
60
|
icon-class="h-6 w-6"
|
|
57
61
|
size="lg"
|
|
@@ -5,6 +5,8 @@ import MessageSquare from '#adata-ui/icons/message/message-square.vue'
|
|
|
5
5
|
import AInputV2 from '#adata-ui/components/forms/input/AInputV2.vue'
|
|
6
6
|
import ATextareaV2 from '#adata-ui/components/forms/input/textarea/ATextareaV2.vue'
|
|
7
7
|
import AButtonV2 from '#adata-ui/components/elements/button/AButtonV2.vue'
|
|
8
|
+
import { buildLocalizedUrl } from '#adata-ui/utils/localizedNavigation'
|
|
9
|
+
import { PAGES } from '#adata-ui/shared/constans/pages'
|
|
8
10
|
|
|
9
11
|
const props = withDefaults(defineProps<{
|
|
10
12
|
userInfo: any
|
|
@@ -16,24 +18,22 @@ const props = withDefaults(defineProps<{
|
|
|
16
18
|
})
|
|
17
19
|
|
|
18
20
|
const emit = defineEmits(['submit'])
|
|
19
|
-
|
|
21
|
+
const { locale } = useI18n()
|
|
20
22
|
const { t } = useI18n()
|
|
21
|
-
|
|
22
|
-
const isLoading = defineModel<
|
|
23
|
-
const isModalOpen = defineModel<
|
|
24
|
-
|
|
23
|
+
const { landing } = useUrls()
|
|
24
|
+
const isLoading = defineModel<null | any>('isLoading', { default: false })
|
|
25
|
+
const isModalOpen = defineModel<null | any>('isModalOpen', { default: false })
|
|
25
26
|
const fullName = ref<string>('')
|
|
26
27
|
const phone = ref<string>('')
|
|
27
28
|
const mail = ref<string>('')
|
|
28
29
|
const comment = ref<string>('')
|
|
29
|
-
|
|
30
30
|
const submitted = ref(false)
|
|
31
31
|
|
|
32
32
|
const scheme = z.object({
|
|
33
|
-
fullName: z.string()
|
|
33
|
+
fullName: z.string(),
|
|
34
34
|
phone: z.string().nonempty(t('forms.feedback.fieldRequired')).min(17, t('forms.feedback.phoneMinLength', { count: 10 })),
|
|
35
|
-
mail: z.string()
|
|
36
|
-
comment: z.string().
|
|
35
|
+
mail: z.string(),
|
|
36
|
+
comment: z.string().max(250, t('forms.feedback.commentMaxLength', { count: 250 })),
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
const { validation, getError } = useUIValidation(
|
|
@@ -58,7 +58,6 @@ const send = () => {
|
|
|
58
58
|
mail.value = props.userInfo?.email || ''
|
|
59
59
|
comment.value = ''
|
|
60
60
|
}
|
|
61
|
-
|
|
62
61
|
defineExpose({
|
|
63
62
|
reset: () => (submitted.value = false),
|
|
64
63
|
})
|
|
@@ -70,10 +69,10 @@ onMounted(() => {
|
|
|
70
69
|
|
|
71
70
|
<template>
|
|
72
71
|
<section class="feedback-v2">
|
|
73
|
-
<!--
|
|
74
|
-
<div class="grid grid-cols-1 items-center gap-8 rounded-[28px] border border-
|
|
72
|
+
<!-- градиент промо-карточки — осознанный литерал (§05 аудита), но флипается в тёмной теме -->
|
|
73
|
+
<div class="grid grid-cols-1 items-center gap-8 rounded-[28px] border border-default bg-linear-to-br from-[#f0f8ff] via-[#eef4ff] to-[#f0f8ff] p-4 md:gap-10 md:p-12 lg:grid-cols-2 dark:from-[#1a2231] dark:via-[#15191f] dark:to-[#121316]">
|
|
75
74
|
<div>
|
|
76
|
-
<div class="mb-4 inline-flex items-center gap-2 rounded-full border border-
|
|
75
|
+
<div class="mb-4 inline-flex items-center gap-2 rounded-full border border-default bg-main h-8 px-3 text-body-md text-brand">
|
|
77
76
|
<message-square class="size-3.5" />
|
|
78
77
|
{{ t('forms.feedback.value') }}
|
|
79
78
|
</div>
|
|
@@ -99,7 +98,6 @@ onMounted(() => {
|
|
|
99
98
|
v-model="fullName"
|
|
100
99
|
variant="gray"
|
|
101
100
|
size="md"
|
|
102
|
-
required
|
|
103
101
|
:label="t('forms.demo.n')"
|
|
104
102
|
:error="getError('fullName')"
|
|
105
103
|
:disabled="isLoading"
|
|
@@ -121,7 +119,6 @@ onMounted(() => {
|
|
|
121
119
|
type="email"
|
|
122
120
|
variant="gray"
|
|
123
121
|
size="md"
|
|
124
|
-
required
|
|
125
122
|
:label="t('forms.feedback.email')"
|
|
126
123
|
:error="getError('mail')"
|
|
127
124
|
:disabled="isLoading"
|
|
@@ -136,7 +133,6 @@ onMounted(() => {
|
|
|
136
133
|
:label="t('forms.feedback.comment')"
|
|
137
134
|
:error="getError('comment')"
|
|
138
135
|
:disabled="isLoading"
|
|
139
|
-
required
|
|
140
136
|
/>
|
|
141
137
|
<a-button-v2
|
|
142
138
|
type="submit"
|
|
@@ -149,9 +145,13 @@ onMounted(() => {
|
|
|
149
145
|
</a-button-v2>
|
|
150
146
|
<p class="mt-1 text-center text-body-sm text-disabled">
|
|
151
147
|
{{ t('forms.feedback.consent') }}
|
|
152
|
-
<
|
|
148
|
+
<nuxt-link-locale
|
|
149
|
+
class="cursor-pointer text-brand"
|
|
150
|
+
:to="buildLocalizedUrl(locale, landing, PAGES.privacy)"
|
|
151
|
+
target="_blank"
|
|
152
|
+
>
|
|
153
153
|
{{ t('forms.feedback.consentAccent') }}
|
|
154
|
-
</
|
|
154
|
+
</nuxt-link-locale>
|
|
155
155
|
</p>
|
|
156
156
|
</form>
|
|
157
157
|
</div>
|
|
@@ -19,6 +19,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
19
19
|
loading: false,
|
|
20
20
|
variant: 'default',
|
|
21
21
|
mask: undefined,
|
|
22
|
+
dropdownStrategy: 'absolute',
|
|
22
23
|
autocompleteList: () => [] as T[],
|
|
23
24
|
autocompleteKey: undefined,
|
|
24
25
|
autocompleteOption: undefined,
|
|
@@ -43,13 +44,15 @@ interface Props {
|
|
|
43
44
|
disabled?: boolean
|
|
44
45
|
readonly?: boolean
|
|
45
46
|
clearable?: boolean
|
|
46
|
-
size?: 'sm' | 'md'
|
|
47
|
+
size?: 'sm' | 'md' | 'lg'
|
|
47
48
|
error?: string | string[]
|
|
48
49
|
startIcon?: Component
|
|
50
|
+
startIconClass?: string
|
|
49
51
|
endIcon?: Component
|
|
50
52
|
loading?: boolean
|
|
51
|
-
variant?: 'default' | 'gray' | 'blue'
|
|
53
|
+
variant?: 'default' | 'gray' | 'blue' | 'canvas'
|
|
52
54
|
mask?: string
|
|
55
|
+
dropdownStrategy?: 'absolute' | 'fixed'
|
|
53
56
|
inputId?: string
|
|
54
57
|
autocomplete?: string
|
|
55
58
|
autocompleteList?: T[]
|
|
@@ -104,7 +107,7 @@ const fieldRef = ref<HTMLElement | null>(null)
|
|
|
104
107
|
const floatingRef = ref<HTMLElement | null>(null)
|
|
105
108
|
const { x, y, strategy, update } = useFloating(fieldRef, floatingRef, {
|
|
106
109
|
placement: 'bottom-start',
|
|
107
|
-
strategy:
|
|
110
|
+
strategy: computed(() => props.dropdownStrategy),
|
|
108
111
|
middleware: [
|
|
109
112
|
offset(6),
|
|
110
113
|
flip({ fallbackPlacements: ['top-start'] }),
|
|
@@ -135,7 +138,7 @@ onUnmounted(() => cleanup?.())
|
|
|
135
138
|
const wrapperRef = ref<HTMLDivElement | null>(null)
|
|
136
139
|
onClickOutside(wrapperRef, () => {
|
|
137
140
|
if (isFocused.value) inputRef.value?.blur()
|
|
138
|
-
})
|
|
141
|
+
}, { ignore: [floatingRef] })
|
|
139
142
|
|
|
140
143
|
function resolveSelectedValue(item: T): AutocompleteValue {
|
|
141
144
|
if (props.autocompleteFn) return props.autocompleteFn(item) as AutocompleteValue
|
|
@@ -255,13 +258,19 @@ defineExpose({ focus, blur, inputRef })
|
|
|
255
258
|
|
|
256
259
|
const layoutClass = computed(() => {
|
|
257
260
|
if (!props.label) {
|
|
261
|
+
if (props.size === 'lg') return 'h-11 py-2'
|
|
258
262
|
return props.size === 'md' ? 'h-10 py-1.5' : 'h-9 py-1.5'
|
|
259
263
|
}
|
|
264
|
+
if (props.size === 'lg') return 'h-11 pt-4 pb-1'
|
|
260
265
|
return props.size === 'md' ? 'h-10 pt-4 pb-0.5' : 'h-9 pt-3 pb-0.5'
|
|
261
266
|
})
|
|
262
267
|
|
|
268
|
+
/* Компенсация асимметричных паддингов поля с label: иконки возвращаются к
|
|
269
|
+
визуальному центру. Величина = (pt + контент/2) - h/2:
|
|
270
|
+
sm 12+11-18=5, md 16+11-20=7, lg 16+12-22=6. */
|
|
263
271
|
const iconOffsetClass = computed(() => {
|
|
264
272
|
if (!props.label) return ''
|
|
273
|
+
if (props.size === 'lg') return '-translate-y-[6px]'
|
|
265
274
|
return props.size === 'md' ? '-translate-y-[7px]' : '-translate-y-[5px]'
|
|
266
275
|
})
|
|
267
276
|
|
|
@@ -286,11 +295,16 @@ const fieldClass = computed(() => {
|
|
|
286
295
|
return 'border-default bg-badge/70'
|
|
287
296
|
}
|
|
288
297
|
if (props.variant === 'gray') {
|
|
289
|
-
|
|
298
|
+
/* по макету ДС-2026 у залитого поля бордер видимый, а не прозрачный */
|
|
299
|
+
return 'border-default bg-muted'
|
|
290
300
|
}
|
|
291
301
|
if (props.variant === 'blue') {
|
|
292
302
|
return 'border-brand-200 bg-brand-subtle'
|
|
293
303
|
}
|
|
304
|
+
if (props.variant === 'canvas') {
|
|
305
|
+
/* «утопленная» зона ДС + hero-поиск лендинга: bg-canvas глубже bg-muted в дарке */
|
|
306
|
+
return 'border-default bg-canvas'
|
|
307
|
+
}
|
|
294
308
|
return 'border-default bg-main'
|
|
295
309
|
})
|
|
296
310
|
|
|
@@ -308,7 +322,7 @@ function rowDisplay(item: T): unknown {
|
|
|
308
322
|
>
|
|
309
323
|
<div
|
|
310
324
|
ref="fieldRef"
|
|
311
|
-
class="input-v2__field
|
|
325
|
+
class="input-v2__field relative flex w-full cursor-text items-center gap-2 rounded-10 border border-solid text-primary transition-colors duration-200 focus-within:border-focus focus-within:ring-2 focus-within:ring-brand-500/20 hover:border-brand"
|
|
312
326
|
:class="[
|
|
313
327
|
layoutClass,
|
|
314
328
|
fieldClass,
|
|
@@ -331,11 +345,13 @@ function rowDisplay(item: T): unknown {
|
|
|
331
345
|
{{ label }}<span v-if="required" class="text-danger"> *</span>
|
|
332
346
|
</label>
|
|
333
347
|
|
|
348
|
+
<!-- startIconClass ЗАМЕНЯЕТ дефолтные размер+цвет, а не дописывается:
|
|
349
|
+
конфликт одинаковых утилит решается порядком в stylesheet, не в class -->
|
|
334
350
|
<component
|
|
335
351
|
:is="startIcon"
|
|
336
352
|
v-if="startIcon"
|
|
337
|
-
class="
|
|
338
|
-
:class="iconOffsetClass"
|
|
353
|
+
class="shrink-0"
|
|
354
|
+
:class="[startIconClass || 'size-4 text-icon-disabled', iconOffsetClass]"
|
|
339
355
|
/>
|
|
340
356
|
|
|
341
357
|
<input
|
|
@@ -348,7 +364,7 @@ function rowDisplay(item: T): unknown {
|
|
|
348
364
|
:readonly="readonly"
|
|
349
365
|
:autocomplete="autocomplete"
|
|
350
366
|
:tabindex="disabled ? -1 : 0"
|
|
351
|
-
class="input-v2__input
|
|
367
|
+
class="input-v2__input w-full min-w-0 bg-transparent leading-tight text-primary outline-hidden placeholder:text-secondary disabled:cursor-not-allowed"
|
|
352
368
|
v-bind="inputAttrs"
|
|
353
369
|
@input="onInput"
|
|
354
370
|
@focus="onFocus"
|
|
@@ -371,7 +387,7 @@ function rowDisplay(item: T): unknown {
|
|
|
371
387
|
@mousedown.prevent
|
|
372
388
|
@click.stop="onClear"
|
|
373
389
|
>
|
|
374
|
-
<a-icon-x-mark class="
|
|
390
|
+
<a-icon-x-mark class="m-0! size-3.5" />
|
|
375
391
|
</button>
|
|
376
392
|
|
|
377
393
|
<component
|
|
@@ -384,43 +400,45 @@ function rowDisplay(item: T): unknown {
|
|
|
384
400
|
<slot name="endButton" />
|
|
385
401
|
</div>
|
|
386
402
|
|
|
387
|
-
<
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
<div
|
|
396
|
-
v-if="isDropdownOpen"
|
|
397
|
-
ref="floatingRef"
|
|
398
|
-
class="input-v2__dropdown z-[10000] flex flex-col gap-2 rounded-lg border border-default bg-main p-1.5 shadow-lg shadow-surface-dark/10 dark:shadow-surface-dark/30"
|
|
399
|
-
:style="{
|
|
400
|
-
position: strategy,
|
|
401
|
-
left: x != null ? `${x}px` : '',
|
|
402
|
-
top: y != null ? `${y}px` : '',
|
|
403
|
-
}"
|
|
403
|
+
<teleport to="body">
|
|
404
|
+
<transition
|
|
405
|
+
enter-active-class="input-v2__dropdown-enter-active"
|
|
406
|
+
enter-from-class="input-v2__dropdown-enter-from"
|
|
407
|
+
enter-to-class="input-v2__dropdown-enter-to"
|
|
408
|
+
leave-active-class="input-v2__dropdown-leave-active"
|
|
409
|
+
leave-from-class="input-v2__dropdown-leave-from"
|
|
410
|
+
leave-to-class="input-v2__dropdown-leave-to"
|
|
404
411
|
>
|
|
405
|
-
<
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
:
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
412
|
+
<div
|
|
413
|
+
v-if="isDropdownOpen"
|
|
414
|
+
ref="floatingRef"
|
|
415
|
+
class="input-v2__dropdown z-10001 flex flex-col gap-2 rounded-xl border border-default bg-main p-1.5 shadow-[0_24px_50px_-24px] shadow-brand-500/50"
|
|
416
|
+
:style="{
|
|
417
|
+
position: strategy,
|
|
418
|
+
left: x != null ? `${x}px` : '',
|
|
419
|
+
top: y != null ? `${y}px` : '',
|
|
420
|
+
}"
|
|
421
|
+
>
|
|
422
|
+
<ul ref="autocompleteListEl" class="input-v2__list flex flex-col">
|
|
423
|
+
<li
|
|
424
|
+
v-for="(item, idx) in autocompleteList"
|
|
425
|
+
:key="idx"
|
|
426
|
+
class="cursor-pointer rounded-lg px-3.5 py-2.5 text-body-md transition-colors duration-100 hover:bg-brand-subtle"
|
|
427
|
+
:class="{ 'input-v2__option--active': currentItemIndex === idx }"
|
|
428
|
+
@mousedown.prevent="onSelectOption(item)"
|
|
429
|
+
@touchend.prevent="onTouchEndOption(item)"
|
|
430
|
+
@touchmove="isTouchScrolling = true"
|
|
431
|
+
>
|
|
432
|
+
<slot name="autocomplete-option" :item="item">
|
|
433
|
+
{{ rowDisplay(item) }}
|
|
434
|
+
</slot>
|
|
435
|
+
</li>
|
|
436
|
+
</ul>
|
|
437
|
+
|
|
438
|
+
<slot name="autocomplete-buttons" :value="modelValue" />
|
|
439
|
+
</div>
|
|
440
|
+
</transition>
|
|
441
|
+
</teleport>
|
|
424
442
|
|
|
425
443
|
<transition
|
|
426
444
|
enter-active-class="input-v2__error-enter-active"
|
|
@@ -471,6 +489,10 @@ function rowDisplay(item: T): unknown {
|
|
|
471
489
|
transform: translateY(-14px);
|
|
472
490
|
}
|
|
473
491
|
|
|
492
|
+
.input-v2__label--floated[data-size="lg"] {
|
|
493
|
+
transform: translateY(-19px);
|
|
494
|
+
}
|
|
495
|
+
|
|
474
496
|
.input-v2__dropdown-enter-active,
|
|
475
497
|
.input-v2__dropdown-leave-active {
|
|
476
498
|
transition:
|
|
@@ -491,7 +513,7 @@ function rowDisplay(item: T): unknown {
|
|
|
491
513
|
}
|
|
492
514
|
|
|
493
515
|
.input-v2__list {
|
|
494
|
-
|
|
516
|
+
min-height: 0;
|
|
495
517
|
overflow-y: auto;
|
|
496
518
|
overflow-x: hidden;
|
|
497
519
|
scrollbar-width: none;
|
|
@@ -30,7 +30,7 @@ interface Props {
|
|
|
30
30
|
readonly?: boolean
|
|
31
31
|
error?: string | string[]
|
|
32
32
|
size?: 'sm' | 'md'
|
|
33
|
-
variant?: 'default' | 'gray' | 'blue'
|
|
33
|
+
variant?: 'default' | 'gray' | 'blue' | 'canvas'
|
|
34
34
|
rows?: number
|
|
35
35
|
resize?: 'none' | 'vertical' | 'both'
|
|
36
36
|
maxlength?: number
|
|
@@ -60,11 +60,16 @@ const fieldClass = computed(() => {
|
|
|
60
60
|
return 'border-default bg-badge/70'
|
|
61
61
|
}
|
|
62
62
|
if (props.variant === 'gray') {
|
|
63
|
-
|
|
63
|
+
/* по макету ДС-2026 у залитого поля бордер видимый, а не прозрачный */
|
|
64
|
+
return 'border-default bg-muted'
|
|
64
65
|
}
|
|
65
66
|
if (props.variant === 'blue') {
|
|
66
67
|
return 'border-brand-200 bg-brand-subtle'
|
|
67
68
|
}
|
|
69
|
+
if (props.variant === 'canvas') {
|
|
70
|
+
/* «утопленная» зона ДС: bg-canvas глубже bg-muted в дарке (парно с AInputV2) */
|
|
71
|
+
return 'border-default bg-canvas'
|
|
72
|
+
}
|
|
68
73
|
return 'border-default bg-main'
|
|
69
74
|
})
|
|
70
75
|
|
|
@@ -124,7 +129,7 @@ defineExpose({ focus, blur, textareaRef })
|
|
|
124
129
|
<template>
|
|
125
130
|
<div class="textarea-v2 relative w-full text-body-md" v-bind="wrapperAttrs">
|
|
126
131
|
<div
|
|
127
|
-
class="textarea-v2__field relative rounded-
|
|
132
|
+
class="textarea-v2__field relative rounded-10 border border-solid transition-colors duration-200 focus-within:border-focus focus-within:ring-2 focus-within:ring-brand-500/20 hover:border-brand"
|
|
128
133
|
:class="[fieldClass, { 'textarea-v2__field--error': hasError, 'textarea-v2__field--disabled': disabled }]"
|
|
129
134
|
>
|
|
130
135
|
<label
|
|
@@ -147,7 +152,7 @@ defineExpose({ focus, blur, textareaRef })
|
|
|
147
152
|
:disabled="disabled"
|
|
148
153
|
:readonly="readonly"
|
|
149
154
|
:tabindex="disabled ? -1 : 0"
|
|
150
|
-
class="textarea-v2__input
|
|
155
|
+
class="textarea-v2__input block w-full bg-transparent leading-snug text-primary outline-hidden placeholder:text-secondary disabled:cursor-not-allowed"
|
|
151
156
|
:class="[resizeClass, paddingClass]"
|
|
152
157
|
v-bind="inputAttrs"
|
|
153
158
|
@focus="onFocus"
|
|
@@ -156,7 +161,7 @@ defineExpose({ focus, blur, textareaRef })
|
|
|
156
161
|
|
|
157
162
|
<span
|
|
158
163
|
v-if="maxlength"
|
|
159
|
-
class="textarea-v2__counter pointer-events-none absolute bottom-1.5
|
|
164
|
+
class="textarea-v2__counter pointer-events-none absolute right-3 bottom-1.5 text-body-sm text-icon-disabled"
|
|
160
165
|
>
|
|
161
166
|
{{ modelValue?.length ?? 0 }}/{{ maxlength }}
|
|
162
167
|
</span>
|
|
@@ -100,7 +100,7 @@ async function send() {
|
|
|
100
100
|
|
|
101
101
|
<div class="flex flex-col gap-5 px-6 py-4">
|
|
102
102
|
<div class="flex flex-col items-center justify-center gap-4">
|
|
103
|
-
<a-ill-
|
|
103
|
+
<a-ill-mail-active class="size-[140px]" />
|
|
104
104
|
<slot name="title">
|
|
105
105
|
<p class="text-body-md text-center">
|
|
106
106
|
{{ name }}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import IdAutoLogoutModal from '#adata-ui/components/modals/id/IdAutoLogoutModal.vue'
|
|
3
|
-
import IdConfirmAccountOtpModal from '#adata-ui/components/modals/id/IdConfirmAccountOtpModal.vue'
|
|
4
|
-
import IdConfirmSuccessfulModal from '#adata-ui/components/modals/id/IdConfirmSuccessfulModal.vue'
|
|
5
|
-
import IdLoginModal from '#adata-ui/components/modals/id/IdLoginModal.vue'
|
|
6
|
-
import IdNewPasswordModal from '#adata-ui/components/modals/id/IdNewPasswordModal.vue'
|
|
7
|
-
import IdPasswordSuccessfulModal from '#adata-ui/components/modals/id/IdPasswordSuccessfulModal.vue'
|
|
8
|
-
import IdRecoveryModal from '#adata-ui/components/modals/id/IdRecoveryModal.vue'
|
|
9
|
-
import IdRegistrationModal from '#adata-ui/components/modals/id/IdRegistrationModal.vue'
|
|
10
|
-
import IdResetPasswordOtpModal from '#adata-ui/components/modals/id/IdResetPasswordOtpModal.vue'
|
|
11
|
-
import IdTwoFactorModal from '#adata-ui/components/modals/id/IdTwoFactorModal.vue'
|
|
12
2
|
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
13
3
|
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
4
|
+
// Каждое из этих окон рендерится только под своим v-if, но статический import
|
|
5
|
+
// всё равно тащил все десять (плюс zod, который они используют для валидации)
|
|
6
|
+
// в стартовый чанк каждого приложения-потребителя. defineAsyncComponent грузит
|
|
7
|
+
// чанк в момент открытия окна: поведение то же, стартовый бандл меньше.
|
|
8
|
+
const IdAutoLogoutModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdAutoLogoutModal.vue'))
|
|
9
|
+
const IdConfirmAccountOtpModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdConfirmAccountOtpModal.vue'))
|
|
10
|
+
const IdConfirmSuccessfulModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdConfirmSuccessfulModal.vue'))
|
|
11
|
+
const IdLoginModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdLoginModal.vue'))
|
|
12
|
+
const IdNewPasswordModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdNewPasswordModal.vue'))
|
|
13
|
+
const IdPasswordSuccessfulModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdPasswordSuccessfulModal.vue'))
|
|
14
|
+
const IdRecoveryModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdRecoveryModal.vue'))
|
|
15
|
+
const IdRegistrationModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdRegistrationModal.vue'))
|
|
16
|
+
const IdResetPasswordOtpModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdResetPasswordOtpModal.vue'))
|
|
17
|
+
const IdTwoFactorModal = defineAsyncComponent(() => import('#adata-ui/components/modals/id/IdTwoFactorModal.vue'))
|
|
14
18
|
|
|
15
19
|
const emit = defineEmits<{
|
|
16
20
|
(e: 'closed', value: boolean): void
|
|
@@ -56,7 +56,7 @@ const profile: string = 'modals.mobile_navigation.type_profile'
|
|
|
56
56
|
</script>
|
|
57
57
|
|
|
58
58
|
<template>
|
|
59
|
-
<header class="mx-3 mb-3 overflow-hidden rounded-full border border-default bg-
|
|
59
|
+
<header class="mx-3 mb-3 overflow-hidden rounded-full border border-default bg-main/10 shadow-lg backdrop-blur-lg backdrop-saturate-140">
|
|
60
60
|
<div class="grid w-full grid-cols-3 justify-items-center">
|
|
61
61
|
<slot>
|
|
62
62
|
<button
|