adata-ui 2.1.41-beta.12 → 2.1.41-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.
- package/components/forms/input/AInputV2.vue +21 -1
- package/components/forms/input/password/AInputPasswordV2.vue +72 -0
- package/components/forms/password/APasswordRequirements.vue +50 -0
- package/components/modals/id/IdConfirmAccountOtpModal.vue +19 -22
- package/components/modals/id/IdConfirmSuccessfulModal.vue +18 -15
- package/components/modals/id/IdLoginModal.vue +150 -119
- package/components/modals/id/IdModals.vue +66 -24
- package/components/modals/id/IdNewPasswordModal.vue +93 -52
- package/components/modals/id/IdOtpInput.vue +14 -14
- package/components/modals/id/IdPasswordSuccessfulModal.vue +11 -11
- package/components/modals/id/IdRecoveryModal.vue +57 -42
- package/components/modals/id/IdRegistrationModal.vue +136 -94
- package/components/modals/id/IdResetPasswordOtpModal.vue +19 -30
- package/components/modals/id/IdTwoFactorModal.vue +14 -29
- package/icons/google.vue +10 -32
- package/icons/mailru.vue +12 -27
- package/icons/yandex.vue +10 -20
- package/lang/en.ts +30 -4
- package/lang/kk.ts +30 -4
- package/lang/ru.ts +30 -4
- package/package.json +1 -1
- package/utils/apiErrorMessage.ts +22 -0
- package/utils/passwordRules.ts +19 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
3
|
+
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
4
|
+
import AIconLock from '#adata-ui/icons/lock.vue'
|
|
5
|
+
import AIconMail from '#adata-ui/icons/mail.vue'
|
|
3
6
|
import { PAGES } from '#adata-ui/shared/constans/pages'
|
|
7
|
+
import { apiErrorMessage } from '#adata-ui/utils/apiErrorMessage'
|
|
8
|
+
import { PASSWORD_MIN_LENGTH, PASSWORD_RULES } from '#adata-ui/utils/passwordRules'
|
|
4
9
|
import * as z from 'zod'
|
|
5
|
-
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
6
10
|
|
|
7
11
|
const { $toast } = useNuxtApp()
|
|
8
12
|
const route = useRoute()
|
|
@@ -30,18 +34,28 @@ const form = reactive<RegistrationForm>({
|
|
|
30
34
|
const agreement = ref(false)
|
|
31
35
|
const loading = ref(false)
|
|
32
36
|
|
|
37
|
+
const uid = useId()
|
|
38
|
+
const emailFieldId = `register-email-${uid}`
|
|
39
|
+
const passwordFieldId = `register-password-${uid}`
|
|
40
|
+
const confirmationFieldId = `register-password-confirmation-${uid}`
|
|
41
|
+
const agreementLabelId = `register-agreement-${uid}`
|
|
42
|
+
|
|
43
|
+
/* Правила берутся из PASSWORD_RULES — того же источника, что и чеклист
|
|
44
|
+
<a-password-requirements>, поэтому валидация и подсказки не расходятся. */
|
|
45
|
+
function passwordField() {
|
|
46
|
+
return PASSWORD_RULES.reduce(
|
|
47
|
+
(schema: z.ZodType<string>, rule) => schema.refine(
|
|
48
|
+
rule.test,
|
|
49
|
+
t(`modals.id.register.errors.${rule.key}`, { length: PASSWORD_MIN_LENGTH }),
|
|
50
|
+
),
|
|
51
|
+
z.string().nonempty(t('error.required')) as z.ZodType<string>,
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
const registerSchema = z.object({
|
|
34
56
|
email: z.string().nonempty(t('error.required')).email(t('error.email')),
|
|
35
|
-
password:
|
|
36
|
-
|
|
37
|
-
.nonempty(t('error.required'))
|
|
38
|
-
.min(8, t('modals.id.register.errors.low_security', { length: 8 }))
|
|
39
|
-
.regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('modals.id.register.errors.low_security')),
|
|
40
|
-
password_confirmation: z
|
|
41
|
-
.string()
|
|
42
|
-
.nonempty(t('error.required'))
|
|
43
|
-
.min(8, t('modals.id.register.errors.low_security', { length: 8 }))
|
|
44
|
-
.regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('modals.id.register.errors.low_security')),
|
|
57
|
+
password: passwordField(),
|
|
58
|
+
password_confirmation: passwordField(),
|
|
45
59
|
}).refine(
|
|
46
60
|
data => data.password === data.password_confirmation,
|
|
47
61
|
{
|
|
@@ -71,7 +85,7 @@ async function onSubmit() {
|
|
|
71
85
|
|
|
72
86
|
try {
|
|
73
87
|
loading.value = true
|
|
74
|
-
const { success
|
|
88
|
+
const { success } = await $fetch(`${removeTrailingSlash(authApiURL)}/v2/register`, {
|
|
75
89
|
method: 'POST',
|
|
76
90
|
credentials: 'include',
|
|
77
91
|
headers: {
|
|
@@ -88,7 +102,7 @@ async function onSubmit() {
|
|
|
88
102
|
}
|
|
89
103
|
}
|
|
90
104
|
catch (error) {
|
|
91
|
-
$toast.error(error
|
|
105
|
+
$toast.error(apiErrorMessage(error, t('error.validation')))
|
|
92
106
|
}
|
|
93
107
|
finally {
|
|
94
108
|
loading.value = false
|
|
@@ -99,10 +113,6 @@ if (route.query.url) {
|
|
|
99
113
|
redirectUrl.value = route.query.url as string
|
|
100
114
|
}
|
|
101
115
|
|
|
102
|
-
// onMounted(() => {
|
|
103
|
-
// v$.value.$touch();
|
|
104
|
-
// });
|
|
105
|
-
|
|
106
116
|
function getUrl() {
|
|
107
117
|
return navigateTo(locale.value !== 'ru'
|
|
108
118
|
? landing + localePath('/') + PAGES.userAgreement
|
|
@@ -114,12 +124,6 @@ function getUrl() {
|
|
|
114
124
|
})
|
|
115
125
|
}
|
|
116
126
|
|
|
117
|
-
function handleEnter(event: KeyboardEvent) {
|
|
118
|
-
if (event.key === 'Enter') {
|
|
119
|
-
onSubmit()
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
127
|
function onLogin() {
|
|
124
128
|
registrationModal.value = false
|
|
125
129
|
loginModal.value = true
|
|
@@ -133,83 +137,121 @@ watch(registrationModal, (value) => {
|
|
|
133
137
|
agreement.value = false
|
|
134
138
|
}
|
|
135
139
|
})
|
|
136
|
-
|
|
137
|
-
onMounted(() => {
|
|
138
|
-
document.addEventListener('keyup', handleEnter)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
onBeforeUnmount(() => {
|
|
142
|
-
document.removeEventListener('keyup', handleEnter)
|
|
143
|
-
})
|
|
144
140
|
</script>
|
|
145
141
|
|
|
146
142
|
<template>
|
|
143
|
+
<!-- Enter отправляет форму нативно: документного keyup-слушателя здесь быть не должно,
|
|
144
|
+
иначе один Enter даёт два запроса -->
|
|
147
145
|
<form
|
|
148
|
-
class="flex flex-col
|
|
146
|
+
class="flex flex-col gap-6 p-6"
|
|
149
147
|
novalidate
|
|
150
148
|
@submit.prevent="onSubmit"
|
|
151
149
|
>
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
<
|
|
179
|
-
<
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
150
|
+
<div class="flex flex-col gap-1">
|
|
151
|
+
<h2 class="text-center text-h2 text-primary">
|
|
152
|
+
{{ t('modals.id.register.title') }}
|
|
153
|
+
</h2>
|
|
154
|
+
<p class="text-center text-body-md text-secondary">
|
|
155
|
+
{{ t('modals.id.register.subtitle') }}
|
|
156
|
+
</p>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<div class="flex flex-col gap-4">
|
|
160
|
+
<div class="flex flex-col gap-1.5">
|
|
161
|
+
<label :for="emailFieldId" class="text-body-md-strong text-primary">
|
|
162
|
+
{{ t('modals.id.register.labels.email') }}
|
|
163
|
+
</label>
|
|
164
|
+
<a-input-v2
|
|
165
|
+
v-model="form.email"
|
|
166
|
+
:input-id="emailFieldId"
|
|
167
|
+
type="email"
|
|
168
|
+
size="lg"
|
|
169
|
+
autocomplete="email"
|
|
170
|
+
:start-icon="AIconMail"
|
|
171
|
+
:placeholder="t('modals.id.register.placeholders.email')"
|
|
172
|
+
:error="getError('email')"
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="flex flex-col gap-1.5">
|
|
177
|
+
<label :for="passwordFieldId" class="text-body-md-strong text-primary">
|
|
178
|
+
{{ t('modals.id.register.labels.password') }}
|
|
179
|
+
</label>
|
|
180
|
+
<a-input-password-v2
|
|
181
|
+
v-model="form.password"
|
|
182
|
+
:input-id="passwordFieldId"
|
|
183
|
+
size="lg"
|
|
184
|
+
autocomplete="new-password"
|
|
185
|
+
:start-icon="AIconLock"
|
|
186
|
+
:placeholder="t('modals.id.register.placeholders.password')"
|
|
187
|
+
:error="getError('password')"
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<a-password-requirements :password="form.password" />
|
|
192
|
+
|
|
193
|
+
<div class="flex flex-col gap-1.5">
|
|
194
|
+
<label :for="confirmationFieldId" class="text-body-md-strong text-primary">
|
|
195
|
+
{{ t('modals.id.register.labels.password_confirmation') }}
|
|
196
|
+
</label>
|
|
197
|
+
<a-input-password-v2
|
|
198
|
+
v-model="form.password_confirmation"
|
|
199
|
+
:input-id="confirmationFieldId"
|
|
200
|
+
size="lg"
|
|
201
|
+
autocomplete="new-password"
|
|
202
|
+
:start-icon="AIconLock"
|
|
203
|
+
:placeholder="t('modals.id.register.placeholders.password_confirmation')"
|
|
204
|
+
:error="getError('password_confirmation')"
|
|
205
|
+
/>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<!-- Чекбокс и текст рядом, а не вложенно: корень ACheckboxV2 — <button>, а ссылка
|
|
209
|
+
внутри кнопки невалидна; подпись привязана через aria-labelledby -->
|
|
210
|
+
<div class="flex items-start gap-2">
|
|
211
|
+
<a-checkbox-v2
|
|
212
|
+
v-model="agreement"
|
|
213
|
+
name="confirm-agreement"
|
|
214
|
+
:aria-labelledby="agreementLabelId"
|
|
215
|
+
/>
|
|
216
|
+
<p :id="agreementLabelId" class="text-body-md text-primary">
|
|
217
|
+
<i18n-t keypath="modals.id.register.agreement.text">
|
|
218
|
+
<template #link>
|
|
219
|
+
<nuxt-link-locale
|
|
220
|
+
class="text-link underline transition-colors hover:text-link-hover"
|
|
221
|
+
@click="getUrl"
|
|
222
|
+
>
|
|
223
|
+
{{ t('modals.id.register.agreement.link') }}
|
|
224
|
+
</nuxt-link-locale>
|
|
225
|
+
</template>
|
|
226
|
+
</i18n-t>
|
|
227
|
+
</p>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<a-button-v2
|
|
231
|
+
type="submit"
|
|
232
|
+
size="lg"
|
|
233
|
+
block
|
|
234
|
+
:disabled="!agreement"
|
|
235
|
+
:loading="loading"
|
|
236
|
+
>
|
|
237
|
+
{{ t('modals.id.register.continue') }}
|
|
238
|
+
</a-button-v2>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div class="flex flex-col gap-4">
|
|
242
|
+
<span class="h-px w-full bg-(--border-color-default)" />
|
|
243
|
+
<p class="text-center text-body-md text-secondary">
|
|
244
|
+
{{ t('modals.id.register.haveAcc') }}
|
|
245
|
+
</p>
|
|
246
|
+
<a-button-v2
|
|
247
|
+
view="outline"
|
|
248
|
+
color="gray"
|
|
249
|
+
size="lg"
|
|
250
|
+
block
|
|
251
|
+
@click="onLogin"
|
|
252
|
+
>
|
|
253
|
+
{{ t('modals.id.register.enter') }}
|
|
254
|
+
</a-button-v2>
|
|
255
|
+
</div>
|
|
214
256
|
</form>
|
|
215
257
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
3
2
|
import IdOtpInput from '#adata-ui/components/modals/id/IdOtpInput.vue'
|
|
3
|
+
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
4
|
+
import { apiErrorMessage } from '#adata-ui/utils/apiErrorMessage'
|
|
4
5
|
|
|
5
6
|
const { $toast } = useNuxtApp()
|
|
6
7
|
const { t, locale } = useI18n()
|
|
@@ -73,16 +74,13 @@ async function onResend() {
|
|
|
73
74
|
})
|
|
74
75
|
resetOtp()
|
|
75
76
|
runTimer()
|
|
76
|
-
isResendLoading.value = false
|
|
77
77
|
}
|
|
78
78
|
catch (error) {
|
|
79
|
-
$toast.error(error.
|
|
79
|
+
$toast.error(apiErrorMessage(error, t('error.validation')))
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (e.key === 'Enter') {
|
|
85
|
-
onConfirm()
|
|
81
|
+
finally {
|
|
82
|
+
// без finally сбой resend оставлял кнопку заблокированной навсегда
|
|
83
|
+
isResendLoading.value = false
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
86
|
|
|
@@ -98,17 +96,12 @@ function runTimer() {
|
|
|
98
96
|
|
|
99
97
|
onMounted(() => {
|
|
100
98
|
runTimer()
|
|
101
|
-
document.addEventListener('keyup', handleEnter)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
onBeforeUnmount(() => {
|
|
105
|
-
document.removeEventListener('keyup', handleEnter)
|
|
106
99
|
})
|
|
107
100
|
</script>
|
|
108
101
|
|
|
109
102
|
<template>
|
|
110
|
-
<div class="flex flex-col items-center gap-4 text-center">
|
|
111
|
-
<h2 class="text-h2">
|
|
103
|
+
<div class="flex flex-col items-center gap-4 p-6 text-center">
|
|
104
|
+
<h2 class="text-h2 text-primary">
|
|
112
105
|
{{ t('modals.id.resetPasswordOtp.title') }}
|
|
113
106
|
</h2>
|
|
114
107
|
|
|
@@ -116,24 +109,22 @@ onBeforeUnmount(() => {
|
|
|
116
109
|
<a-icon-hand-with-phone-light class="size-32 dark:hidden" />
|
|
117
110
|
<a-icon-hand-with-phone-dark class="hidden size-32 dark:block" />
|
|
118
111
|
|
|
119
|
-
<div class="text-body-md">
|
|
112
|
+
<div class="text-body-md text-secondary">
|
|
120
113
|
<p class="mb-1">
|
|
121
114
|
{{ t('modals.id.resetPasswordOtp.content') }}
|
|
122
115
|
</p>
|
|
123
116
|
|
|
124
|
-
<div v-if="timer > 0" class="text-h2">
|
|
117
|
+
<div v-if="timer > 0" class="text-h2 text-primary">
|
|
125
118
|
{{ timer }} {{ t('modals.id.resetPasswordOtp.seconds') }}
|
|
126
119
|
</div>
|
|
127
120
|
<button
|
|
128
121
|
v-else
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
type="button"
|
|
123
|
+
class="mx-auto flex items-center gap-2 text-body-md text-link transition-colors hover:text-link-hover"
|
|
131
124
|
:disabled="isResendLoading"
|
|
125
|
+
@click="onResend"
|
|
132
126
|
>
|
|
133
|
-
<a-icon-loader-circle
|
|
134
|
-
v-if="isResendLoading"
|
|
135
|
-
class="animate-spin"
|
|
136
|
-
></a-icon-loader-circle>
|
|
127
|
+
<a-icon-loader-circle v-if="isResendLoading" class="size-4 animate-spin" />
|
|
137
128
|
<span>{{ t('actions.resend') }}</span>
|
|
138
129
|
</button>
|
|
139
130
|
</div>
|
|
@@ -144,16 +135,14 @@ onBeforeUnmount(() => {
|
|
|
144
135
|
@on-completed="onConfirm"
|
|
145
136
|
/>
|
|
146
137
|
|
|
147
|
-
<a-button
|
|
148
|
-
block
|
|
138
|
+
<a-button-v2
|
|
149
139
|
view="outline"
|
|
140
|
+
color="gray"
|
|
141
|
+
size="lg"
|
|
142
|
+
block
|
|
150
143
|
@click="onClose"
|
|
151
144
|
>
|
|
152
145
|
{{ t('actions.close') }}
|
|
153
|
-
</a-button>
|
|
146
|
+
</a-button-v2>
|
|
154
147
|
</div>
|
|
155
148
|
</template>
|
|
156
|
-
|
|
157
|
-
<style scoped>
|
|
158
|
-
|
|
159
|
-
</style>
|
|
@@ -12,7 +12,7 @@ const { pk } = useUrls()
|
|
|
12
12
|
const { twoFactorModal, intermediateState } = useIdModals()
|
|
13
13
|
|
|
14
14
|
const authApiURL = commonAuth.authApiURL
|
|
15
|
-
const regex = /^\/counterparty\/main\/company\/\d+\/basic-info
|
|
15
|
+
const regex = /^\/counterparty\/main\/company\/\d+\/basic-info$/
|
|
16
16
|
|
|
17
17
|
const otp = ref(['', '', '', '', '', ''])
|
|
18
18
|
const otpFormatted = computed(() => {
|
|
@@ -62,11 +62,12 @@ async function onConfirm() {
|
|
|
62
62
|
$toast.success(t('login.successfully'))
|
|
63
63
|
twoFactorModal.value = false
|
|
64
64
|
|
|
65
|
-
const path = window.location.pathname
|
|
65
|
+
const path = window.location.pathname
|
|
66
66
|
|
|
67
67
|
if (regex.test(path)) {
|
|
68
|
-
navigateTo(`${pk}/company/${path.split(
|
|
69
|
-
}
|
|
68
|
+
navigateTo(`${pk}/company/${path.split('/')[4]}`, { external: true })
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
70
71
|
window.location.reload()
|
|
71
72
|
}
|
|
72
73
|
}
|
|
@@ -78,27 +79,13 @@ function onClose() {
|
|
|
78
79
|
showError.value = false
|
|
79
80
|
twoFactorModal.value = false
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
-
function handleEnter(e: KeyboardEvent) {
|
|
83
|
-
if (e.key === 'Enter') {
|
|
84
|
-
onConfirm()
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
onMounted(() => {
|
|
89
|
-
document.addEventListener('keyup', handleEnter)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
onBeforeUnmount(() => {
|
|
93
|
-
document.removeEventListener('keyup', handleEnter)
|
|
94
|
-
})
|
|
95
82
|
</script>
|
|
96
83
|
|
|
97
84
|
<template>
|
|
98
|
-
<div class="flex flex-col items-center gap-4">
|
|
99
|
-
<
|
|
85
|
+
<div class="flex flex-col items-center gap-4 p-6">
|
|
86
|
+
<h2 class="text-center text-h2 text-primary">
|
|
100
87
|
{{ t('modals.id.twoFactor.title') }}
|
|
101
|
-
</
|
|
88
|
+
</h2>
|
|
102
89
|
<!-- осознанно константный: пара light/dark-ассетов, тема переключается dark:-классами -->
|
|
103
90
|
<a-icon-hand-with-phone-light class="size-32 dark:hidden" />
|
|
104
91
|
<a-icon-hand-with-phone-dark class="hidden size-32 dark:block" />
|
|
@@ -107,20 +94,18 @@ onBeforeUnmount(() => {
|
|
|
107
94
|
v-model:error="showError"
|
|
108
95
|
@on-completed="onConfirm"
|
|
109
96
|
/>
|
|
110
|
-
<p class="text-body-md text-
|
|
97
|
+
<p class="text-center text-body-md text-secondary">
|
|
111
98
|
{{ t('modals.id.twoFactor.code') }}
|
|
112
99
|
</p>
|
|
113
100
|
|
|
114
|
-
<a-button
|
|
115
|
-
class="w-full"
|
|
101
|
+
<a-button-v2
|
|
116
102
|
view="outline"
|
|
103
|
+
color="gray"
|
|
104
|
+
size="lg"
|
|
105
|
+
block
|
|
117
106
|
@click="onClose"
|
|
118
107
|
>
|
|
119
108
|
{{ t('actions.close') }}
|
|
120
|
-
</a-button>
|
|
109
|
+
</a-button-v2>
|
|
121
110
|
</div>
|
|
122
111
|
</template>
|
|
123
|
-
|
|
124
|
-
<style scoped>
|
|
125
|
-
|
|
126
|
-
</style>
|
package/icons/google.vue
CHANGED
|
@@ -1,41 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
<script lang="ts" setup>
|
|
4
2
|
</script>
|
|
5
3
|
|
|
6
4
|
<template>
|
|
5
|
+
<!-- осознанно константные: фирменные цвета логотипа Google -->
|
|
7
6
|
<svg
|
|
8
|
-
width="
|
|
9
|
-
height="
|
|
10
|
-
viewBox="0 0
|
|
11
|
-
fill="none"
|
|
7
|
+
width="24"
|
|
8
|
+
height="24"
|
|
9
|
+
viewBox="0 0 24 24"
|
|
12
10
|
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
aria-hidden="true"
|
|
12
|
+
focusable="false"
|
|
13
13
|
>
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
height="31"
|
|
19
|
-
rx="5.5"
|
|
20
|
-
stroke="#9DA3AC"
|
|
21
|
-
/>
|
|
22
|
-
<path
|
|
23
|
-
d="M26.501 16.2332C26.501 15.3699 26.4296 14.7399 26.2748 14.0865H16.2153V17.9832H22.12C22.001 18.9515 21.3582 20.4099 19.9296 21.3898L19.9096 21.5203L23.0902 23.935L23.3106 23.9565C25.3343 22.1249 26.501 19.4298 26.501 16.2332Z"
|
|
24
|
-
fill="#4285F4"
|
|
25
|
-
/>
|
|
26
|
-
<path
|
|
27
|
-
d="M16.2147 26.5C19.1075 26.5 21.5361 25.5666 23.3099 23.9566L19.929 21.3898C19.0242 22.0082 17.8099 22.4399 16.2147 22.4399C13.3814 22.4399 10.9767 20.6082 10.1195 18.0765L9.99382 18.087L6.68656 20.5954L6.64331 20.7132C8.40519 24.1432 12.0242 26.5 16.2147 26.5Z"
|
|
28
|
-
fill="#34A853"
|
|
29
|
-
/>
|
|
30
|
-
<path
|
|
31
|
-
d="M10.12 18.0766C9.89379 17.4233 9.76289 16.7232 9.76289 15.9999C9.76289 15.2765 9.89379 14.5766 10.1081 13.9232L10.1021 13.7841L6.75337 11.2355L6.64381 11.2865C5.91765 12.7099 5.50098 14.3083 5.50098 15.9999C5.50098 17.6916 5.91765 19.2899 6.64381 20.7132L10.12 18.0766Z"
|
|
32
|
-
fill="#FBBC05"
|
|
33
|
-
/>
|
|
34
|
-
<path
|
|
35
|
-
d="M16.2148 9.55997C18.2267 9.55997 19.5838 10.4116 20.3576 11.1233L23.3814 8.23C21.5243 6.53834 19.1076 5.5 16.2148 5.5C12.0243 5.5 8.4052 7.85665 6.64331 11.2866L10.1076 13.9233C10.9767 11.3917 13.3815 9.55997 16.2148 9.55997Z"
|
|
36
|
-
fill="#EB4335"
|
|
37
|
-
/>
|
|
14
|
+
<path fill="#4285F4" d="M23.52 12.27c0-.85-.08-1.67-.22-2.45H12v4.64h6.46a5.52 5.52 0 0 1-2.4 3.62v3.01h3.88c2.27-2.09 3.58-5.17 3.58-8.82Z" />
|
|
15
|
+
<path fill="#34A853" d="M12 24c3.24 0 5.96-1.08 7.94-2.91l-3.88-3.01c-1.08.72-2.45 1.15-4.06 1.15-3.12 0-5.77-2.11-6.71-4.95H1.28v3.1A12 12 0 0 0 12 24Z" />
|
|
16
|
+
<path fill="#FBBC05" d="M5.29 14.28a7.2 7.2 0 0 1 0-4.56v-3.1H1.28a12 12 0 0 0 0 10.76l4.01-3.1Z" />
|
|
17
|
+
<path fill="#EA4335" d="M12 4.77c1.76 0 3.35.61 4.6 1.8l3.44-3.44C17.95 1.19 15.24 0 12 0A12 12 0 0 0 1.28 6.62l4.01 3.1C6.23 6.88 8.88 4.77 12 4.77Z" />
|
|
38
18
|
</svg>
|
|
39
19
|
</template>
|
|
40
|
-
|
|
41
|
-
<style scoped></style>
|
package/icons/mailru.vue
CHANGED
|
@@ -1,34 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
|
+
<!-- осознанно константные: фирменные цвета логотипа Mail.ru (белая «@» — декор на синем) -->
|
|
2
3
|
<svg
|
|
3
|
-
width="
|
|
4
|
-
height="
|
|
5
|
-
viewBox="0 0
|
|
6
|
-
fill="none"
|
|
4
|
+
width="24"
|
|
5
|
+
height="24"
|
|
6
|
+
viewBox="0 0 24 24"
|
|
7
7
|
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
aria-hidden="true"
|
|
9
|
+
focusable="false"
|
|
8
10
|
>
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
rx="5.5"
|
|
15
|
-
stroke="#9DA3AC"
|
|
11
|
+
<circle
|
|
12
|
+
cx="12"
|
|
13
|
+
cy="12"
|
|
14
|
+
r="12"
|
|
15
|
+
fill="#0077FF"
|
|
16
16
|
/>
|
|
17
|
-
<
|
|
18
|
-
<path
|
|
19
|
-
d="M19.1585 15.5C19.1585 17.2417 17.7417 18.6585 16 18.6585C14.2583 18.6585 12.8415 17.2417 12.8415 15.5C12.8415 13.7583 14.2583 12.3415 16 12.3415C17.7417 12.3415 19.1585 13.7583 19.1585 15.5ZM16 5C10.2106 5 5.5 9.71056 5.5 15.5C5.5 21.2894 10.2106 26 16 26C18.121 26 20.1665 25.3687 21.9148 24.1737L21.945 24.1527L20.5301 22.5081L20.5065 22.5238C19.1605 23.3894 17.6019 23.8475 16 23.8475C11.3971 23.8475 7.6525 20.1029 7.6525 15.5C7.6525 10.8971 11.3971 7.1525 16 7.1525C20.6029 7.1525 24.3475 10.8971 24.3475 15.5C24.3475 16.0965 24.2812 16.7003 24.1506 17.2942C23.8868 18.3777 23.1282 18.7097 22.5586 18.6657C21.9857 18.6192 21.3156 18.211 21.311 17.2122V16.4509V15.5C21.311 12.5712 18.9288 10.189 16 10.189C13.0712 10.189 10.689 12.5712 10.689 15.5C10.689 18.4288 13.0712 20.811 16 20.811C17.4227 20.811 18.7569 20.2545 19.7629 19.2432C20.3477 20.1535 21.3012 20.7244 22.386 20.811C22.4792 20.8189 22.5743 20.8228 22.6688 20.8228C23.432 20.8228 24.1893 20.5676 24.7997 20.1036C25.429 19.6258 25.8989 18.9348 26.1588 18.1047C26.2001 17.9708 26.2762 17.6637 26.2769 17.6617L26.2788 17.6505C26.4317 16.9844 26.5 16.3203 26.5 15.5C26.5 9.71056 21.7894 5 16 5Z"
|
|
20
|
-
fill="#FF9E00"
|
|
21
|
-
/>
|
|
22
|
-
</g>
|
|
23
|
-
<defs>
|
|
24
|
-
<clipPath id="clip0_680_206687">
|
|
25
|
-
<rect
|
|
26
|
-
width="21"
|
|
27
|
-
height="21"
|
|
28
|
-
fill="white"
|
|
29
|
-
transform="translate(5.5 5)"
|
|
30
|
-
/>
|
|
31
|
-
</clipPath>
|
|
32
|
-
</defs>
|
|
17
|
+
<path fill="#fff" d="M12 5.4a6.6 6.6 0 1 0 3.63 12.11.9.9 0 1 0-.99-1.5A4.8 4.8 0 1 1 16.8 12v.63a1.05 1.05 0 0 1-2.1 0V12a2.7 2.7 0 1 0-.83 1.95 2.85 2.85 0 0 0 4.73-2.13V12A6.6 6.6 0 0 0 12 5.4Zm0 5.7a.9.9 0 1 1 0 1.8.9.9 0 0 1 0-1.8Z" />
|
|
33
18
|
</svg>
|
|
34
19
|
</template>
|
package/icons/yandex.vue
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
|
+
<!-- осознанно константные: фирменные цвета логотипа Яндекса (белая «Я» — декор на красном) -->
|
|
2
3
|
<svg
|
|
3
|
-
width="
|
|
4
|
-
height="
|
|
5
|
-
viewBox="0 0
|
|
6
|
-
fill="none"
|
|
4
|
+
width="24"
|
|
5
|
+
height="24"
|
|
6
|
+
viewBox="0 0 24 24"
|
|
7
7
|
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
aria-hidden="true"
|
|
9
|
+
focusable="false"
|
|
8
10
|
>
|
|
9
|
-
<rect
|
|
10
|
-
class="stroke-icon-secondary"
|
|
11
|
-
x="0.5"
|
|
12
|
-
y="0.5"
|
|
13
|
-
width="31"
|
|
14
|
-
height="31"
|
|
15
|
-
rx="5.5"
|
|
16
|
-
/>
|
|
17
|
-
<!-- осознанно константный: фирменные цвета логотипа Яндекса -->
|
|
18
11
|
<circle
|
|
19
|
-
cx="
|
|
20
|
-
cy="
|
|
21
|
-
r="
|
|
12
|
+
cx="12"
|
|
13
|
+
cy="12"
|
|
14
|
+
r="12"
|
|
22
15
|
fill="#FC3F1D"
|
|
23
16
|
/>
|
|
24
|
-
<path
|
|
25
|
-
d="M19.75 22.75H17.3928V11.076H16.3409C14.4125 11.076 13.3995 12.0469 13.3995 13.4845C13.3995 15.1158 14.1008 15.8736 15.5421 16.845L16.7304 17.6415L13.3019 22.75H10.75L13.8279 18.1853C12.0553 16.9224 11.0618 15.6988 11.0618 13.6206C11.0618 11.0174 12.8736 9.25 16.3215 9.25H19.75V22.75Z"
|
|
26
|
-
fill="white"
|
|
27
|
-
/>
|
|
17
|
+
<path fill="#fff" d="M13.14 19.2h2.2V4.8h-3.2c-3.22 0-5.5 1.97-5.5 4.87 0 2.32 1.1 3.68 3.07 5.08L6.29 19.2h2.39l3.8-5.68-1.11-.74c-1.6-1.08-2.38-1.92-2.38-3.74 0-1.6 1.13-2.68 2.79-2.68h1.36V19.2Z" />
|
|
28
18
|
</svg>
|
|
29
19
|
</template>
|