spoko-design-system 1.3.0 → 1.3.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/CHANGELOG.md +4 -0
- package/package.json +1 -1
- package/src/components/Breadcrumbs.vue +21 -8
- package/src/components/ButtonCopy.vue +9 -2
- package/src/components/Category/CategoryLink.vue +5 -1
- package/src/components/Category/CategorySidebarToggler.vue +5 -1
- package/src/components/FeaturesList.vue +9 -2
- package/src/components/FuckRussia.vue +36 -9
- package/src/components/Input.vue +18 -5
- package/src/components/MainColors.vue +13 -3
- package/src/components/MainInput.vue +2 -3
- package/src/components/PartNumber.vue +4 -1
- package/src/components/Product/ProductButton.vue +4 -1
- package/src/components/Product/ProductDetailName.vue +4 -1
- package/src/components/Product/ProductDetails.vue +57 -15
- package/src/components/Product/ProductDoc.vue +4 -1
- package/src/components/Product/ProductEngineType.vue +4 -1
- package/src/components/Product/ProductLink.vue +26 -6
- package/src/components/Product/ProductModel.vue +4 -1
- package/src/components/Product/ProductModels.vue +10 -2
- package/src/components/Product/ProductPositions.vue +5 -1
- package/src/components/ProductCodes.vue +9 -2
- package/src/components/ProductDetailName.vue +4 -1
- package/src/components/ProductDetailsList.vue +55 -141
- package/src/components/SlimBanner.vue +9 -7
- package/src/components/Table.vue +15 -3
- package/src/components/Translations.vue +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## [1.3.2](https://github.com/polo-blue/sds/compare/v1.3.1...v1.3.2) (2025-10-07)
|
|
2
|
+
|
|
3
|
+
## [1.3.1](https://github.com/polo-blue/sds/compare/v1.3.0...v1.3.1) (2025-10-05)
|
|
4
|
+
|
|
1
5
|
## [1.3.0](https://github.com/polo-blue/sds/compare/v1.2.2...v1.3.0) (2025-10-05)
|
|
2
6
|
|
|
3
7
|
### Features
|
package/package.json
CHANGED
|
@@ -74,7 +74,7 @@ const isLast = (index: number) => {
|
|
|
74
74
|
<meta
|
|
75
75
|
itemprop="position"
|
|
76
76
|
content="1"
|
|
77
|
-
|
|
77
|
+
>
|
|
78
78
|
</li>
|
|
79
79
|
<li
|
|
80
80
|
v-for="(crumb, index) in breadcrumbs"
|
|
@@ -84,7 +84,10 @@ const isLast = (index: number) => {
|
|
|
84
84
|
itemscope
|
|
85
85
|
itemtype="https://schema.org/ListItem"
|
|
86
86
|
>
|
|
87
|
-
<span
|
|
87
|
+
<span
|
|
88
|
+
v-if="index > 0 || props.showHome"
|
|
89
|
+
class="text-gray-400 px-1 py-4.25 sm:py-1"
|
|
90
|
+
>/</span>
|
|
88
91
|
|
|
89
92
|
<a
|
|
90
93
|
v-if="!isLast(index)"
|
|
@@ -93,7 +96,10 @@ const isLast = (index: number) => {
|
|
|
93
96
|
itemprop="item"
|
|
94
97
|
:title="`Polo 6R ${crumb.name}`"
|
|
95
98
|
>
|
|
96
|
-
<strong
|
|
99
|
+
<strong
|
|
100
|
+
class="font-normal"
|
|
101
|
+
itemprop="name"
|
|
102
|
+
>{{ crumb.name }}</strong>
|
|
97
103
|
</a>
|
|
98
104
|
<a
|
|
99
105
|
v-else
|
|
@@ -102,15 +108,22 @@ const isLast = (index: number) => {
|
|
|
102
108
|
:title="`Polo 6R ${crumb.name} ${productNumber}`"
|
|
103
109
|
itemprop="item"
|
|
104
110
|
>
|
|
105
|
-
<span
|
|
111
|
+
<span
|
|
112
|
+
class="font-normal"
|
|
113
|
+
itemprop="name"
|
|
114
|
+
>
|
|
106
115
|
<span v-html="crumb.name" />
|
|
107
|
-
<b
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
<b
|
|
117
|
+
v-if="productNumber"
|
|
118
|
+
class="hidden sm:inline font-normal ml-1"
|
|
119
|
+
> {{ productNumber }}</b>
|
|
110
120
|
</span>
|
|
111
121
|
</a>
|
|
112
122
|
|
|
113
|
-
<meta
|
|
123
|
+
<meta
|
|
124
|
+
itemprop="position"
|
|
125
|
+
:content="String(props.showHome ? index + 2 : index + 1)"
|
|
126
|
+
>
|
|
114
127
|
</li>
|
|
115
128
|
</ul>
|
|
116
129
|
</nav>
|
|
@@ -27,11 +27,18 @@ const { copy, copied } = useClipboard({ source, legacy: true });
|
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<template>
|
|
30
|
-
<button
|
|
30
|
+
<button
|
|
31
|
+
:aria-label="texts.copy"
|
|
32
|
+
class="btn-copy has-tooltip"
|
|
33
|
+
@click="copy()"
|
|
34
|
+
>
|
|
31
35
|
<span
|
|
32
36
|
:class="`tooltip rounded-full btn-copy-text ${tooltipClasses}`"
|
|
33
37
|
:data-text="!copied ? texts.copy : texts.copied"
|
|
34
38
|
/>
|
|
35
|
-
<span
|
|
39
|
+
<span
|
|
40
|
+
i-ph-copy-simple-light
|
|
41
|
+
class="leading-none w-full h-full"
|
|
42
|
+
/>
|
|
36
43
|
</button>
|
|
37
44
|
</template>
|
|
@@ -16,11 +16,18 @@ const props = defineProps({
|
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
18
|
<template>
|
|
19
|
-
<h2
|
|
19
|
+
<h2
|
|
20
|
+
v-if="props.caption"
|
|
21
|
+
class="features-list-caption"
|
|
22
|
+
>
|
|
20
23
|
{{ props.caption }}
|
|
21
24
|
</h2>
|
|
22
25
|
<ul class="features-list-ul">
|
|
23
|
-
<li
|
|
26
|
+
<li
|
|
27
|
+
v-for="(item, index) in props.items"
|
|
28
|
+
:key="index"
|
|
29
|
+
class="features-list-item"
|
|
30
|
+
>
|
|
24
31
|
{{ item }}
|
|
25
32
|
</li>
|
|
26
33
|
</ul>
|
|
@@ -1,20 +1,47 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
<div
|
|
3
|
+
class="relative flex"
|
|
4
|
+
title="FUCK PUTIN! FUCK RUZZIA!"
|
|
5
|
+
>
|
|
6
|
+
<div
|
|
7
|
+
i-mdi:tank
|
|
8
|
+
class="inline-block bg-ukraine mr-1 mt-0.5"
|
|
9
|
+
/>
|
|
10
|
+
<div
|
|
11
|
+
i-mdi:tank
|
|
12
|
+
class="inline-block bg-ukraine mr-1 mt-0.5"
|
|
13
|
+
/>
|
|
14
|
+
<div
|
|
15
|
+
i-mdi:tank
|
|
16
|
+
class="inline-block bg-ukraine mr-5 mt-0.5"
|
|
17
|
+
/>
|
|
6
18
|
<div class="relative">
|
|
7
|
-
<div
|
|
8
|
-
|
|
19
|
+
<div
|
|
20
|
+
class="inline-block absolute"
|
|
21
|
+
i-noto-v1:flag-for-flag-russia
|
|
22
|
+
/>
|
|
23
|
+
<div
|
|
24
|
+
i-emojione:fire
|
|
25
|
+
class="inline-block absolute -mt-1 animate-ping animate-pulseR"
|
|
26
|
+
/>
|
|
9
27
|
</div>
|
|
10
28
|
<div class="absolute left-0 animate-bull animate-bull--1 -mt-1">
|
|
11
|
-
<div
|
|
29
|
+
<div
|
|
30
|
+
i-mdi:bullet
|
|
31
|
+
class="rotate-90 inline-block bg-yellow"
|
|
32
|
+
/>
|
|
12
33
|
</div>
|
|
13
34
|
<div class="absolute left-0 animate-bull animate-bull animate-bull--2 -mt-1">
|
|
14
|
-
<div
|
|
35
|
+
<div
|
|
36
|
+
i-mdi:bullet
|
|
37
|
+
class="rotate-90 inline-block bg-yellow"
|
|
38
|
+
/>
|
|
15
39
|
</div>
|
|
16
40
|
<div class="absolute left-0 animate-bull animate-bull--3 -mt-1">
|
|
17
|
-
<div
|
|
41
|
+
<div
|
|
42
|
+
i-mdi:bullet
|
|
43
|
+
class="rotate-90 inline-block bg-yellow"
|
|
44
|
+
/>
|
|
18
45
|
</div>
|
|
19
46
|
</div>
|
|
20
47
|
</template>
|
package/src/components/Input.vue
CHANGED
|
@@ -125,18 +125,31 @@ const handleBlur = (event: globalThis.FocusEvent) => emit('blur', event);
|
|
|
125
125
|
@input="handleInput"
|
|
126
126
|
@focus="handleFocus"
|
|
127
127
|
@blur="handleBlur"
|
|
128
|
-
|
|
128
|
+
>
|
|
129
129
|
|
|
130
|
-
<label
|
|
130
|
+
<label
|
|
131
|
+
:for="id"
|
|
132
|
+
:class="labelClass"
|
|
133
|
+
style="transform-origin: top left"
|
|
134
|
+
>
|
|
131
135
|
{{ label }}
|
|
132
|
-
<span
|
|
136
|
+
<span
|
|
137
|
+
v-if="required"
|
|
138
|
+
class="text-red-500 ml-1"
|
|
139
|
+
>*</span>
|
|
133
140
|
</label>
|
|
134
141
|
|
|
135
|
-
<div
|
|
142
|
+
<div
|
|
143
|
+
v-if="error && typeof error === 'string'"
|
|
144
|
+
class="input-error-message"
|
|
145
|
+
>
|
|
136
146
|
{{ error }}
|
|
137
147
|
</div>
|
|
138
148
|
|
|
139
|
-
<div
|
|
149
|
+
<div
|
|
150
|
+
v-if="success && typeof success === 'string'"
|
|
151
|
+
class="input-success-message"
|
|
152
|
+
>
|
|
140
153
|
{{ success }}
|
|
141
154
|
</div>
|
|
142
155
|
</div>
|
|
@@ -7,13 +7,23 @@ const colorCategories = Object.entries(colors);
|
|
|
7
7
|
|
|
8
8
|
<template>
|
|
9
9
|
<div class="flex flex-col space-y-12">
|
|
10
|
-
<div
|
|
10
|
+
<div
|
|
11
|
+
v-for="[category, shades] in colorCategories"
|
|
12
|
+
:key="category"
|
|
13
|
+
>
|
|
11
14
|
<h3 class="capitalize">
|
|
12
15
|
{{ category }}
|
|
13
16
|
</h3>
|
|
14
17
|
<div class="grid grid-cols-3 md:grid-cols-9">
|
|
15
|
-
<div
|
|
16
|
-
|
|
18
|
+
<div
|
|
19
|
+
v-for="(value, name) in shades"
|
|
20
|
+
:key="name"
|
|
21
|
+
class="mb-6"
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
class="h-12 transition-all"
|
|
25
|
+
:style="`background: ${value}`"
|
|
26
|
+
/>
|
|
17
27
|
<div class="text-sm flex flex-col text-center font-mono text-slate-500">
|
|
18
28
|
<span>{{ name }}</span>
|
|
19
29
|
<span class="uppercase text-xs">{{ value }}</span>
|
|
@@ -8,11 +8,10 @@ const props = defineProps<{
|
|
|
8
8
|
<label class="group text-left w-full max-w-xs flex flex-col">
|
|
9
9
|
<span
|
|
10
10
|
class="group-hover:text-blue-medium ml-2 text-slate-600 text-sm group-focus-within:text-blue-medium"
|
|
11
|
-
|
|
12
|
-
>
|
|
11
|
+
>{{ props.label }}</span>
|
|
13
12
|
<input
|
|
14
13
|
class="group-hover:border-blue-lightest border px-4 py-2 transition-colors rounded-md w-full focus:ring focus:outline-none focus:border-blue-medium"
|
|
15
14
|
type="text"
|
|
16
|
-
|
|
15
|
+
>
|
|
17
16
|
</label>
|
|
18
17
|
</template>
|
|
@@ -9,7 +9,10 @@ const props = defineProps({
|
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
11
|
<template>
|
|
12
|
-
<button
|
|
12
|
+
<button
|
|
13
|
+
class="product-button"
|
|
14
|
+
:class="props.shadow ? 'drop-shadow hover:(drop-shadow-md)' : ''"
|
|
15
|
+
>
|
|
13
16
|
<slot />
|
|
14
17
|
</button>
|
|
15
18
|
</template>
|
|
@@ -15,7 +15,10 @@ const props = defineProps({
|
|
|
15
15
|
|
|
16
16
|
<template>
|
|
17
17
|
<div class="font-bold detail-name w-full sm:w-50 flex">
|
|
18
|
-
<span
|
|
18
|
+
<span
|
|
19
|
+
class="colon-after"
|
|
20
|
+
:class="styles && styles.length ? styles : 'mt-auto'"
|
|
21
|
+
>
|
|
19
22
|
{{ props.text }}
|
|
20
23
|
</span>
|
|
21
24
|
</div>
|
|
@@ -30,18 +30,28 @@ const props = defineProps({
|
|
|
30
30
|
|
|
31
31
|
<template>
|
|
32
32
|
<div v-if="details && details.length">
|
|
33
|
-
<template
|
|
33
|
+
<template
|
|
34
|
+
v-for="(detail, index) in details"
|
|
35
|
+
:key="index"
|
|
36
|
+
>
|
|
34
37
|
<!-- PDP PAGE - PRODUCT ROW -->
|
|
35
38
|
<!-- <pre>{{ JSON.stringify(details) }}</pre> -->
|
|
36
39
|
<li
|
|
37
40
|
v-if="props.small"
|
|
38
41
|
class="text-xs md:text-sm text-slate-darkest dark:text-neutral-light leading-tight font-textlight md:font-textregular"
|
|
39
42
|
>
|
|
40
|
-
<span
|
|
43
|
+
<span
|
|
44
|
+
v-if="detail.id"
|
|
45
|
+
class="inline-block mr-1 items--0"
|
|
46
|
+
>
|
|
41
47
|
{{ getTranslation(`detail.${detail.id}`) }}:
|
|
42
48
|
</span>
|
|
43
49
|
|
|
44
|
-
<span
|
|
50
|
+
<span
|
|
51
|
+
v-if="detail.translated"
|
|
52
|
+
:class="detail.id ? 'font-semibold' : ''"
|
|
53
|
+
class="items--1"
|
|
54
|
+
>
|
|
45
55
|
{{ detail.value }}
|
|
46
56
|
</span>
|
|
47
57
|
|
|
@@ -49,16 +59,26 @@ const props = defineProps({
|
|
|
49
59
|
v-else-if="detail.value !== undefined && detail.id === 'color' && detail.isArrayValue"
|
|
50
60
|
class="items--2 inline-block"
|
|
51
61
|
>
|
|
52
|
-
<span
|
|
62
|
+
<span
|
|
63
|
+
v-for="(color, indexColor) in detail.value"
|
|
64
|
+
:key="indexColor"
|
|
65
|
+
class="comma"
|
|
66
|
+
>
|
|
53
67
|
{{ color['name'] }}
|
|
54
68
|
</span>
|
|
55
69
|
</div>
|
|
56
70
|
|
|
57
|
-
<span
|
|
71
|
+
<span
|
|
72
|
+
v-else-if="detail.id !== 'paint-marks' && !detail.isArrayValue"
|
|
73
|
+
class="items--3"
|
|
74
|
+
>
|
|
58
75
|
{{ locale === 'en' ? String(detail.value).replace(/,/g, '.') : String(detail.value) }}
|
|
59
76
|
</span>
|
|
60
77
|
|
|
61
|
-
<span
|
|
78
|
+
<span
|
|
79
|
+
v-else-if="detail.id && detail.id === 'paint-marks'"
|
|
80
|
+
class="items items--4"
|
|
81
|
+
>
|
|
62
82
|
{{ detail.value }}
|
|
63
83
|
<!-- <span v-for="(mark, index2) in JSON.parse(String(detail.value))" :key="index2" class="item">
|
|
64
84
|
{{ mark[0] }} x <span>{{ getTranslation(`color.${mark[1]}`) }}</span>
|
|
@@ -71,8 +91,14 @@ const props = defineProps({
|
|
|
71
91
|
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"
|
|
72
92
|
>
|
|
73
93
|
<!-- PRODUCT CARD -->
|
|
74
|
-
<ProductDetailName
|
|
75
|
-
|
|
94
|
+
<ProductDetailName
|
|
95
|
+
v-if="detail.id"
|
|
96
|
+
:text="getTranslation(`detail.${detail.id}`)"
|
|
97
|
+
/>
|
|
98
|
+
<div
|
|
99
|
+
v-if="detail.value"
|
|
100
|
+
class="leading-4 flex items-end"
|
|
101
|
+
>
|
|
76
102
|
<span
|
|
77
103
|
v-if="detail.translated && !detail.isArrayValue"
|
|
78
104
|
:class="detail.id === 'light-function' ? 'whitespace-pre-line' : ''"
|
|
@@ -80,25 +106,34 @@ const props = defineProps({
|
|
|
80
106
|
{{ getTranslation(`detail.value.${detail.value}`) }}
|
|
81
107
|
</span>
|
|
82
108
|
|
|
83
|
-
<span
|
|
109
|
+
<span
|
|
110
|
+
v-else-if="detail.id !== 'paint-marks' && !detail.isArrayValue"
|
|
111
|
+
class=""
|
|
112
|
+
>
|
|
84
113
|
{{ detail.value }}
|
|
85
114
|
</span>
|
|
86
115
|
|
|
87
116
|
<span
|
|
88
117
|
v-else-if="
|
|
89
118
|
detail.value !== undefined &&
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
detail.id === 'for-exterior-colour' &&
|
|
120
|
+
detail.isArrayValue
|
|
92
121
|
"
|
|
93
122
|
class=""
|
|
94
123
|
>
|
|
95
|
-
<div
|
|
124
|
+
<div
|
|
125
|
+
v-for="(color, indexColor) in JSON.parse(String(detail.value))"
|
|
126
|
+
:key="indexColor"
|
|
127
|
+
>
|
|
96
128
|
<span class="font-mono">{{ color }}</span> -
|
|
97
129
|
{{ getTranslation(`colorCodes.${color}`) }}
|
|
98
130
|
</div>
|
|
99
131
|
</span>
|
|
100
132
|
|
|
101
|
-
<span
|
|
133
|
+
<span
|
|
134
|
+
v-else-if="detail.id && detail.id === 'paint-marks'"
|
|
135
|
+
class="items"
|
|
136
|
+
>
|
|
102
137
|
<span
|
|
103
138
|
v-for="(mark, markIndex) in JSON.parse(String(detail.value))"
|
|
104
139
|
:key="markIndex"
|
|
@@ -107,8 +142,15 @@ const props = defineProps({
|
|
|
107
142
|
{{ mark[0] }} x <span>{{ getTranslation(`color.${mark[1]}`) }}</span>
|
|
108
143
|
</span>
|
|
109
144
|
</span>
|
|
110
|
-
<ul
|
|
111
|
-
|
|
145
|
+
<ul
|
|
146
|
+
v-else-if="detail.id && detail.isArrayValue"
|
|
147
|
+
class="items"
|
|
148
|
+
>
|
|
149
|
+
<li
|
|
150
|
+
v-for="(d, index3) in JSON.parse(String(detail.value))"
|
|
151
|
+
:key="index3"
|
|
152
|
+
class="item"
|
|
153
|
+
>
|
|
112
154
|
· {{ d }}
|
|
113
155
|
</li>
|
|
114
156
|
</ul>
|
|
@@ -20,7 +20,10 @@ 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
|
|
23
|
+
<div
|
|
24
|
+
i-system-uicons-document-justified
|
|
25
|
+
class="text-blue-lightest dark:text-accent-light"
|
|
26
|
+
/>
|
|
24
27
|
<span>{{ props.file.name }}</span>
|
|
25
28
|
</a>
|
|
26
29
|
</li>
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="product-link"
|
|
4
|
+
itemscope
|
|
5
|
+
itemtype="https://schema.org/Product"
|
|
6
|
+
>
|
|
3
7
|
<div
|
|
4
8
|
:class="[bigTile ? 'product-link--big-tile' : 'product-thumb--plp product-thumb--carousel']"
|
|
5
9
|
>
|
|
6
10
|
<!-- Slot na ProductImage z Astro -->
|
|
7
11
|
<slot name="image">
|
|
8
|
-
<img
|
|
12
|
+
<img
|
|
13
|
+
src="/1x1.png"
|
|
14
|
+
class="bg-neutral-lightest/70"
|
|
15
|
+
:alt="productName"
|
|
16
|
+
>
|
|
9
17
|
</slot>
|
|
10
18
|
</div>
|
|
11
19
|
|
|
12
20
|
<div :class="[bigTile ? '' : 'sm:pl-4']">
|
|
13
|
-
<p
|
|
21
|
+
<p
|
|
22
|
+
v-if="price"
|
|
23
|
+
class="block mb-2 font-600 font-headbold text-5"
|
|
24
|
+
>
|
|
14
25
|
{{ price }}
|
|
15
26
|
</p>
|
|
16
27
|
|
|
@@ -22,11 +33,20 @@
|
|
|
22
33
|
v-html="nameFormatted"
|
|
23
34
|
/>
|
|
24
35
|
|
|
25
|
-
<ProductNumber
|
|
36
|
+
<ProductNumber
|
|
37
|
+
:product-number="productNumber"
|
|
38
|
+
:copy-disabled="true"
|
|
39
|
+
/>
|
|
26
40
|
|
|
27
41
|
<template v-if="index !== null">
|
|
28
|
-
<meta
|
|
29
|
-
|
|
42
|
+
<meta
|
|
43
|
+
itemprop="position"
|
|
44
|
+
:content="index.toString()"
|
|
45
|
+
>
|
|
46
|
+
<meta
|
|
47
|
+
itemprop="name"
|
|
48
|
+
:content="nameFormatted"
|
|
49
|
+
>
|
|
30
50
|
</template>
|
|
31
51
|
</div>
|
|
32
52
|
</div>
|
|
@@ -10,7 +10,10 @@ const props = defineProps({
|
|
|
10
10
|
});
|
|
11
11
|
</script>
|
|
12
12
|
<template>
|
|
13
|
-
<strong
|
|
13
|
+
<strong
|
|
14
|
+
class="product-model"
|
|
15
|
+
:data-pagefind-filter="`model:${props.carModel.name}`"
|
|
16
|
+
>{{
|
|
14
17
|
props.carModel.name
|
|
15
18
|
}}</strong>
|
|
16
19
|
</template>
|
|
@@ -11,8 +11,16 @@ const props = defineProps({
|
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
13
|
<template>
|
|
14
|
-
<div
|
|
15
|
-
|
|
14
|
+
<div
|
|
15
|
+
inline-flex
|
|
16
|
+
flex-wrap
|
|
17
|
+
max-w-max
|
|
18
|
+
>
|
|
19
|
+
<span
|
|
20
|
+
v-for="(modelId, index) in props.modelIds"
|
|
21
|
+
:key="index"
|
|
22
|
+
class="product-model block"
|
|
23
|
+
>
|
|
16
24
|
<ProductModel :id="modelId" />
|
|
17
25
|
</span>
|
|
18
26
|
</div>
|
|
@@ -16,7 +16,11 @@ const props = defineProps({
|
|
|
16
16
|
</script>
|
|
17
17
|
|
|
18
18
|
<template>
|
|
19
|
-
<span
|
|
19
|
+
<span
|
|
20
|
+
v-for="position in props.positions"
|
|
21
|
+
:key="position.sort"
|
|
22
|
+
class="product-position"
|
|
23
|
+
>
|
|
20
24
|
{{ position.name }}
|
|
21
25
|
</span>
|
|
22
26
|
</template>
|
|
@@ -24,8 +24,15 @@ const settings = {
|
|
|
24
24
|
</script>
|
|
25
25
|
|
|
26
26
|
<template>
|
|
27
|
-
<span
|
|
28
|
-
|
|
27
|
+
<span
|
|
28
|
+
v-for="(prcode, index) in settings.prcodes"
|
|
29
|
+
:key="index"
|
|
30
|
+
class="not-last:mr-1"
|
|
31
|
+
>
|
|
32
|
+
<PrCode
|
|
33
|
+
v-if="!String(prcode).includes('+')"
|
|
34
|
+
:prcode="prcode"
|
|
35
|
+
/>
|
|
29
36
|
<span v-else>
|
|
30
37
|
<PrCode
|
|
31
38
|
v-for="(splittedCode, index2) in String(prcode).split('+')"
|
|
@@ -23,7 +23,10 @@ const props = defineProps({
|
|
|
23
23
|
</script>
|
|
24
24
|
|
|
25
25
|
<template>
|
|
26
|
-
<component
|
|
26
|
+
<component
|
|
27
|
+
:is="props.as"
|
|
28
|
+
class="font-bold detail-name w-full sm:w-50 flex 2xl:w-64"
|
|
29
|
+
>
|
|
27
30
|
<span :class="styles && styles.length ? styles : 'mt-auto'">
|
|
28
31
|
<b class="bg-white z-1 colon-after pr-1">{{ props.text }}</b>
|
|
29
32
|
</span>
|
|
@@ -2,30 +2,18 @@
|
|
|
2
2
|
import { PropType, computed } from 'vue';
|
|
3
3
|
import ProductDetailName from './ProductDetailName.vue';
|
|
4
4
|
|
|
5
|
-
interface
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
interface Link {
|
|
6
|
+
type: string;
|
|
7
|
+
url: string;
|
|
8
|
+
anchor: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
interface TableItem {
|
|
11
12
|
id: string;
|
|
12
|
-
|
|
13
|
-
value: unknown; //
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
isArrayValue?: boolean;
|
|
17
|
-
isColorArray?: boolean; // dla product.colors (color_ids)
|
|
18
|
-
isPaintMarks?: boolean; // dla product.paint_marks_text
|
|
19
|
-
isGenericArray?: boolean; // dla ogólnych tablic stringów (np. position)
|
|
20
|
-
isForExteriorColour?: boolean; // Ta flaga będzie ustawiana przez getProductDetails na true, jeśli 'value' jest tablicą
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface GroupedLink {
|
|
24
|
-
id: string;
|
|
25
|
-
links: {
|
|
26
|
-
name: string;
|
|
27
|
-
value: string;
|
|
28
|
-
}[];
|
|
13
|
+
label: string; // Display name from API
|
|
14
|
+
value: unknown; // Can be string, number, boolean, string array, or Link array
|
|
15
|
+
type?: string; // 'links' for link arrays
|
|
16
|
+
isGenericArray?: boolean; // for generic string arrays (e.g., position)
|
|
29
17
|
}
|
|
30
18
|
|
|
31
19
|
const props = defineProps({
|
|
@@ -33,21 +21,9 @@ const props = defineProps({
|
|
|
33
21
|
caption: { type: String, default: null },
|
|
34
22
|
});
|
|
35
23
|
|
|
36
|
-
// Function to check if a
|
|
37
|
-
const
|
|
38
|
-
return
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Function to check if it's a color array (for 'color_ids' field from product.colors)
|
|
42
|
-
// This will still apply to the 'color' detail if its value is an array of ColorCode objects
|
|
43
|
-
const isColorArray = (item: TableItem) => {
|
|
44
|
-
const colorIds = ['color', 'thread-color']; // lista ID które są kolorami
|
|
45
|
-
return (item.isColorArray || colorIds.includes(item.id)) && Array.isArray(item.value);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// Function to check if it's paint marks (value is now a string from API)
|
|
49
|
-
const isPaintMarks = (item: TableItem) => {
|
|
50
|
-
return item.isPaintMarks && typeof item.value === 'string';
|
|
24
|
+
// Function to check if it's a links array from new API
|
|
25
|
+
const isLinksArray = (item: TableItem) => {
|
|
26
|
+
return item.type === 'links' && Array.isArray(item.value);
|
|
51
27
|
};
|
|
52
28
|
|
|
53
29
|
// Function to check if it's a generic array (e.g., for position)
|
|
@@ -55,33 +31,23 @@ const isGenericArray = (item: TableItem) => {
|
|
|
55
31
|
return item.isGenericArray && Array.isArray(item.value);
|
|
56
32
|
};
|
|
57
33
|
|
|
58
|
-
// ✅ Zaktualizowana funkcja: Sprawdzamy ID i czy value jest faktycznie tablicą ColorCode[]
|
|
59
|
-
const isForExteriorColour = (item: TableItem) => {
|
|
60
|
-
return item.id === 'for-exterior-colour' && Array.isArray(item.value);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
34
|
// Function to check if value is HTML string (fallback)
|
|
64
35
|
const isHtmlValue = (value: unknown): boolean => {
|
|
65
36
|
return typeof value === 'string' && (value.includes('<span') || value.includes('<br>'));
|
|
66
37
|
};
|
|
67
38
|
|
|
68
39
|
// Function for specifying header text
|
|
69
|
-
const getHeaderText = (row: TableItem
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return 'name' in row
|
|
75
|
-
? row.name
|
|
76
|
-
: row.id
|
|
77
|
-
.split('-')
|
|
78
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
79
|
-
.join(' ');
|
|
40
|
+
const getHeaderText = (row: TableItem) => {
|
|
41
|
+
return row.label || row.id
|
|
42
|
+
.split('-')
|
|
43
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
44
|
+
.join(' ');
|
|
80
45
|
};
|
|
81
46
|
|
|
82
47
|
// Function to determine the icon class for a link type
|
|
83
|
-
const getLinkIconClass = (
|
|
84
|
-
|
|
48
|
+
const getLinkIconClass = (linkType: string) => {
|
|
49
|
+
const type = linkType.toLowerCase();
|
|
50
|
+
switch (type) {
|
|
85
51
|
case 'blog':
|
|
86
52
|
return 'i-lucide-book-text';
|
|
87
53
|
case 'youtube':
|
|
@@ -93,46 +59,13 @@ const getLinkIconClass = (linkId: string) => {
|
|
|
93
59
|
}
|
|
94
60
|
};
|
|
95
61
|
|
|
96
|
-
//
|
|
97
|
-
const
|
|
98
|
-
// ✅ Add validation to ensure props.items is an array
|
|
62
|
+
// Validate items
|
|
63
|
+
const validatedItems = computed(() => {
|
|
99
64
|
if (!Array.isArray(props.items)) {
|
|
100
65
|
console.warn('ProductDetailsList: items prop is not an array:', props.items);
|
|
101
66
|
return [];
|
|
102
67
|
}
|
|
103
|
-
|
|
104
|
-
const result: (TableItem | GroupedLink)[] = [];
|
|
105
|
-
const linkGroups = new Map<string, GroupedLink>();
|
|
106
|
-
|
|
107
|
-
// Process all elements
|
|
108
|
-
for (const item of props.items) {
|
|
109
|
-
// If it's a link (blog, youtube, vimeo)
|
|
110
|
-
if (isLink(item.id)) {
|
|
111
|
-
// Add a link to the relevant group
|
|
112
|
-
if (!linkGroups.has(item.id)) {
|
|
113
|
-
linkGroups.set(item.id, {
|
|
114
|
-
id: item.id,
|
|
115
|
-
links: [],
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Add link to the relevant group
|
|
120
|
-
linkGroups.get(item.id)?.links.push({
|
|
121
|
-
name: item.name,
|
|
122
|
-
value: item.value as string,
|
|
123
|
-
});
|
|
124
|
-
} else {
|
|
125
|
-
// If it's not a link, add it normally to the results
|
|
126
|
-
result.push(item);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Add all link groups at the end
|
|
131
|
-
for (const group of linkGroups.values()) {
|
|
132
|
-
result.push(group);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return result;
|
|
68
|
+
return props.items;
|
|
136
69
|
});
|
|
137
70
|
</script>
|
|
138
71
|
|
|
@@ -144,74 +77,53 @@ const groupedItems = computed(() => {
|
|
|
144
77
|
</slot>
|
|
145
78
|
</caption>
|
|
146
79
|
<colgroup>
|
|
147
|
-
<col class="details-table-col"
|
|
148
|
-
<col class="details-table-col"
|
|
80
|
+
<col class="details-table-col">
|
|
81
|
+
<col class="details-table-col">
|
|
149
82
|
</colgroup>
|
|
150
83
|
<tbody>
|
|
151
|
-
<tr
|
|
152
|
-
|
|
84
|
+
<tr
|
|
85
|
+
v-for="(row, index) in validatedItems"
|
|
86
|
+
:key="index"
|
|
87
|
+
class="details-table-row"
|
|
88
|
+
>
|
|
89
|
+
<ProductDetailName
|
|
90
|
+
as="th"
|
|
91
|
+
:text="getHeaderText(row)"
|
|
92
|
+
class="details-table-header"
|
|
93
|
+
/>
|
|
153
94
|
|
|
154
|
-
<td
|
|
95
|
+
<td
|
|
96
|
+
v-if="isLinksArray(row)"
|
|
97
|
+
class="details-table-cell"
|
|
98
|
+
>
|
|
155
99
|
<ul class="list-none p-0 m-0">
|
|
156
100
|
<li
|
|
157
|
-
v-for="(link, linkIndex) in row.
|
|
101
|
+
v-for="(link, linkIndex) in row.value as Link[]"
|
|
158
102
|
:key="linkIndex"
|
|
159
103
|
class="mb-2 last:mb-0 flex items-center"
|
|
160
104
|
>
|
|
161
105
|
<span
|
|
162
106
|
:class="[
|
|
163
|
-
getLinkIconClass(
|
|
107
|
+
getLinkIconClass(link.type),
|
|
164
108
|
'leading-none inline-block mr-2 w-4 min-w-4 h-4 text-gray-400',
|
|
165
109
|
]"
|
|
166
110
|
/>
|
|
167
|
-
<a
|
|
168
|
-
|
|
111
|
+
<a
|
|
112
|
+
:href="link.url"
|
|
113
|
+
target="_blank"
|
|
114
|
+
rel="noopener noreferrer"
|
|
115
|
+
class="link-primary"
|
|
116
|
+
>
|
|
117
|
+
{{ link.anchor }}
|
|
169
118
|
</a>
|
|
170
119
|
</li>
|
|
171
120
|
</ul>
|
|
172
121
|
</td>
|
|
173
122
|
|
|
174
|
-
<td
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
:key="colorIndex"
|
|
179
|
-
class="flex items-center gap-1 mb-1 last:mb-0"
|
|
180
|
-
>
|
|
181
|
-
<template v-if="colorItem.code">
|
|
182
|
-
<code class="font-mono text-sm">
|
|
183
|
-
{{ colorItem.code }}
|
|
184
|
-
</code>
|
|
185
|
-
<span class="text-gray-400">-</span>
|
|
186
|
-
</template>
|
|
187
|
-
<span class="text-gray-700 dark:text-gray-300">{{ colorItem.name }}</span>
|
|
188
|
-
</li>
|
|
189
|
-
</ul>
|
|
190
|
-
</td>
|
|
191
|
-
|
|
192
|
-
<td v-else-if="'id' in row && isPaintMarks(row)" class="details-table-cell">
|
|
193
|
-
<span class="text-gray-700 dark:text-gray-300">{{ row.value }}</span>
|
|
194
|
-
</td>
|
|
195
|
-
|
|
196
|
-
<td v-else-if="'id' in row && isForExteriorColour(row)" class="details-table-cell">
|
|
197
|
-
<ul class="list-none p-0 m-0">
|
|
198
|
-
<li
|
|
199
|
-
v-for="(colorEntry, colourIndex) in row.value as ColorCode[]"
|
|
200
|
-
:key="colourIndex"
|
|
201
|
-
class="flex items-center gap-1 mb-1 last:mb-0"
|
|
202
|
-
>
|
|
203
|
-
<template v-if="colorEntry.code">
|
|
204
|
-
<code class="font-mono text-sm">
|
|
205
|
-
{{ colorEntry.code }}
|
|
206
|
-
</code>
|
|
207
|
-
<span class="text-gray-400">-</span>
|
|
208
|
-
</template>
|
|
209
|
-
<span class="text-gray-700 dark:text-gray-300">{{ colorEntry.name }}</span>
|
|
210
|
-
</li>
|
|
211
|
-
</ul>
|
|
212
|
-
</td>
|
|
213
|
-
|
|
214
|
-
<td v-else-if="'id' in row && isGenericArray(row)" class="details-table-cell">
|
|
123
|
+
<td
|
|
124
|
+
v-else-if="isGenericArray(row)"
|
|
125
|
+
class="details-table-cell"
|
|
126
|
+
>
|
|
215
127
|
<ul class="list-none p-0 m-0">
|
|
216
128
|
<li
|
|
217
129
|
v-for="(item, itemIndex) in row.value as string[]"
|
|
@@ -225,12 +137,14 @@ const groupedItems = computed(() => {
|
|
|
225
137
|
</td>
|
|
226
138
|
|
|
227
139
|
<td
|
|
228
|
-
v-else-if="
|
|
140
|
+
v-else-if="isHtmlValue(row.value)"
|
|
229
141
|
class="details-table-cell"
|
|
230
142
|
v-html="row.value"
|
|
231
143
|
/>
|
|
232
144
|
|
|
233
|
-
<slot
|
|
145
|
+
<slot
|
|
146
|
+
:name="row.id"
|
|
147
|
+
>
|
|
234
148
|
<td class="details-table-cell">
|
|
235
149
|
{{ row.value }}
|
|
236
150
|
</td>
|
|
@@ -15,22 +15,24 @@ const toggleVisibility = () => {
|
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
|
-
<div
|
|
18
|
+
<div
|
|
19
|
+
v-if="isShow"
|
|
20
|
+
data-pagefind-ignore
|
|
21
|
+
class="slimbanner"
|
|
22
|
+
>
|
|
19
23
|
<span
|
|
20
24
|
class="inline-block text-4xl w-6 h-3.5 min-w-[1.25rem] mr-3 bg-gradient-to-b stops-[#0057b7_50%,50%,#ffd700_100%]"
|
|
21
25
|
/>
|
|
22
|
-
<span class="leading-none"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/>
|
|
26
|
+
<span class="leading-none"><span
|
|
27
|
+
data-text="We stand with our friends and colleagues in Ukraine. To support Ukraine in their time of need visit "
|
|
28
|
+
/>
|
|
26
29
|
<a
|
|
27
30
|
href="https://polo.blue/support-ukraine/"
|
|
28
31
|
target="_blank"
|
|
29
32
|
rel="noopener"
|
|
30
33
|
title="Support Ukraine"
|
|
31
34
|
class="underline underline-offset-2 hover:text-blue-wrc"
|
|
32
|
-
|
|
33
|
-
>.
|
|
35
|
+
>this page</a>.
|
|
34
36
|
</span>
|
|
35
37
|
|
|
36
38
|
<button
|
package/src/components/Table.vue
CHANGED
|
@@ -14,14 +14,26 @@ const capitalizeFirstLetter = (text: string) => {
|
|
|
14
14
|
<table class="table-auto text-left border bg-white shadow-md">
|
|
15
15
|
<thead class="bg-gray-500 text-white">
|
|
16
16
|
<tr class="border">
|
|
17
|
-
<th
|
|
17
|
+
<th
|
|
18
|
+
v-for="(thead, index) in theads"
|
|
19
|
+
:key="index"
|
|
20
|
+
class="px-4 py-2 font-semibold"
|
|
21
|
+
>
|
|
18
22
|
{{ capitalizeFirstLetter(thead) }}
|
|
19
23
|
</th>
|
|
20
24
|
</tr>
|
|
21
25
|
</thead>
|
|
22
26
|
<tbody>
|
|
23
|
-
<tr
|
|
24
|
-
|
|
27
|
+
<tr
|
|
28
|
+
v-for="(row, index) in props.data"
|
|
29
|
+
:key="index"
|
|
30
|
+
class="border"
|
|
31
|
+
>
|
|
32
|
+
<td
|
|
33
|
+
v-for="key in Object.keys(row)"
|
|
34
|
+
:key="key"
|
|
35
|
+
class="px-4 py-2"
|
|
36
|
+
>
|
|
25
37
|
{{ row[key] }}
|
|
26
38
|
</td>
|
|
27
39
|
</tr>
|
|
@@ -18,7 +18,10 @@ const props = defineProps({
|
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<template>
|
|
21
|
-
<div
|
|
21
|
+
<div
|
|
22
|
+
v-if="props.translations !== null && props.translations.uri"
|
|
23
|
+
data-pagefind-ignore
|
|
24
|
+
>
|
|
22
25
|
<a
|
|
23
26
|
aria-label="Change language"
|
|
24
27
|
type="button"
|