spoko-design-system 1.9.0 → 1.9.2
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/.claude/settings.json +1 -1
- package/.husky/pre-commit +17 -1
- package/.prettierrc +8 -4
- package/CHANGELOG.md +35 -0
- package/index.ts +8 -2
- package/package.json +4 -3
- package/src/components/Badge.vue +1 -4
- package/src/components/Badges.vue +1 -4
- package/src/components/Breadcrumbs.vue +10 -44
- package/src/components/Button.vue +1 -5
- package/src/components/ButtonCopy.astro +2 -7
- package/src/components/ButtonCopy.vue +2 -9
- package/src/components/Card.astro +1 -4
- package/src/components/Category/CategoriesCarousel.astro +3 -11
- package/src/components/Category/CategoryDetails.astro +7 -32
- package/src/components/Category/CategoryLink.vue +1 -5
- package/src/components/Category/CategorySidebarToggler.vue +1 -5
- package/src/components/Category/CategoryTile.astro +2 -9
- package/src/components/Category/CategoryViewToggler.astro +3 -16
- package/src/components/Copyright.astro +1 -4
- package/src/components/Date.astro +1 -4
- package/src/components/Faq.astro +1 -5
- package/src/components/FaqItem.astro +3 -14
- package/src/components/FeaturesList.vue +2 -9
- package/src/components/FuckRussia.vue +9 -36
- package/src/components/HandDrive.astro +2 -12
- package/src/components/Header/Header.astro +3 -14
- package/src/components/Header/SkipToContent.astro +1 -5
- package/src/components/Input.vue +19 -31
- package/src/components/Jumbotron/index.astro +7 -41
- package/src/components/Jumbotron/variants/Default.astro +2 -17
- package/src/components/Jumbotron/variants/Hero.astro +3 -17
- package/src/components/Jumbotron/variants/Post.astro +3 -13
- package/src/components/Jumbotron/variants/PostSplit.astro +3 -13
- package/src/components/Jumbotron.astro +3 -12
- package/src/components/LanguageSuggestion.astro +3 -14
- package/src/components/MainColors.vue +7 -25
- package/src/components/MainInput.vue +2 -1
- package/src/components/Modal.astro +43 -41
- package/src/components/PageContent.astro +1 -4
- package/src/components/PartNumber.vue +1 -4
- package/src/components/PostHeader.astro +3 -13
- package/src/components/PrCode.vue +2 -2
- package/src/components/Product/ProductButton.vue +1 -4
- package/src/components/Product/ProductDetailName.vue +1 -4
- package/src/components/Product/ProductDetails.vue +19 -65
- package/src/components/Product/ProductDoc.vue +1 -4
- package/src/components/Product/ProductEngine.astro +67 -0
- package/src/components/Product/ProductEngineType.vue +1 -4
- package/src/components/Product/ProductEngines.astro +43 -0
- package/src/components/Product/ProductLink.astro +8 -32
- package/src/components/Product/ProductLink.vue +8 -36
- package/src/components/Product/ProductLinkInfo.astro +4 -19
- package/src/components/Product/ProductModel.vue +3 -5
- package/src/components/Product/ProductModels.vue +2 -10
- package/src/components/Product/ProductNumber.astro +6 -26
- package/src/components/Product/ProductPositions.vue +1 -5
- package/src/components/ProductCodes.vue +6 -14
- package/src/components/ProductDetailName.vue +2 -7
- package/src/components/ProductDetailsList.vue +7 -33
- package/src/components/ProductTile.astro +3 -13
- package/src/components/ReloadPrompt.astro +1 -5
- package/src/components/SlimBanner.vue +10 -15
- package/src/components/Table.vue +3 -15
- package/src/components/Translations.vue +1 -4
- package/src/components/layout/CallToAction.astro +2 -7
- package/src/components/layout/Container.astro +1 -3
- package/src/components/layout/Header.astro +2 -12
- package/src/layouts/Layout.astro +2 -9
- package/src/layouts/MainLayout.astro +4 -17
- package/src/layouts/partials/HeadCommon.astro +7 -24
- package/src/layouts/partials/HeadSEO.astro +12 -48
- package/src/pages/components/icons.astro +1 -4
- package/src/pages/components/product-engine.mdx +75 -31
- package/src/pages/core/typography.astro +2 -6
- package/src/pages/index.astro +16 -63
- package/src/scripts/tooltips.ts +33 -28
- package/src/styles/main.css +4 -0
- package/src/styles/tippy-theme.css +4 -2
- package/src/utils/product/getEngineTooltipContent.ts +158 -0
- package/src/utils/product/getPriceFormatted.ts +2 -6
- package/src/utils/product/useFormatProductNumber.ts +1 -4
- package/src/utils/seo/getShorterDescription.ts +1 -4
- package/uno-config/index.ts +1 -1
- package/src/components/Product/ProductEngine.vue +0 -240
- package/src/components/Product/ProductEngines.vue +0 -116
|
@@ -84,31 +84,26 @@ const {
|
|
|
84
84
|
}
|
|
85
85
|
</style>
|
|
86
86
|
|
|
87
|
-
{
|
|
88
|
-
|
|
89
|
-
primary
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<dialog
|
|
96
|
-
id={id}
|
|
97
|
-
class="p-6"
|
|
98
|
-
>
|
|
87
|
+
{
|
|
88
|
+
showTrigger && (
|
|
89
|
+
<Button primary onclick={`window.${id}.showModal()`}>
|
|
90
|
+
{open}
|
|
91
|
+
</Button>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
<dialog id={id} class="p-6">
|
|
99
96
|
<div class="modal-header">
|
|
100
97
|
{title && <h2 class="text-2xl font-bold pr-8">{title}</h2>}
|
|
101
98
|
<slot name="header" />
|
|
102
99
|
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
<
|
|
106
|
-
class="modal-close-x"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
</form>
|
|
111
|
-
)}
|
|
100
|
+
{
|
|
101
|
+
showXButton && (
|
|
102
|
+
<form method="dialog" class="inline">
|
|
103
|
+
<button class="modal-close-x" aria-label="Close" type="submit" />
|
|
104
|
+
</form>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
112
107
|
</div>
|
|
113
108
|
|
|
114
109
|
<div class="modal-content">
|
|
@@ -116,37 +111,44 @@ const {
|
|
|
116
111
|
<slot />
|
|
117
112
|
</div>
|
|
118
113
|
|
|
119
|
-
{
|
|
120
|
-
|
|
121
|
-
<
|
|
122
|
-
<
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
114
|
+
{
|
|
115
|
+
showActions && (
|
|
116
|
+
<div class="modal-actions">
|
|
117
|
+
<slot name="actions">
|
|
118
|
+
<form method="dialog" class="contents">
|
|
119
|
+
<Button>{cancelText}</Button>
|
|
120
|
+
</form>
|
|
121
|
+
<Button
|
|
122
|
+
primary={confirmPrimary}
|
|
123
|
+
onclick={`document.getElementById('${id}').dispatchEvent(new CustomEvent('confirm', { detail: { id: '${id}' } }))`}
|
|
124
|
+
>
|
|
125
|
+
{confirmText}
|
|
126
|
+
</Button>
|
|
127
|
+
</slot>
|
|
128
|
+
</div>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
!showActions && (
|
|
134
|
+
<form method="dialog">
|
|
135
|
+
<slot name="close" />
|
|
136
|
+
</form>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
137
139
|
</dialog>
|
|
138
140
|
|
|
139
141
|
<script define:vars={{ id }}>
|
|
140
142
|
// Close on backdrop click
|
|
141
143
|
const dialog = document.getElementById(id);
|
|
142
|
-
dialog?.addEventListener('click',
|
|
144
|
+
dialog?.addEventListener('click', e => {
|
|
143
145
|
if (e.target === dialog) {
|
|
144
146
|
dialog.close();
|
|
145
147
|
}
|
|
146
148
|
});
|
|
147
149
|
|
|
148
150
|
// Close on Escape key
|
|
149
|
-
dialog?.addEventListener('keydown',
|
|
151
|
+
dialog?.addEventListener('keydown', e => {
|
|
150
152
|
if (e.key === 'Escape') {
|
|
151
153
|
dialog.close();
|
|
152
154
|
}
|
|
@@ -31,18 +31,11 @@ import Date from './Date.astro';
|
|
|
31
31
|
>
|
|
32
32
|
{title}
|
|
33
33
|
</h1>
|
|
34
|
-
<PostCategories
|
|
35
|
-
categories={categories}
|
|
36
|
-
lang={lang}
|
|
37
|
-
/>
|
|
34
|
+
<PostCategories categories={categories} lang={lang} />
|
|
38
35
|
<div class="order-3 flex items-center text-gray-1 00">
|
|
39
36
|
{
|
|
40
37
|
author && (
|
|
41
|
-
<span
|
|
42
|
-
class="text-sm"
|
|
43
|
-
title={author.firstName}
|
|
44
|
-
data-pagefind-ignore
|
|
45
|
-
>
|
|
38
|
+
<span class="text-sm" title={author.firstName} data-pagefind-ignore>
|
|
46
39
|
{author.name}
|
|
47
40
|
</span>
|
|
48
41
|
)
|
|
@@ -53,10 +46,7 @@ import Date from './Date.astro';
|
|
|
53
46
|
</div>
|
|
54
47
|
</div>
|
|
55
48
|
<div class="featured-image">
|
|
56
|
-
<img
|
|
57
|
-
src={image}
|
|
58
|
-
alt={title}
|
|
59
|
-
/>
|
|
49
|
+
<img src={image} alt={title} />
|
|
60
50
|
</div>
|
|
61
51
|
</header>
|
|
62
52
|
|
|
@@ -13,7 +13,7 @@ interface PrCodeObject {
|
|
|
13
13
|
variant_category?: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
defineProps({
|
|
17
17
|
prcode: {
|
|
18
18
|
type: Object as PropType<PrCodeObject>,
|
|
19
19
|
required: true,
|
|
@@ -32,7 +32,7 @@ const props = defineProps({
|
|
|
32
32
|
class="btn-prcode"
|
|
33
33
|
:class="[
|
|
34
34
|
prcode.variant_category ? `btn-prcode--variant-${prcode.variant_category.toLowerCase()}` : '',
|
|
35
|
-
{ 'btn-prcode--pdp': isPdp }
|
|
35
|
+
{ 'btn-prcode--pdp': isPdp },
|
|
36
36
|
]"
|
|
37
37
|
:data-tippy-content="prcode.description || undefined"
|
|
38
38
|
>
|
|
@@ -9,10 +9,7 @@ const props = defineProps({
|
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
11
|
<template>
|
|
12
|
-
<button
|
|
13
|
-
class="product-button"
|
|
14
|
-
:class="props.shadow ? 'drop-shadow hover:(drop-shadow-md)' : ''"
|
|
15
|
-
>
|
|
12
|
+
<button class="product-button" :class="props.shadow ? 'drop-shadow hover:(drop-shadow-md)' : ''">
|
|
16
13
|
<slot />
|
|
17
14
|
</button>
|
|
18
15
|
</template>
|
|
@@ -15,10 +15,7 @@ const props = defineProps({
|
|
|
15
15
|
|
|
16
16
|
<template>
|
|
17
17
|
<div class="font-bold detail-name w-full sm:w-50 flex">
|
|
18
|
-
<span
|
|
19
|
-
class="colon-after"
|
|
20
|
-
:class="styles && styles.length ? styles : 'mt-auto'"
|
|
21
|
-
>
|
|
18
|
+
<span class="colon-after" :class="styles && styles.length ? styles : 'mt-auto'">
|
|
22
19
|
{{ props.text }}
|
|
23
20
|
</span>
|
|
24
21
|
</div>
|
|
@@ -30,28 +30,18 @@ const props = defineProps({
|
|
|
30
30
|
|
|
31
31
|
<template>
|
|
32
32
|
<div v-if="details && details.length">
|
|
33
|
-
<template
|
|
34
|
-
v-for="(detail, index) in details"
|
|
35
|
-
:key="index"
|
|
36
|
-
>
|
|
33
|
+
<template v-for="(detail, index) in details" :key="index">
|
|
37
34
|
<!-- PDP PAGE - PRODUCT ROW -->
|
|
38
35
|
<!-- <pre>{{ JSON.stringify(details) }}</pre> -->
|
|
39
36
|
<li
|
|
40
37
|
v-if="props.small"
|
|
41
38
|
class="text-xs md:text-sm text-slate-darkest dark:text-neutral-light leading-tight font-textlight md:font-textregular"
|
|
42
39
|
>
|
|
43
|
-
<span
|
|
44
|
-
v-if="detail.id"
|
|
45
|
-
class="inline-block mr-1 items--0"
|
|
46
|
-
>
|
|
40
|
+
<span v-if="detail.id" class="inline-block mr-1 items--0">
|
|
47
41
|
{{ getTranslation(`detail.${detail.id}`) }}:
|
|
48
42
|
</span>
|
|
49
43
|
|
|
50
|
-
<span
|
|
51
|
-
v-if="detail.translated"
|
|
52
|
-
:class="detail.id ? 'font-semibold' : ''"
|
|
53
|
-
class="items--1"
|
|
54
|
-
>
|
|
44
|
+
<span v-if="detail.translated" :class="detail.id ? 'font-semibold' : ''" class="items--1">
|
|
55
45
|
{{ detail.value }}
|
|
56
46
|
</span>
|
|
57
47
|
|
|
@@ -59,26 +49,16 @@ const props = defineProps({
|
|
|
59
49
|
v-else-if="detail.value !== undefined && detail.id === 'color' && detail.isArrayValue"
|
|
60
50
|
class="items--2 inline-block"
|
|
61
51
|
>
|
|
62
|
-
<span
|
|
63
|
-
v-for="(color, indexColor) in detail.value"
|
|
64
|
-
:key="indexColor"
|
|
65
|
-
class="comma"
|
|
66
|
-
>
|
|
52
|
+
<span v-for="(color, indexColor) in detail.value" :key="indexColor" class="comma">
|
|
67
53
|
{{ color['name'] }}
|
|
68
54
|
</span>
|
|
69
55
|
</div>
|
|
70
56
|
|
|
71
|
-
<span
|
|
72
|
-
v-else-if="detail.id !== 'paint-marks' && !detail.isArrayValue"
|
|
73
|
-
class="items--3"
|
|
74
|
-
>
|
|
57
|
+
<span v-else-if="detail.id !== 'paint-marks' && !detail.isArrayValue" class="items--3">
|
|
75
58
|
{{ locale === 'en' ? String(detail.value).replace(/,/g, '.') : String(detail.value) }}
|
|
76
59
|
</span>
|
|
77
60
|
|
|
78
|
-
<span
|
|
79
|
-
v-else-if="detail.id && detail.id === 'paint-marks'"
|
|
80
|
-
class="items items--4"
|
|
81
|
-
>
|
|
61
|
+
<span v-else-if="detail.id && detail.id === 'paint-marks'" class="items items--4">
|
|
82
62
|
{{ detail.value }}
|
|
83
63
|
<!-- <span v-for="(mark, index2) in JSON.parse(String(detail.value))" :key="index2" class="item">
|
|
84
64
|
{{ mark[0] }} x <span>{{ getTranslation(`color.${mark[1]}`) }}</span>
|
|
@@ -91,14 +71,8 @@ const props = defineProps({
|
|
|
91
71
|
class="text-sm mt-1 md:mt-4 mb-4 grid grid-cols-2 sm:(grid-cols-details-desktop grid-flow-col auto-cols-max) gap-4"
|
|
92
72
|
>
|
|
93
73
|
<!-- PRODUCT CARD -->
|
|
94
|
-
<ProductDetailName
|
|
95
|
-
|
|
96
|
-
:text="getTranslation(`detail.${detail.id}`)"
|
|
97
|
-
/>
|
|
98
|
-
<div
|
|
99
|
-
v-if="detail.value"
|
|
100
|
-
class="leading-4 flex items-end"
|
|
101
|
-
>
|
|
74
|
+
<ProductDetailName v-if="detail.id" :text="getTranslation(`detail.${detail.id}`)" />
|
|
75
|
+
<div v-if="detail.value" class="leading-4 flex items-end">
|
|
102
76
|
<span
|
|
103
77
|
v-if="detail.translated && !detail.isArrayValue"
|
|
104
78
|
:class="detail.id === 'light-function' ? 'whitespace-pre-line' : ''"
|
|
@@ -106,51 +80,31 @@ const props = defineProps({
|
|
|
106
80
|
{{ getTranslation(`detail.value.${detail.value}`) }}
|
|
107
81
|
</span>
|
|
108
82
|
|
|
109
|
-
<span
|
|
110
|
-
v-else-if="detail.id !== 'paint-marks' && !detail.isArrayValue"
|
|
111
|
-
class=""
|
|
112
|
-
>
|
|
83
|
+
<span v-else-if="detail.id !== 'paint-marks' && !detail.isArrayValue" class="">
|
|
113
84
|
{{ detail.value }}
|
|
114
85
|
</span>
|
|
115
86
|
|
|
116
87
|
<span
|
|
117
88
|
v-else-if="
|
|
118
|
-
detail.value !== undefined &&
|
|
119
|
-
detail.id === 'for-exterior-colour' &&
|
|
120
|
-
detail.isArrayValue
|
|
89
|
+
detail.value !== undefined && detail.id === 'for-exterior-colour' && detail.isArrayValue
|
|
121
90
|
"
|
|
122
91
|
class=""
|
|
123
92
|
>
|
|
124
|
-
<div
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
>
|
|
128
|
-
<span class="font-mono">{{ color }}</span> -
|
|
93
|
+
<div v-for="(color, indexColor) in JSON.parse(String(detail.value))" :key="indexColor">
|
|
94
|
+
<span class="font-mono">{{ color }}</span>
|
|
95
|
+
-
|
|
129
96
|
{{ getTranslation(`colorCodes.${color}`) }}
|
|
130
97
|
</div>
|
|
131
98
|
</span>
|
|
132
99
|
|
|
133
|
-
<span
|
|
134
|
-
v-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
<span
|
|
138
|
-
v-for="(mark, markIndex) in JSON.parse(String(detail.value))"
|
|
139
|
-
:key="markIndex"
|
|
140
|
-
class="item"
|
|
141
|
-
>
|
|
142
|
-
{{ mark[0] }} x <span>{{ getTranslation(`color.${mark[1]}`) }}</span>
|
|
100
|
+
<span v-else-if="detail.id && detail.id === 'paint-marks'" class="items">
|
|
101
|
+
<span v-for="(mark, markIndex) in JSON.parse(String(detail.value))" :key="markIndex" class="item">
|
|
102
|
+
{{ mark[0] }} x
|
|
103
|
+
<span>{{ getTranslation(`color.${mark[1]}`) }}</span>
|
|
143
104
|
</span>
|
|
144
105
|
</span>
|
|
145
|
-
<ul
|
|
146
|
-
v-
|
|
147
|
-
class="items"
|
|
148
|
-
>
|
|
149
|
-
<li
|
|
150
|
-
v-for="(d, index3) in JSON.parse(String(detail.value))"
|
|
151
|
-
:key="index3"
|
|
152
|
-
class="item"
|
|
153
|
-
>
|
|
106
|
+
<ul v-else-if="detail.id && detail.isArrayValue" class="items">
|
|
107
|
+
<li v-for="(d, index3) in JSON.parse(String(detail.value))" :key="index3" class="item">
|
|
154
108
|
· {{ d }}
|
|
155
109
|
</li>
|
|
156
110
|
</ul>
|
|
@@ -20,10 +20,7 @@ const props = defineProps({
|
|
|
20
20
|
:title="props.file.path"
|
|
21
21
|
class="flex items-center hover:underline underline-offset-2 hover:underline-1"
|
|
22
22
|
>
|
|
23
|
-
<div
|
|
24
|
-
i-system-uicons-document-justified
|
|
25
|
-
class="text-blue-lightest dark:text-accent-light"
|
|
26
|
-
/>
|
|
23
|
+
<div i-system-uicons-document-justified class="text-blue-lightest dark:text-accent-light" />
|
|
27
24
|
<span>{{ props.file.name }}</span>
|
|
28
25
|
</a>
|
|
29
26
|
</li>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getEngineTooltipContent } from '../../utils/product/getEngineTooltipContent';
|
|
3
|
+
import type { Engine, EngineTranslations } from '../../utils/product/getEngineTooltipContent';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
VAG group (VW/Audi/Skoda/Seat/Porsche/Bentley/Lamborghini/Ducati/Cupra/Scania/MAN) manufacturer Engine Code
|
|
7
|
+
Displays engine code with detailed tooltip showing: name, power, displacement, dates, etc.
|
|
8
|
+
|
|
9
|
+
SEO-friendly: Engine code is rendered in HTML, tooltips enhanced via delegation script
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
engine: Engine;
|
|
14
|
+
showComma?: boolean;
|
|
15
|
+
translations?: EngineTranslations;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { engine, showComma = false, translations = {} } = Astro.props;
|
|
19
|
+
|
|
20
|
+
// Generate tooltip content for data attribute
|
|
21
|
+
const tooltipContent = getEngineTooltipContent(engine, translations);
|
|
22
|
+
|
|
23
|
+
// Escape quotes for HTML attribute
|
|
24
|
+
const tooltipContentEscaped = tooltipContent.replace(/"/g, '"');
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<span
|
|
28
|
+
class="engine-code"
|
|
29
|
+
class:list={[`engine-code-${engine.code}`]}
|
|
30
|
+
data-tippy-content={tooltipContentEscaped}
|
|
31
|
+
>
|
|
32
|
+
{engine.code}{showComma && ','}
|
|
33
|
+
</span>
|
|
34
|
+
|
|
35
|
+
<style>
|
|
36
|
+
/* Engine Code Styles */
|
|
37
|
+
.engine-code {
|
|
38
|
+
@apply inline-block mr-1;
|
|
39
|
+
@apply underline decoration-dotted underline-offset-4 py-0.5;
|
|
40
|
+
@apply decoration-neutral-light cursor-default;
|
|
41
|
+
@apply transition-colors duration-200;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.engine-code:hover {
|
|
45
|
+
@apply decoration-blue-darker dark:decoration-blue-light;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Semantic Engine Code Colors */
|
|
49
|
+
/* GTI Engines - Red */
|
|
50
|
+
.engine-code-CAVE,
|
|
51
|
+
.engine-code-CTHE,
|
|
52
|
+
.engine-code-DAJA,
|
|
53
|
+
.engine-code-DAYB {
|
|
54
|
+
@apply text-red-600 dark:text-red-500;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* WRC R Engine - Blue */
|
|
58
|
+
.engine-code-CDLJ {
|
|
59
|
+
@apply text-blue-600 dark:text-blue-500;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Special Blue Engines */
|
|
63
|
+
.engine-code-CPTA,
|
|
64
|
+
.engine-code-CZEA {
|
|
65
|
+
@apply text-blue-700 dark:text-blue-600;
|
|
66
|
+
}
|
|
67
|
+
</style>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
import ProductEngine from './ProductEngine.astro';
|
|
3
|
+
import type { Engine, EngineTranslations } from '../../utils/product/getEngineTooltipContent';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
ProductEngines wrapper component
|
|
7
|
+
Displays a list of engine codes with tooltips
|
|
8
|
+
SEO-friendly: All engine codes rendered in HTML, enhanced by global delegation script
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
engines: Engine[];
|
|
13
|
+
isPdp?: boolean;
|
|
14
|
+
translations?: EngineTranslations;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { engines = [], translations = {} } = Astro.props;
|
|
18
|
+
|
|
19
|
+
// Sort engines by code
|
|
20
|
+
const sortedEngines = engines.length
|
|
21
|
+
? [...engines].sort((a, b) => (a.code || '').localeCompare(b.code || ''))
|
|
22
|
+
: [];
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
{
|
|
26
|
+
sortedEngines.length > 0 && (
|
|
27
|
+
<div class="engines-list inline-flex flex-wrap items-center gap-x-0.5">
|
|
28
|
+
{sortedEngines.map((engine, index) => (
|
|
29
|
+
<ProductEngine
|
|
30
|
+
engine={engine}
|
|
31
|
+
showComma={index !== sortedEngines.length - 1}
|
|
32
|
+
translations={translations}
|
|
33
|
+
/>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
.engines-list {
|
|
41
|
+
@apply leading-none;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
@@ -27,12 +27,7 @@ import { getProductUrl } from '@utils/product/getProductUrl';
|
|
|
27
27
|
import { getShopProductUrl } from '@utils/product/getShopProductUrl';
|
|
28
28
|
import { getProductTranslation } from '@utils/product/getProductTranslation';
|
|
29
29
|
import { getImageUrl } from '@utils/getImageUrl';
|
|
30
|
-
import {
|
|
31
|
-
ProductImage,
|
|
32
|
-
ProductNumber,
|
|
33
|
-
removeSemicolon,
|
|
34
|
-
getPriceFormatted,
|
|
35
|
-
} from 'spoko-design-system';
|
|
30
|
+
import { ProductImage, ProductNumber, removeSemicolon, getPriceFormatted } from 'spoko-design-system';
|
|
36
31
|
|
|
37
32
|
// Użycie productObject jeśli przekazane, inaczej pobranie produktu na podstawie productId
|
|
38
33
|
const product =
|
|
@@ -51,9 +46,7 @@ const thumb = product
|
|
|
51
46
|
: '';
|
|
52
47
|
|
|
53
48
|
// Product translation removed - using English only
|
|
54
|
-
const productTranslation = productId
|
|
55
|
-
? await getProductTranslation(productId, product?.number || '')
|
|
56
|
-
: null;
|
|
49
|
+
const productTranslation = productId ? await getProductTranslation(productId, product?.number || '') : null;
|
|
57
50
|
|
|
58
51
|
const productName = product
|
|
59
52
|
? isShopProduct
|
|
@@ -67,9 +60,7 @@ const nameFormatted = removeSemicolon(productName.toString());
|
|
|
67
60
|
{
|
|
68
61
|
product && (
|
|
69
62
|
<>
|
|
70
|
-
<div
|
|
71
|
-
class={bigTile ? 'product-link--big-tile' : 'product-thumb--plp product-thumb--carousel'}
|
|
72
|
-
>
|
|
63
|
+
<div class={bigTile ? 'product-link--big-tile' : 'product-thumb--plp product-thumb--carousel'}>
|
|
73
64
|
{product.photo !== null && thumb ? (
|
|
74
65
|
<ProductImage
|
|
75
66
|
imagesApiUrl="https://api.polo.blue"
|
|
@@ -82,11 +73,7 @@ const nameFormatted = removeSemicolon(productName.toString());
|
|
|
82
73
|
}}
|
|
83
74
|
/>
|
|
84
75
|
) : (
|
|
85
|
-
<img
|
|
86
|
-
src="/1x1.png"
|
|
87
|
-
class="bg-neutral-lightest/70"
|
|
88
|
-
alt={productName}
|
|
89
|
-
/>
|
|
76
|
+
<img src="/1x1.png" class="bg-neutral-lightest/70" alt={productName} />
|
|
90
77
|
)}
|
|
91
78
|
</div>
|
|
92
79
|
|
|
@@ -98,30 +85,19 @@ const nameFormatted = removeSemicolon(productName.toString());
|
|
|
98
85
|
<a
|
|
99
86
|
class="product-link--url"
|
|
100
87
|
href={
|
|
101
|
-
isShopProduct
|
|
102
|
-
? getShopProductUrl(product.slug, locale)
|
|
103
|
-
: getProductUrl(product.number, locale)
|
|
88
|
+
isShopProduct ? getShopProductUrl(product.slug, locale) : getProductUrl(product.number, locale)
|
|
104
89
|
}
|
|
105
90
|
itemprop="url"
|
|
106
91
|
title={product.number}
|
|
107
92
|
set:html={nameFormatted}
|
|
108
93
|
/>
|
|
109
94
|
|
|
110
|
-
<ProductNumber
|
|
111
|
-
productNumber={product.number}
|
|
112
|
-
copyDisabled={true}
|
|
113
|
-
/>
|
|
95
|
+
<ProductNumber productNumber={product.number} copyDisabled={true} />
|
|
114
96
|
|
|
115
97
|
{index !== null && (
|
|
116
98
|
<>
|
|
117
|
-
<meta
|
|
118
|
-
|
|
119
|
-
content={index.toString()}
|
|
120
|
-
/>
|
|
121
|
-
<meta
|
|
122
|
-
itemprop="name"
|
|
123
|
-
content={nameFormatted}
|
|
124
|
-
/>
|
|
99
|
+
<meta itemprop="position" content={index.toString()} />
|
|
100
|
+
<meta itemprop="name" content={nameFormatted} />
|
|
125
101
|
</>
|
|
126
102
|
)}
|
|
127
103
|
</div>
|
|
@@ -1,52 +1,24 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
class="product-link"
|
|
4
|
-
itemscope
|
|
5
|
-
itemtype="https://schema.org/Product"
|
|
6
|
-
>
|
|
7
|
-
<div
|
|
8
|
-
:class="[bigTile ? 'product-link--big-tile' : 'product-thumb--plp product-thumb--carousel']"
|
|
9
|
-
>
|
|
2
|
+
<div class="product-link" itemscope itemtype="https://schema.org/Product">
|
|
3
|
+
<div :class="[bigTile ? 'product-link--big-tile' : 'product-thumb--plp product-thumb--carousel']">
|
|
10
4
|
<!-- Slot na ProductImage z Astro -->
|
|
11
5
|
<slot name="image">
|
|
12
|
-
<img
|
|
13
|
-
src="/1x1.png"
|
|
14
|
-
class="bg-neutral-lightest/70"
|
|
15
|
-
:alt="productName"
|
|
16
|
-
/>
|
|
6
|
+
<img src="/1x1.png" class="bg-neutral-lightest/70" :alt="productName" />
|
|
17
7
|
</slot>
|
|
18
8
|
</div>
|
|
19
9
|
|
|
20
10
|
<div :class="[bigTile ? '' : 'sm:pl-4']">
|
|
21
|
-
<p
|
|
22
|
-
v-if="price"
|
|
23
|
-
class="block mb-2 font-600 font-headbold text-5"
|
|
24
|
-
>
|
|
11
|
+
<p v-if="price" class="block mb-2 font-600 font-headbold text-5">
|
|
25
12
|
{{ price }}
|
|
26
13
|
</p>
|
|
27
14
|
|
|
28
|
-
<a
|
|
29
|
-
class="product-link--url"
|
|
30
|
-
:href="url"
|
|
31
|
-
itemprop="url"
|
|
32
|
-
:title="productNumber"
|
|
33
|
-
v-html="nameFormatted"
|
|
34
|
-
/>
|
|
15
|
+
<a class="product-link--url" :href="url" itemprop="url" :title="productNumber" v-html="nameFormatted" />
|
|
35
16
|
|
|
36
|
-
<ProductNumber
|
|
37
|
-
:product-number="productNumber"
|
|
38
|
-
:copy-disabled="true"
|
|
39
|
-
/>
|
|
17
|
+
<ProductNumber :product-number="productNumber" :copy-disabled="true" />
|
|
40
18
|
|
|
41
19
|
<template v-if="index !== null">
|
|
42
|
-
<meta
|
|
43
|
-
|
|
44
|
-
:content="index.toString()"
|
|
45
|
-
/>
|
|
46
|
-
<meta
|
|
47
|
-
itemprop="name"
|
|
48
|
-
:content="nameFormatted"
|
|
49
|
-
/>
|
|
20
|
+
<meta itemprop="position" :content="index.toString()" />
|
|
21
|
+
<meta itemprop="name" :content="nameFormatted" />
|
|
50
22
|
</template>
|
|
51
23
|
</div>
|
|
52
24
|
</div>
|
|
@@ -16,28 +16,13 @@ const { product, nameFormatted, price, url, index, bigTile } = Astro.props;
|
|
|
16
16
|
|
|
17
17
|
<div class={`flex flex-col ${bigTile ? '' : 'sm:pl-4'}`}>
|
|
18
18
|
{price && <p class="block mb-2 font-600 font-headbold text-5">{price}</p>}
|
|
19
|
-
<a
|
|
20
|
-
|
|
21
|
-
href={url}
|
|
22
|
-
itemprop="url"
|
|
23
|
-
title={product.number}
|
|
24
|
-
set:html={nameFormatted}
|
|
25
|
-
/>
|
|
26
|
-
<ProductNumber
|
|
27
|
-
productNumber={product.number}
|
|
28
|
-
copyDisabled={true}
|
|
29
|
-
/>
|
|
19
|
+
<a class="product-link--url" href={url} itemprop="url" title={product.number} set:html={nameFormatted} />
|
|
20
|
+
<ProductNumber productNumber={product.number} copyDisabled={true} />
|
|
30
21
|
{
|
|
31
22
|
index !== null && (
|
|
32
23
|
<>
|
|
33
|
-
<meta
|
|
34
|
-
|
|
35
|
-
content={index}
|
|
36
|
-
/>
|
|
37
|
-
<meta
|
|
38
|
-
itemprop="name"
|
|
39
|
-
content={nameFormatted}
|
|
40
|
-
/>
|
|
24
|
+
<meta itemprop="position" content={index} />
|
|
25
|
+
<meta itemprop="name" content={nameFormatted} />
|
|
41
26
|
</>
|
|
42
27
|
)
|
|
43
28
|
}
|