adata-ui 2.1.40-beta → 2.1.40-beta.1
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.
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { PAGES } from '#adata-ui/shared/constans/pages'
|
|
3
2
|
import { usePkServicesLinks } from '#adata-ui/composables/useHeaderNavigationLinks'
|
|
4
|
-
import {
|
|
3
|
+
import { useActiveNavigation } from '#adata-ui/composables/useActiveNavigation'
|
|
5
4
|
import { NuxtLinkLocale } from '#components'
|
|
6
5
|
|
|
7
6
|
const services = usePkServicesLinks()
|
|
8
|
-
const
|
|
9
|
-
const localePath = useLocalePath()
|
|
10
|
-
const { landing } = useUrls()
|
|
11
|
-
const { locale } = useI18n()
|
|
12
|
-
|
|
13
|
-
const pageUrl = useRequestURL()
|
|
7
|
+
const { isActiveService } = useActiveNavigation()
|
|
14
8
|
|
|
15
9
|
const blockStyles = [
|
|
16
10
|
'first-border-gradient',
|
|
@@ -23,22 +17,6 @@ const blockStyles = [
|
|
|
23
17
|
'eighth-border-gradient',
|
|
24
18
|
'ninth-border-gradient',
|
|
25
19
|
]
|
|
26
|
-
const linkByIndex = [
|
|
27
|
-
PAGES.pk.main,
|
|
28
|
-
PAGES.pk.employees,
|
|
29
|
-
PAGES.pk.connections,
|
|
30
|
-
PAGES.pk.offshore,
|
|
31
|
-
PAGES.pk.foreign,
|
|
32
|
-
PAGES.pk.unload,
|
|
33
|
-
PAGES.pk.compare,
|
|
34
|
-
PAGES.pk.sanctions,
|
|
35
|
-
buildLocalizedUrl(locale, landing, '/all-services'),
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
const normalize = (path: string) => {
|
|
39
|
-
const cleaned = path.replace(/\/+$/, '')
|
|
40
|
-
return cleaned === '' ? '/' : cleaned
|
|
41
|
-
}
|
|
42
20
|
</script>
|
|
43
21
|
|
|
44
22
|
<template>
|
|
@@ -46,15 +24,15 @@ const normalize = (path: string) => {
|
|
|
46
24
|
<component
|
|
47
25
|
v-for="(service, index) in services"
|
|
48
26
|
:key="index"
|
|
49
|
-
:is="
|
|
50
|
-
:to="
|
|
27
|
+
:is="isActiveService(service.to) ? 'div' : NuxtLinkLocale"
|
|
28
|
+
:to="isActiveService(service.to) ? '' : service.to"
|
|
51
29
|
:class="['flex flex-col items-center gap-2 p-2', blockStyles[index]]"
|
|
52
30
|
>
|
|
53
31
|
<div
|
|
54
32
|
class="size-10 p-2 rounded-lg"
|
|
55
33
|
:class="[
|
|
56
34
|
'bg-deepblue-900/5 dark:bg-gray-200/5',
|
|
57
|
-
{'!bg-blue-700 text-white dark:!bg-blue-500 ':
|
|
35
|
+
{'!bg-blue-700 text-white dark:!bg-blue-500 ': isActiveService(service.to)}
|
|
58
36
|
]"
|
|
59
37
|
>
|
|
60
38
|
<component
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { useActiveNavigation } from '#adata-ui/composables/useActiveNavigation'
|
|
3
3
|
|
|
4
4
|
interface NavList {
|
|
5
5
|
title: string
|
|
@@ -18,61 +18,28 @@ const props = defineProps<{
|
|
|
18
18
|
currentModule: boolean
|
|
19
19
|
}>()
|
|
20
20
|
|
|
21
|
+
const { isActiveService } = useActiveNavigation()
|
|
22
|
+
|
|
21
23
|
function isActive(itemPath: string) {
|
|
22
24
|
if (!props.currentModule) return false
|
|
23
|
-
|
|
24
|
-
const currentUrl = window.location.href.split('/').filter(item => !['kk', 'en'].includes(item)).join('/')
|
|
25
|
-
const section = PAGES[props.id]
|
|
26
|
-
|
|
27
|
-
const currentPath = itemPath.split('/').filter(item => !['kk', 'en'].includes(item)).join('/')
|
|
28
|
-
|
|
29
|
-
if (currentUrl === currentPath) return true
|
|
30
|
-
|
|
31
|
-
if (currentPath.endsWith('/')) {
|
|
32
|
-
let atLeastOne = false
|
|
33
|
-
|
|
34
|
-
for (const key in section) {
|
|
35
|
-
const path = section[key]
|
|
36
|
-
if (key !== 'main') {
|
|
37
|
-
const includes = currentUrl.includes(path) || currentUrl.includes('car-result')
|
|
38
|
-
if (includes) {
|
|
39
|
-
atLeastOne = true
|
|
40
|
-
break
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return !atLeastOne
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (currentPath.includes('check-car')) {
|
|
49
|
-
if (currentUrl.includes('car-result')) {
|
|
50
|
-
return true
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
for (const key in section) {
|
|
55
|
-
const path = section[key]
|
|
56
|
-
if (key !== 'main') {
|
|
57
|
-
const includesBoth = currentUrl.includes(path) && currentUrl.startsWith(currentPath)
|
|
58
|
-
if (includesBoth) {
|
|
59
|
-
return true
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return false
|
|
25
|
+
return isActiveService(itemPath)
|
|
65
26
|
}
|
|
66
27
|
</script>
|
|
67
28
|
|
|
68
29
|
<template>
|
|
69
|
-
<section
|
|
30
|
+
<section
|
|
31
|
+
class="group/section overflow-hidden rounded-xl border p-3 shadow-sm transition-colors"
|
|
32
|
+
:class="currentModule
|
|
33
|
+
? 'border-blue-700/20 bg-blue-50 hover:border-blue-700/40 dark:border-blue-500/20 dark:bg-blue-500/5 dark:hover:border-blue-500/40'
|
|
34
|
+
: 'border-gray-200 bg-white hover:border-blue-700/40 dark:border-gray-800 dark:bg-gray-950 dark:hover:border-blue-500/40'"
|
|
35
|
+
>
|
|
70
36
|
<div class="mb-3 flex items-start justify-between gap-2">
|
|
71
37
|
<nuxt-link-locale
|
|
72
38
|
:to="link"
|
|
73
39
|
class="group/title flex min-w-0 items-center gap-2 text-sm font-bold text-deepblue-900 transition-colors hover:text-blue-700 dark:text-gray-100 dark:hover:text-blue-500"
|
|
74
40
|
>
|
|
75
|
-
<span
|
|
41
|
+
<span
|
|
42
|
+
class="flex size-8 shrink-0 items-center justify-center rounded-lg bg-blue-700/10 text-blue-700 transition-colors group-hover/title:bg-blue-700 group-hover/title:text-white dark:bg-blue-500/10 dark:text-blue-500">
|
|
76
43
|
<component
|
|
77
44
|
:is="icon"
|
|
78
45
|
class="size-4"
|
|
@@ -86,7 +53,8 @@ function isActive(itemPath: string) {
|
|
|
86
53
|
v-if="badge"
|
|
87
54
|
class="rounded-full bg-blue-700/10 px-1.5 py-0.5 text-[10px] font-bold uppercase leading-none text-blue-700 dark:bg-blue-500/10 dark:text-blue-500"
|
|
88
55
|
>NEW</span>
|
|
89
|
-
<a-icon-arrow-side-up
|
|
56
|
+
<a-icon-arrow-side-up
|
|
57
|
+
class="size-4 text-gray-500 transition-colors group-hover/section:text-blue-700 dark:group-hover/section:text-blue-500"/>
|
|
90
58
|
</div>
|
|
91
59
|
</div>
|
|
92
60
|
|
|
@@ -102,13 +70,25 @@ function isActive(itemPath: string) {
|
|
|
102
70
|
class="group/item flex items-center gap-2 rounded-lg px-2 py-1.5 transition-colors hover:bg-blue-100 dark:hover:bg-gray-200/10"
|
|
103
71
|
:class="{ 'bg-blue-100 dark:bg-gray-200/10': isActive(item.to) }"
|
|
104
72
|
>
|
|
105
|
-
<span
|
|
73
|
+
<span
|
|
74
|
+
class="flex size-7 shrink-0 items-center justify-center rounded-md transition-colors group-hover/item:bg-blue-700/10 group-hover/item:text-blue-700 dark:group-hover/item:text-blue-500"
|
|
75
|
+
:class="{
|
|
76
|
+
'bg-blue-700/10 text-blue-700 dark:bg-blue-500/10 dark:text-blue-500': isActive(item.to),
|
|
77
|
+
'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-300': !isActive(item.to),
|
|
78
|
+
// 'bg-white text-gray-600 dark:bg-white dark:text-gray-300': currentModule,
|
|
79
|
+
}"
|
|
80
|
+
>
|
|
106
81
|
<component
|
|
107
82
|
:is="item.icon"
|
|
108
83
|
class="size-[14px]"
|
|
109
84
|
/>
|
|
110
85
|
</span>
|
|
111
|
-
<span
|
|
86
|
+
<span
|
|
87
|
+
class="truncate text-sm font-semibold normal-case transition-colors group-hover/item:text-deepblue-900 dark:group-hover/item:text-gray-100"
|
|
88
|
+
:class="isActive(item.to)
|
|
89
|
+
? 'text-blue-700 dark:text-blue-500'
|
|
90
|
+
: 'text-gray-600 dark:text-gray-300'"
|
|
91
|
+
>{{ item.title }}</span>
|
|
112
92
|
</nuxt-link-locale>
|
|
113
93
|
</li>
|
|
114
94
|
</ul>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import CardGallery from '#adata-ui/components/navigation/header/CardGallery.vue'
|
|
3
3
|
import NavList from '#adata-ui/components/navigation/header/NavList.vue'
|
|
4
|
+
import { useActiveNavigation } from '#adata-ui/composables/useActiveNavigation'
|
|
4
5
|
import { useHeaderNavigationLinks } from '#adata-ui/composables/useHeaderNavigationLinks'
|
|
5
6
|
|
|
6
7
|
defineProps<{ url?: string }>()
|
|
@@ -8,6 +9,7 @@ defineEmits(['outerClick', 'mouseOver'])
|
|
|
8
9
|
|
|
9
10
|
const { t } = useI18n()
|
|
10
11
|
const modules = useHeaderNavigationLinks()
|
|
12
|
+
const { isActiveModule } = useActiveNavigation()
|
|
11
13
|
|
|
12
14
|
// Fixed EDO promo card on the left (like edo-editor); EDO is excluded from the catalog grid below.
|
|
13
15
|
const edoModule = computed(() => modules.value.find(module => module.key === 'edo') ?? null)
|
|
@@ -32,35 +34,39 @@ const catalogColumns = computed(() =>
|
|
|
32
34
|
<nuxt-link-locale
|
|
33
35
|
v-if="edoModule"
|
|
34
36
|
:to="edoModule.link"
|
|
35
|
-
class="group block overflow-hidden rounded-xl border border-blue-700/20
|
|
37
|
+
class="group block overflow-hidden rounded-xl border border-blue-700/20 p-4 shadow-sm transition-colors hover:border-blue-700/40 hover:bg-blue-100 dark:border-blue-500/20 dark:hover:border-blue-500/40 dark:hover:bg-blue-500/10"
|
|
36
38
|
:data-test-id="edoModule.data_attribute"
|
|
37
39
|
>
|
|
38
40
|
<div class="flex items-start gap-3">
|
|
39
|
-
<div
|
|
41
|
+
<div
|
|
42
|
+
class="flex size-10 shrink-0 items-center justify-center rounded-lg bg-blue-700 text-white shadow-sm">
|
|
40
43
|
<component
|
|
41
44
|
:is="edoModule.icon"
|
|
42
45
|
class="size-5"
|
|
43
46
|
/>
|
|
44
47
|
</div>
|
|
45
48
|
<div class="min-w-0">
|
|
46
|
-
<div class="flex items-center gap-2">
|
|
49
|
+
<div class="flex items-center justify-between gap-2">
|
|
47
50
|
<h3 class="text-base font-bold normal-case text-deepblue-900 dark:text-gray-100">
|
|
48
51
|
{{ edoModule.name }}
|
|
49
52
|
</h3>
|
|
50
|
-
<
|
|
53
|
+
<div class="flex items-center gap-1 justify-end">
|
|
54
|
+
<span
|
|
55
|
+
v-if="edoModule.is_new"
|
|
56
|
+
class="rounded-full bg-blue-700/10 px-1.5 py-0.5 text-[10px] font-bold uppercase leading-none text-blue-700 dark:bg-blue-500/10 dark:text-blue-500"
|
|
57
|
+
>NEW</span>
|
|
58
|
+
<a-icon-arrow-side-up
|
|
59
|
+
class="size-4 text-gray-500 transition group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-blue-700 dark:text-gray-400 dark:group-hover:text-blue-500"/>
|
|
60
|
+
</div>
|
|
51
61
|
</div>
|
|
52
62
|
<p class="mt-1 text-sm normal-case leading-snug text-gray-600 dark:text-gray-300">
|
|
53
63
|
{{ t('header.products.edo.heroSubtitle') }}
|
|
54
64
|
</p>
|
|
55
65
|
</div>
|
|
56
66
|
</div>
|
|
57
|
-
<div class="mt-4 inline-flex items-center gap-1.5 text-sm font-semibold normal-case text-blue-700 dark:text-blue-500">
|
|
58
|
-
{{ t('header.products.edo.heroCta') }}
|
|
59
|
-
<a-icon-arrow-side-up class="size-3.5" />
|
|
60
|
-
</div>
|
|
61
67
|
</nuxt-link-locale>
|
|
62
68
|
|
|
63
|
-
<card-gallery class="hidden lg:block"
|
|
69
|
+
<card-gallery class="hidden lg:block"/>
|
|
64
70
|
</aside>
|
|
65
71
|
|
|
66
72
|
<!-- Catalog grid: products as bordered section cards (EDO lives in the hero). -->
|
|
@@ -74,7 +80,7 @@ const catalogColumns = computed(() =>
|
|
|
74
80
|
v-for="module in column"
|
|
75
81
|
:id="module.key"
|
|
76
82
|
:key="module.key"
|
|
77
|
-
:current-module="
|
|
83
|
+
:current-module="isActiveModule(module)"
|
|
78
84
|
:title="module.name"
|
|
79
85
|
:link="module.link"
|
|
80
86
|
:nav-list="module.items"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Active-state detection for the products mega-menu.
|
|
2
|
+
//
|
|
3
|
+
// Two levels of "active":
|
|
4
|
+
// 1. activeModule — the current host matches one of the module's hosts
|
|
5
|
+
// (host ≠ module key: e.g. the `fines` module lives on avto.*.kz,
|
|
6
|
+
// and `tenders` spans both zakupki.*.kz and tender.*.kz).
|
|
7
|
+
// 2. activeService — within the active module, the current page matches a
|
|
8
|
+
// concrete item link (`/`, `/unload`, `/foreign`, …).
|
|
9
|
+
|
|
10
|
+
const LOCALE_SEGMENTS = new Set(['kk', 'en'])
|
|
11
|
+
|
|
12
|
+
interface NavTarget { to: string }
|
|
13
|
+
interface NavModule { link: string, items: NavTarget[] }
|
|
14
|
+
interface NormalizedUrl { host: string, key: string }
|
|
15
|
+
|
|
16
|
+
function safeUrl(href: string): URL | null {
|
|
17
|
+
try {
|
|
18
|
+
return new URL(href)
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Reduce a URL to a comparable identity: host + locale-stripped path + query.
|
|
26
|
+
// The query is kept because it can be significant (fines main is `/?check_fines=true`).
|
|
27
|
+
function normalize(href: string): NormalizedUrl | null {
|
|
28
|
+
const url = safeUrl(href)
|
|
29
|
+
if (!url) return null
|
|
30
|
+
|
|
31
|
+
const segments = url.pathname
|
|
32
|
+
.split('/')
|
|
33
|
+
.filter(segment => segment && !LOCALE_SEGMENTS.has(segment))
|
|
34
|
+
|
|
35
|
+
const path = `/${segments.join('/')}`
|
|
36
|
+
return { host: url.host, key: path + url.search }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// A "root" target has no path segments (e.g. `/` or `/?check_fines=true`).
|
|
40
|
+
// Roots must match exactly, otherwise their prefix would light up every sub-page.
|
|
41
|
+
function isRoot(key: string): boolean {
|
|
42
|
+
return key === '/' || key.startsWith('/?')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function useActiveNavigation() {
|
|
46
|
+
const route = useRoute()
|
|
47
|
+
|
|
48
|
+
// Recompute on every SPA navigation. Host stays constant within an app, so we
|
|
49
|
+
// read it from the request URL (SSR-safe); the path/query come from the route.
|
|
50
|
+
const current = computed<NormalizedUrl | null>(() => {
|
|
51
|
+
const host = useRequestURL().host
|
|
52
|
+
return normalize(`https://${host}${route.fullPath}`)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
function isActiveModule(module: NavModule): boolean {
|
|
56
|
+
if (!current.value) return false
|
|
57
|
+
|
|
58
|
+
const hosts = new Set<string>()
|
|
59
|
+
const link = normalize(module.link)
|
|
60
|
+
if (link) hosts.add(link.host)
|
|
61
|
+
for (const item of module.items) {
|
|
62
|
+
const item_url = normalize(item.to)
|
|
63
|
+
if (item_url) hosts.add(item_url.host)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return hosts.has(current.value.host)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isActiveService(itemTo: string): boolean {
|
|
70
|
+
const cur = current.value
|
|
71
|
+
const item = normalize(itemTo)
|
|
72
|
+
if (!cur || !item || cur.host !== item.host) return false
|
|
73
|
+
|
|
74
|
+
// The car-check page redirects to a `car-result` URL; keep its item lit.
|
|
75
|
+
if (item.key.includes('check-car') && cur.key.includes('car-result')) return true
|
|
76
|
+
|
|
77
|
+
if (cur.key === item.key) return true
|
|
78
|
+
if (isRoot(item.key)) return false
|
|
79
|
+
|
|
80
|
+
return cur.key.startsWith(item.key)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { isActiveModule, isActiveService }
|
|
84
|
+
}
|