spoko-design-system 1.6.0 → 1.8.0
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 +12 -0
- package/index.ts +2 -0
- package/package.json +7 -5
- package/src/components/Headline.vue +76 -71
- package/src/components/PrCode.vue +14 -52
- package/src/components/Product/ProductEngine.vue +240 -0
- package/src/components/Product/ProductEngines.vue +116 -0
- package/src/config.ts +1 -0
- package/src/layouts/MainLayout.astro +5 -0
- package/src/pages/components/headline.mdx +268 -68
- package/src/pages/components/product-engine.mdx +370 -0
- package/src/scripts/tooltips.ts +46 -0
- package/src/styles/tippy-theme.css +131 -0
- package/uno-config/index.ts +3 -1
- package/uno-config/theme/shortcuts/jumbotron.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [1.8.0](https://github.com/polo-blue/sds/compare/v1.7.0...v1.8.0) (2025-10-29)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **Headline:** add responsive design support and advanced features ([254d5d3](https://github.com/polo-blue/sds/commit/254d5d30f8eff673a48343270f0f23eb39ab7f95))
|
|
6
|
+
|
|
7
|
+
## [1.7.0](https://github.com/polo-blue/sds/compare/v1.6.0...v1.7.0) (2025-10-29)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* add Tippy.js tooltips with ProductEngine components and API-driven translations ([eb18a17](https://github.com/polo-blue/sds/commit/eb18a171a3e5d80889f64a645d82767898561764))
|
|
12
|
+
|
|
1
13
|
## [1.6.0](https://github.com/polo-blue/sds/compare/v1.5.3...v1.6.0) (2025-10-28)
|
|
2
14
|
|
|
3
15
|
### Features
|
package/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ export { default as Headline } from './src/components/Headline.vue';
|
|
|
19
19
|
export { default as Quote } from './src/components/Quote.vue';
|
|
20
20
|
|
|
21
21
|
export { default as ProductEngineType } from './src/components/Product/ProductEngineType.vue';
|
|
22
|
+
export { default as ProductEngine } from './src/components/Product/ProductEngine.vue';
|
|
23
|
+
export { default as ProductEngines } from './src/components/Product/ProductEngines.vue';
|
|
22
24
|
export { default as ProductButton } from './src/components/Product/ProductButton.vue';
|
|
23
25
|
export { default as ProductColors } from './src/components/Product/ProductColors.vue';
|
|
24
26
|
export { default as ProductDetailName } from './src/components/Product/ProductDetailName.vue';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spoko-design-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "./index.ts",
|
|
6
6
|
"module": "./index.ts",
|
|
@@ -53,12 +53,13 @@
|
|
|
53
53
|
],
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@algolia/client-search": "^5.41.0",
|
|
56
|
-
"@astrojs/mdx": "^4.3.
|
|
56
|
+
"@astrojs/mdx": "^4.3.9",
|
|
57
57
|
"@astrojs/node": "^9.5.0",
|
|
58
58
|
"@astrojs/sitemap": "^3.6.0",
|
|
59
59
|
"@astrojs/ts-plugin": "^1.10.5",
|
|
60
|
-
"@astrojs/vue": "^5.1.
|
|
60
|
+
"@astrojs/vue": "^5.1.2",
|
|
61
61
|
"@docsearch/css": "^4.2.0",
|
|
62
|
+
"@floating-ui/vue": "^1.1.9",
|
|
62
63
|
"@iconify-json/ant-design": "^1.2.5",
|
|
63
64
|
"@iconify-json/bi": "^1.2.6",
|
|
64
65
|
"@iconify-json/bx": "^1.2.2",
|
|
@@ -107,6 +108,7 @@
|
|
|
107
108
|
"astro-remote": "^0.3.4",
|
|
108
109
|
"dotenv": "^17.2.3",
|
|
109
110
|
"swiper": "^12.0.3",
|
|
111
|
+
"tippy.js": "^6.3.7",
|
|
110
112
|
"unocss": "66.5.4",
|
|
111
113
|
"vite-plugin-pwa": "^1.1.0",
|
|
112
114
|
"vue": "^3.5.22"
|
|
@@ -115,14 +117,14 @@
|
|
|
115
117
|
"@semantic-release/changelog": "^6.0.3",
|
|
116
118
|
"@semantic-release/git": "^10.0.1",
|
|
117
119
|
"@types/gtag.js": "^0.0.20",
|
|
118
|
-
"@types/node": "^24.9.
|
|
120
|
+
"@types/node": "^24.9.2",
|
|
119
121
|
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
|
120
122
|
"@typescript-eslint/parser": "^8.46.2",
|
|
121
123
|
"@unocss/transformer-variant-group": "66.5.4",
|
|
122
124
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
123
125
|
"@vue/compiler-sfc": "^3.5.22",
|
|
124
126
|
"@vue/eslint-config-typescript": "^14.6.0",
|
|
125
|
-
"astro": "^5.15.
|
|
127
|
+
"astro": "^5.15.2",
|
|
126
128
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
127
129
|
"eslint": "^9.38.0",
|
|
128
130
|
"eslint-plugin-astro": "^1.4.0",
|
|
@@ -1,85 +1,90 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
fontFamily: {
|
|
30
|
-
type: String as PropType<'head' | 'text' | 'novamono' | 'mono'>,
|
|
31
|
-
required: false,
|
|
32
|
-
default: 'head',
|
|
33
|
-
},
|
|
34
|
-
fontWeight: {
|
|
35
|
-
type: String as PropType<'light' | 'regular' | 'bold' | 'light-bold' | 'light-thin'>,
|
|
36
|
-
required: false,
|
|
37
|
-
default: 'regular',
|
|
38
|
-
},
|
|
39
|
-
underline: {
|
|
40
|
-
type: [Boolean, String] as PropType<boolean | 'center'>,
|
|
41
|
-
required: false,
|
|
42
|
-
default: false,
|
|
43
|
-
},
|
|
2
|
+
import { computed, useAttrs } from 'vue';
|
|
3
|
+
|
|
4
|
+
const attrs = useAttrs();
|
|
5
|
+
|
|
6
|
+
interface HeadlineProps {
|
|
7
|
+
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'div' | 'span';
|
|
8
|
+
class?: string;
|
|
9
|
+
fontFamily?: 'head' | 'text' | 'novamono' | 'mono';
|
|
10
|
+
fontWeight?: 'light' | 'regular' | 'bold' | 'light-bold' | 'light-thin';
|
|
11
|
+
underline?: boolean | 'center';
|
|
12
|
+
defaultSize?: string;
|
|
13
|
+
noFlexLayout?: boolean;
|
|
14
|
+
noMargin?: boolean;
|
|
15
|
+
noLeading?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<HeadlineProps>(), {
|
|
19
|
+
as: 'span',
|
|
20
|
+
class: '',
|
|
21
|
+
fontFamily: 'head',
|
|
22
|
+
fontWeight: 'regular',
|
|
23
|
+
underline: false,
|
|
24
|
+
defaultSize: 'text-inherit',
|
|
25
|
+
noFlexLayout: false,
|
|
26
|
+
noMargin: false,
|
|
27
|
+
noLeading: false,
|
|
44
28
|
});
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const family = props.fontFamily;
|
|
49
|
-
const weight = props.fontWeight;
|
|
30
|
+
const typographyClass = computed(() => {
|
|
31
|
+
const { fontFamily, fontWeight } = props;
|
|
50
32
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return `font-${family}`;
|
|
33
|
+
if (fontFamily === 'novamono' || fontFamily === 'mono') {
|
|
34
|
+
return `font-${fontFamily}`;
|
|
54
35
|
}
|
|
55
36
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
37
|
+
if (fontFamily === 'head') {
|
|
38
|
+
const weightMap = {
|
|
39
|
+
light: 'headline-light',
|
|
40
|
+
bold: 'headline-bold',
|
|
41
|
+
'light-bold': 'headline-light-bold',
|
|
42
|
+
'light-thin': 'headline-light-thin',
|
|
43
|
+
regular: 'headline',
|
|
44
|
+
};
|
|
45
|
+
return weightMap[fontWeight] || 'headline';
|
|
63
46
|
}
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return `font-text${weight}`;
|
|
48
|
+
if (fontFamily === 'text') {
|
|
49
|
+
return `font-text${fontWeight}`;
|
|
68
50
|
}
|
|
69
51
|
|
|
70
|
-
// Default fallback
|
|
71
52
|
return 'headline';
|
|
72
|
-
};
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const underlineClass = computed(() => {
|
|
56
|
+
if (props.underline === true) return 'headline--underline';
|
|
57
|
+
if (props.underline === 'center') return 'headline--underline-center';
|
|
58
|
+
return '';
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const layoutClass = computed(() => {
|
|
62
|
+
// Centered underline needs block + text-center
|
|
63
|
+
if (props.underline === 'center') return 'block text-center';
|
|
73
64
|
|
|
74
|
-
|
|
65
|
+
// Default flex layout for icon alignment (unless disabled)
|
|
66
|
+
if (!props.noFlexLayout) {
|
|
67
|
+
return 'flex sm:block md:flex items-center';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return '';
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const computedClasses = computed(() => {
|
|
74
|
+
const baseClasses = [];
|
|
75
|
+
|
|
76
|
+
// Conditionally add base classes
|
|
77
|
+
if (!props.noMargin) baseClasses.push('mb-2.5');
|
|
78
|
+
if (!props.noLeading) baseClasses.push('leading-none');
|
|
79
|
+
|
|
80
|
+
const sizeClasses = props.class || props.defaultSize;
|
|
81
|
+
|
|
82
|
+
return `${baseClasses.join(' ')} ${typographyClass.value} ${sizeClasses} ${underlineClass.value} ${layoutClass.value}`.trim();
|
|
83
|
+
});
|
|
75
84
|
</script>
|
|
76
85
|
|
|
77
86
|
<template>
|
|
78
|
-
<component
|
|
79
|
-
:is="props.as"
|
|
80
|
-
class="mb-2.5 leading-none"
|
|
81
|
-
:class="`${typographyClass} ${props.textSize ? `text-${props.textSize}` : 'text-xl'} ${props.underline === true ? 'headline--underline' : ''} ${props.underline === 'center' ? 'headline--underline-center block text-center' : 'flex sm:block md:flex items-center'}`"
|
|
82
|
-
>
|
|
87
|
+
<component :is="as" :class="computedClasses" v-bind="attrs">
|
|
83
88
|
<slot />
|
|
84
89
|
</component>
|
|
85
90
|
</template>
|
|
@@ -92,14 +97,14 @@ const typographyClass = getTypographyClass();
|
|
|
92
97
|
@apply content-empty absolute left-0 bottom-0;
|
|
93
98
|
height: 3px;
|
|
94
99
|
width: 55px;
|
|
95
|
-
background-color: var(--clr-primary-400);
|
|
100
|
+
background-color: var(--headline-underline-accent, var(--clr-primary-400));
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
&:before {
|
|
99
104
|
@apply content-empty absolute left-0 bottom-px h-px;
|
|
100
105
|
width: 95%;
|
|
101
106
|
max-width: 255px;
|
|
102
|
-
background-color: #64748b;
|
|
107
|
+
background-color: var(--headline-underline-base, #64748b);
|
|
103
108
|
}
|
|
104
109
|
}
|
|
105
110
|
|
|
@@ -110,7 +115,7 @@ const typographyClass = getTypographyClass();
|
|
|
110
115
|
@apply content-empty absolute left-1/2 bottom-px h-px;
|
|
111
116
|
width: 95%;
|
|
112
117
|
max-width: 255px;
|
|
113
|
-
background-color: #64748b;
|
|
118
|
+
background-color: var(--headline-underline-base, #64748b);
|
|
114
119
|
transform: translateX(-50%);
|
|
115
120
|
}
|
|
116
121
|
|
|
@@ -118,7 +123,7 @@ const typographyClass = getTypographyClass();
|
|
|
118
123
|
@apply content-empty absolute bottom-0;
|
|
119
124
|
height: 3px;
|
|
120
125
|
width: 55px;
|
|
121
|
-
background-color: var(--clr-primary-400);
|
|
126
|
+
background-color: var(--headline-underline-accent, var(--clr-primary-400));
|
|
122
127
|
left: calc(50% - min(47.5%, 127.5px));
|
|
123
128
|
}
|
|
124
129
|
}
|
|
@@ -27,69 +27,31 @@ const props = defineProps({
|
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<template>
|
|
30
|
-
<span
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</span>
|
|
41
|
-
|
|
42
|
-
<!-- Dynamic Tooltip with description from API -->
|
|
43
|
-
<div v-if="props.prcode.description" class="tooltip">
|
|
44
|
-
<div class="tooltip-content">
|
|
45
|
-
{{ props.prcode.description }}
|
|
46
|
-
<span v-if="props.prcode.group" class="tooltip-group">
|
|
47
|
-
({{ props.prcode.group }})
|
|
48
|
-
</span>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
30
|
+
<span
|
|
31
|
+
data-pagefind-filter="PR-Code"
|
|
32
|
+
class="btn-prcode"
|
|
33
|
+
:class="[
|
|
34
|
+
prcode.variant_category ? `btn-prcode--variant-${prcode.variant_category.toLowerCase()}` : '',
|
|
35
|
+
{ 'btn-prcode--pdp': isPdp }
|
|
36
|
+
]"
|
|
37
|
+
:data-tippy-content="prcode.description || undefined"
|
|
38
|
+
>
|
|
39
|
+
{{ prcode.code }}
|
|
51
40
|
</span>
|
|
52
41
|
</template>
|
|
53
42
|
|
|
54
43
|
<style scoped>
|
|
55
|
-
/* Base PrCode
|
|
44
|
+
/* Base PrCode Styles */
|
|
56
45
|
.btn-prcode {
|
|
57
|
-
@apply inline-block
|
|
46
|
+
@apply inline-block cursor-default;
|
|
47
|
+
@apply px-1.5 py-0.5;
|
|
48
|
+
@apply rounded;
|
|
58
49
|
}
|
|
59
50
|
|
|
60
51
|
.btn-prcode--pdp {
|
|
61
52
|
@apply mb-1;
|
|
62
53
|
}
|
|
63
54
|
|
|
64
|
-
/* Tooltip Styles - Similar to ProductEngine */
|
|
65
|
-
.tooltip {
|
|
66
|
-
@apply invisible absolute left-1/2 -translate-x-1/2 bottom-full mb-2 z-50;
|
|
67
|
-
@apply px-3 py-1.5 rounded-lg shadow-lg whitespace-nowrap;
|
|
68
|
-
@apply bg-blue-darker text-white text-xs;
|
|
69
|
-
@apply pointer-events-none;
|
|
70
|
-
max-width: 300px;
|
|
71
|
-
white-space: normal;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.has-tooltip:hover .tooltip {
|
|
75
|
-
@apply visible;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.tooltip-content {
|
|
79
|
-
@apply relative;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.tooltip-group {
|
|
83
|
-
@apply ml-2 opacity-75 text-xs font-light;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* Tooltip Arrow */
|
|
87
|
-
.tooltip::after {
|
|
88
|
-
content: '';
|
|
89
|
-
@apply absolute left-1/2 -translate-x-1/2 top-full;
|
|
90
|
-
@apply border-4 border-transparent border-t-blue-darker;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
55
|
/* Semantic Variant Category Colors */
|
|
94
56
|
/* GTI - Red */
|
|
95
57
|
.btn-prcode--variant-gti {
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, onMounted, onUnmounted } from 'vue';
|
|
3
|
+
import type { PropType } from 'vue';
|
|
4
|
+
import tippy, { type Instance } from 'tippy.js';
|
|
5
|
+
import 'tippy.js/dist/tippy.css';
|
|
6
|
+
import '../../styles/tippy-theme.css';
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
VAG group (VW/Audi/Skoda/Seat/Porsche/Bentley/Lamborghini/Ducati/Cupra/Scania/MAN) manufacturer Engine Code
|
|
10
|
+
Displays engine code with detailed tooltip showing: name, power, displacement, dates, etc.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
interface Engine {
|
|
14
|
+
id?: number;
|
|
15
|
+
code: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
info?: string | null;
|
|
18
|
+
serie?: {
|
|
19
|
+
value: string;
|
|
20
|
+
label: string;
|
|
21
|
+
};
|
|
22
|
+
type?: {
|
|
23
|
+
value: string;
|
|
24
|
+
translated: string;
|
|
25
|
+
label: string;
|
|
26
|
+
};
|
|
27
|
+
power?: {
|
|
28
|
+
kw: number;
|
|
29
|
+
ps: number;
|
|
30
|
+
ps_label: string;
|
|
31
|
+
label: string;
|
|
32
|
+
};
|
|
33
|
+
date?: {
|
|
34
|
+
value: string;
|
|
35
|
+
label: string;
|
|
36
|
+
};
|
|
37
|
+
displacement?: {
|
|
38
|
+
value: number;
|
|
39
|
+
label: string;
|
|
40
|
+
};
|
|
41
|
+
compression_ratio?: {
|
|
42
|
+
value: string | null;
|
|
43
|
+
label: string;
|
|
44
|
+
};
|
|
45
|
+
valves?: {
|
|
46
|
+
value: number | null;
|
|
47
|
+
label: string;
|
|
48
|
+
};
|
|
49
|
+
euro?: {
|
|
50
|
+
value: number;
|
|
51
|
+
label: string;
|
|
52
|
+
};
|
|
53
|
+
pivot?: any;
|
|
54
|
+
|
|
55
|
+
// Backward compatibility - old flat structure
|
|
56
|
+
kw?: number;
|
|
57
|
+
ps?: number;
|
|
58
|
+
cc?: number;
|
|
59
|
+
c_ratio?: string | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const props = defineProps({
|
|
63
|
+
engine: {
|
|
64
|
+
type: Object as PropType<Engine>,
|
|
65
|
+
required: true,
|
|
66
|
+
},
|
|
67
|
+
showComma: {
|
|
68
|
+
type: Boolean,
|
|
69
|
+
default: false,
|
|
70
|
+
required: false,
|
|
71
|
+
},
|
|
72
|
+
translations: {
|
|
73
|
+
type: Object as PropType<{
|
|
74
|
+
power?: string;
|
|
75
|
+
cc?: string;
|
|
76
|
+
compressionRatio?: string;
|
|
77
|
+
valves?: string;
|
|
78
|
+
euro?: string;
|
|
79
|
+
horsepowerUnit?: string; // 'PS' for German, 'KM' for Polish, 'HP' for English
|
|
80
|
+
}>,
|
|
81
|
+
default: () => ({
|
|
82
|
+
power: 'Power',
|
|
83
|
+
cc: 'CC',
|
|
84
|
+
compressionRatio: 'C. Ratio',
|
|
85
|
+
valves: 'Valves',
|
|
86
|
+
euro: 'Euro',
|
|
87
|
+
horsepowerUnit: 'PS',
|
|
88
|
+
}),
|
|
89
|
+
required: false,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const engineRef = ref<HTMLElement | null>(null);
|
|
94
|
+
let tippyInstance: Instance | null = null;
|
|
95
|
+
|
|
96
|
+
// Helper to get series value (supports both old and new API)
|
|
97
|
+
const getSerieValue = (): string => {
|
|
98
|
+
if (props.engine.serie && typeof props.engine.serie === 'object') {
|
|
99
|
+
return props.engine.serie.value;
|
|
100
|
+
}
|
|
101
|
+
// Backward compatibility - old API
|
|
102
|
+
const serie = props.engine.serie as any;
|
|
103
|
+
if (!serie) return '';
|
|
104
|
+
return serie === 3 ? 'EA288' : serie === 2 ? 'EA189' : `Serie ${serie}`;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Generate tooltip HTML content
|
|
108
|
+
const getTooltipContent = () => {
|
|
109
|
+
// Header section
|
|
110
|
+
let headerContent = `<strong>${props.engine.name || props.engine.code}</strong>`;
|
|
111
|
+
if (props.engine.info) {
|
|
112
|
+
headerContent += ` <span class="info">${props.engine.info}</span>`;
|
|
113
|
+
}
|
|
114
|
+
const serieValue = getSerieValue();
|
|
115
|
+
if (serieValue) {
|
|
116
|
+
headerContent += `<div class="series-badge">${serieValue}</div>`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const header = `<div class="tooltip-header">${headerContent}</div>`;
|
|
120
|
+
|
|
121
|
+
// Specs rows
|
|
122
|
+
const rows = [];
|
|
123
|
+
|
|
124
|
+
// Power (supports both new and old API structure)
|
|
125
|
+
const power = props.engine.power;
|
|
126
|
+
const oldKw = props.engine.kw;
|
|
127
|
+
const oldPs = props.engine.ps;
|
|
128
|
+
|
|
129
|
+
if (power || oldKw || oldPs) {
|
|
130
|
+
const powerValues = [];
|
|
131
|
+
const kw = power?.kw || oldKw;
|
|
132
|
+
const ps = power?.ps || oldPs;
|
|
133
|
+
const psLabel = power?.ps_label || props.translations.horsepowerUnit;
|
|
134
|
+
const powerLabel = power?.label || props.translations.power;
|
|
135
|
+
|
|
136
|
+
if (kw) powerValues.push(`${kw} kW`);
|
|
137
|
+
if (ps) powerValues.push(`${ps} ${psLabel}`);
|
|
138
|
+
|
|
139
|
+
if (powerValues.length) {
|
|
140
|
+
rows.push(`<div class="tooltip-row"><span class="tooltip-label">${powerLabel}:</span><span class="tooltip-value">${powerValues.join(' / ')}</span></div>`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Displacement (CC)
|
|
145
|
+
const displacement = props.engine.displacement;
|
|
146
|
+
const oldCc = props.engine.cc;
|
|
147
|
+
|
|
148
|
+
if (displacement || oldCc) {
|
|
149
|
+
const ccValue = displacement?.value || oldCc;
|
|
150
|
+
const ccLabel = displacement?.label || props.translations.cc;
|
|
151
|
+
|
|
152
|
+
if (ccValue) {
|
|
153
|
+
rows.push(`<div class="tooltip-row"><span class="tooltip-label">${ccLabel}:</span><span class="tooltip-value">${ccValue} cm³</span></div>`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Euro standard
|
|
158
|
+
const euro = props.engine.euro;
|
|
159
|
+
|
|
160
|
+
if (euro && typeof euro === 'object') {
|
|
161
|
+
if (euro.value) {
|
|
162
|
+
rows.push(`<div class="tooltip-row"><span class="tooltip-label">${euro.label}:</span><span class="tooltip-value">Euro ${euro.value}</span></div>`);
|
|
163
|
+
}
|
|
164
|
+
} else if (euro) {
|
|
165
|
+
// Backward compatibility - old API
|
|
166
|
+
rows.push(`<div class="tooltip-row"><span class="tooltip-label">${props.translations.euro}:</span><span class="tooltip-value">Euro ${euro}</span></div>`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const specsContent = rows.length
|
|
170
|
+
? `<div class="tooltip-specs">${rows.join('')}</div>`
|
|
171
|
+
: '';
|
|
172
|
+
|
|
173
|
+
return header + specsContent;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
onMounted(() => {
|
|
177
|
+
if (engineRef.value) {
|
|
178
|
+
tippyInstance = tippy(engineRef.value, {
|
|
179
|
+
content: getTooltipContent(),
|
|
180
|
+
allowHTML: true,
|
|
181
|
+
theme: 'sds',
|
|
182
|
+
placement: 'top',
|
|
183
|
+
arrow: true,
|
|
184
|
+
animation: 'shift-away',
|
|
185
|
+
duration: [200, 150],
|
|
186
|
+
maxWidth: 280,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
onUnmounted(() => {
|
|
192
|
+
if (tippyInstance) {
|
|
193
|
+
tippyInstance.destroy();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
</script>
|
|
197
|
+
|
|
198
|
+
<template>
|
|
199
|
+
<span
|
|
200
|
+
ref="engineRef"
|
|
201
|
+
class="engine-code"
|
|
202
|
+
:class="`engine-code-${engine.code}`"
|
|
203
|
+
>
|
|
204
|
+
{{ engine.code }}<span v-if="showComma">,</span>
|
|
205
|
+
</span>
|
|
206
|
+
</template>
|
|
207
|
+
|
|
208
|
+
<style scoped>
|
|
209
|
+
/* Engine Code Styles */
|
|
210
|
+
.engine-code {
|
|
211
|
+
@apply inline-block mr-1;
|
|
212
|
+
@apply underline decoration-dotted underline-offset-4 py-0.5;
|
|
213
|
+
@apply decoration-neutral-light cursor-default;
|
|
214
|
+
@apply transition-colors duration-200;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.engine-code:hover {
|
|
218
|
+
@apply decoration-blue-darker dark:decoration-blue-light;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Semantic Engine Code Colors */
|
|
222
|
+
/* GTI Engines - Red */
|
|
223
|
+
.engine-code-CAVE,
|
|
224
|
+
.engine-code-CTHE,
|
|
225
|
+
.engine-code-DAJA,
|
|
226
|
+
.engine-code-DAYB {
|
|
227
|
+
@apply text-red-600 dark:text-red-500;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* WRC R Engine - Blue */
|
|
231
|
+
.engine-code-CDLJ {
|
|
232
|
+
@apply text-blue-600 dark:text-blue-500;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Special Blue Engines */
|
|
236
|
+
.engine-code-CPTA,
|
|
237
|
+
.engine-code-CZEA {
|
|
238
|
+
@apply text-blue-700 dark:text-blue-600;
|
|
239
|
+
}
|
|
240
|
+
</style>
|