spoko-design-system 1.9.0 → 1.9.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.
- package/.claude/settings.json +1 -1
- package/CHANGELOG.md +29 -0
- package/index.ts +8 -2
- package/package.json +4 -3
- package/src/components/Product/ProductEngine.astro +67 -0
- package/src/components/Product/ProductEngines.astro +43 -0
- package/src/pages/components/product-engine.mdx +75 -31
- package/src/scripts/tooltips.ts +33 -28
- package/src/styles/main.css +4 -0
- package/src/utils/product/getEngineTooltipContent.ts +163 -0
- package/uno-config/index.ts +1 -1
- package/src/components/Product/ProductEngine.vue +0 -240
- package/src/components/Product/ProductEngines.vue +0 -116
package/.claude/settings.json
CHANGED
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
"Bash(git log:*)",
|
|
15
15
|
"Bash(git add:*)",
|
|
16
16
|
"Bash(git commit:*)"
|
|
17
|
+
"Bash(git push:*)"
|
|
17
18
|
],
|
|
18
19
|
"deny": [
|
|
19
20
|
"Bash(pnpm semantic-release:*)",
|
|
20
21
|
"Bash(pnpm publish:*)",
|
|
21
22
|
"Bash(npm publish:*)",
|
|
22
|
-
"Bash(git push:*)"
|
|
23
23
|
],
|
|
24
24
|
"ask": [
|
|
25
25
|
"Bash(rm:*)",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
## [1.9.1](https://github.com/polo-blue/sds/compare/v1.9.0...v1.9.1) (2025-10-30)
|
|
2
|
+
|
|
3
|
+
### ⚠ BREAKING CHANGES
|
|
4
|
+
|
|
5
|
+
* ProductEngine and ProductEngines are now Astro components
|
|
6
|
+
|
|
7
|
+
Replace Vue components with Astro for better performance and SEO:
|
|
8
|
+
- Convert ProductEngine.vue to ProductEngine.astro (SSR-friendly)
|
|
9
|
+
- Convert ProductEngines.vue to ProductEngines.astro
|
|
10
|
+
- Add getEngineTooltipContent utility for tooltip generation
|
|
11
|
+
- Create global tooltip delegation script for performance
|
|
12
|
+
- Add tippy.js CSS to main.css (tippy.css + tippy-theme.css)
|
|
13
|
+
|
|
14
|
+
Benefits:
|
|
15
|
+
- Engine codes now in HTML for SEO (no client-side hydration)
|
|
16
|
+
- 1 global script handles all tooltips via delegation pattern
|
|
17
|
+
- Tooltips created on-demand when hovered (not on mount)
|
|
18
|
+
- Works for both engine codes and PR codes
|
|
19
|
+
- Massive performance improvement on pages with many engines
|
|
20
|
+
|
|
21
|
+
Migration:
|
|
22
|
+
- Remove client:load directive from ProductEngines usage
|
|
23
|
+
- Import and call initTooltips() in your layout
|
|
24
|
+
- ProductEngine/ProductEngines now pure Astro (no Vue)
|
|
25
|
+
|
|
26
|
+
### Code Refactoring
|
|
27
|
+
|
|
28
|
+
* migrate ProductEngine from Vue to Astro with tooltip delegation ([b3589cc](https://github.com/polo-blue/sds/commit/b3589cc75da9cbf514bcffce2d06a8a36d67012e))
|
|
29
|
+
|
|
1
30
|
## [1.9.0](https://github.com/polo-blue/sds/compare/v1.8.1...v1.9.0) (2025-10-29)
|
|
2
31
|
|
|
3
32
|
### Features
|
package/index.ts
CHANGED
|
@@ -19,8 +19,7 @@ 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
|
-
|
|
23
|
-
export { default as ProductEngines } from './src/components/Product/ProductEngines.vue';
|
|
22
|
+
// ProductEngine and ProductEngines are now Astro components (see Astro Components section below)
|
|
24
23
|
export { default as ProductButton } from './src/components/Product/ProductButton.vue';
|
|
25
24
|
export { default as ProductColors } from './src/components/Product/ProductColors.vue';
|
|
26
25
|
export { default as ProductDetailName } from './src/components/Product/ProductDetailName.vue';
|
|
@@ -50,6 +49,8 @@ export { default as ButtonCopy } from './src/components/ButtonCopy.astro';
|
|
|
50
49
|
export { default as CallToAction } from './src/components/Layout/CallToAction.astro';
|
|
51
50
|
|
|
52
51
|
export { default as ProductImage } from './src/components/Product/ProductImage.astro';
|
|
52
|
+
export { default as ProductEngine } from './src/components/Product/ProductEngine.astro';
|
|
53
|
+
export { default as ProductEngines } from './src/components/Product/ProductEngines.astro';
|
|
53
54
|
|
|
54
55
|
export { default as CategoryDetails } from './src/components/Category/CategoryDetails.astro';
|
|
55
56
|
export { default as CategoryTile } from './src/components/Category/CategoryTile.astro';
|
|
@@ -58,6 +59,8 @@ export { default as CategoryTile } from './src/components/Category/CategoryTile.
|
|
|
58
59
|
// Utils: Product
|
|
59
60
|
export { default as getPriceFormatted } from './src/utils/product/getPriceFormatted';
|
|
60
61
|
export { default as getProductChecklist } from './src/utils/product/getProductChecklist';
|
|
62
|
+
export { getEngineTooltipContent } from './src/utils/product/getEngineTooltipContent';
|
|
63
|
+
export type { Engine, EngineTranslations } from './src/utils/product/getEngineTooltipContent';
|
|
61
64
|
|
|
62
65
|
// Utils: SEO
|
|
63
66
|
export { default as getShorterDescription } from './src/utils/seo/getShorterDescription';
|
|
@@ -70,3 +73,6 @@ export { default as formatLocaleNumber } from './src/utils/text/formatLocaleNumb
|
|
|
70
73
|
export { default as formatPad } from './src/utils/text/formatPad';
|
|
71
74
|
export { default as getNumberFormatted } from './src/utils/text/getNumberFormatted';
|
|
72
75
|
export { default as getTranslatedLink } from './src/utils/text/getTranslatedLink';
|
|
76
|
+
|
|
77
|
+
// Scripts
|
|
78
|
+
export { initTooltips } from './src/scripts/tooltips';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spoko-design-system",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "./index.ts",
|
|
6
6
|
"module": "./index.ts",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"spoko design system"
|
|
53
53
|
],
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@algolia/client-search": "^5.
|
|
55
|
+
"@algolia/client-search": "^5.42.0",
|
|
56
56
|
"@astrojs/mdx": "^4.3.9",
|
|
57
57
|
"@astrojs/node": "^9.5.0",
|
|
58
58
|
"@astrojs/sitemap": "^3.6.0",
|
|
@@ -145,7 +145,8 @@
|
|
|
145
145
|
},
|
|
146
146
|
"ignoredBuiltDependencies": [
|
|
147
147
|
"esbuild",
|
|
148
|
-
"sharp"
|
|
148
|
+
"sharp",
|
|
149
|
+
"vue-demi"
|
|
149
150
|
]
|
|
150
151
|
},
|
|
151
152
|
"engines": {
|
|
@@ -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 = [], isPdp = false, 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>
|
|
@@ -2,28 +2,46 @@
|
|
|
2
2
|
title: "ProductEngine"
|
|
3
3
|
layout: "../../layouts/MainLayout.astro"
|
|
4
4
|
---
|
|
5
|
-
import ProductEngine from '../../components/Product/ProductEngine.
|
|
6
|
-
import ProductEngines from '../../components/Product/ProductEngines.
|
|
5
|
+
import ProductEngine from '../../components/Product/ProductEngine.astro'
|
|
6
|
+
import ProductEngines from '../../components/Product/ProductEngines.astro'
|
|
7
7
|
|
|
8
8
|
# ProductEngine
|
|
9
9
|
|
|
10
10
|
Engine codes with detailed tooltips showing specifications like power, displacement, dates, and more.
|
|
11
11
|
|
|
12
|
+
**⚡ Performance-optimized**: Uses Tippy.js delegation pattern - one global script handles all tooltips efficiently.
|
|
13
|
+
|
|
14
|
+
**🔍 SEO-friendly**: Engine codes rendered as static HTML (no client-side hydration required).
|
|
15
|
+
|
|
12
16
|
## Single Engine Code
|
|
13
17
|
|
|
14
18
|
Display a single engine code with tooltip showing detailed engine information.
|
|
15
19
|
|
|
16
|
-
###
|
|
20
|
+
### Setup:
|
|
21
|
+
|
|
22
|
+
**1. Import the component:**
|
|
17
23
|
|
|
18
24
|
```js
|
|
19
|
-
import ProductEngine from 'spoko-design-system
|
|
25
|
+
import { ProductEngine } from 'spoko-design-system'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**2. Initialize tooltips in your layout (once):**
|
|
29
|
+
|
|
30
|
+
```astro
|
|
31
|
+
---
|
|
32
|
+
// In your layout file (e.g., Layout.astro)
|
|
33
|
+
---
|
|
34
|
+
<script>
|
|
35
|
+
import { initTooltips } from 'spoko-design-system';
|
|
36
|
+
initTooltips();
|
|
37
|
+
</script>
|
|
20
38
|
```
|
|
21
39
|
|
|
22
40
|
### Basic Usage (New API Structure):
|
|
23
41
|
|
|
24
42
|
<div class="component-preview">
|
|
25
43
|
<div class="bg-white p-6 w-full flex gap-2">
|
|
26
|
-
<ProductEngine
|
|
44
|
+
<ProductEngine engine={{
|
|
27
45
|
code: "CAYA",
|
|
28
46
|
name: "1.6 TDI",
|
|
29
47
|
serie: { value: "EA189", label: "Seria silnika" },
|
|
@@ -31,7 +49,7 @@ import ProductEngine from 'spoko-design-system/src/components/Product/ProductEng
|
|
|
31
49
|
displacement: { value: 1598, label: "Pojemność" },
|
|
32
50
|
euro: { value: 5, label: "Norma Euro" }
|
|
33
51
|
}} />
|
|
34
|
-
<ProductEngine
|
|
52
|
+
<ProductEngine engine={{
|
|
35
53
|
code: "CAYB",
|
|
36
54
|
name: "1.6 TDI",
|
|
37
55
|
serie: { value: "EA189", label: "Seria silnika" },
|
|
@@ -42,21 +60,25 @@ import ProductEngine from 'spoko-design-system/src/components/Product/ProductEng
|
|
|
42
60
|
</div>
|
|
43
61
|
</div>
|
|
44
62
|
|
|
45
|
-
```
|
|
63
|
+
```astro
|
|
64
|
+
---
|
|
65
|
+
import { ProductEngine } from 'spoko-design-system';
|
|
66
|
+
---
|
|
67
|
+
|
|
46
68
|
<!-- New API structure (with built-in translations) -->
|
|
47
|
-
<ProductEngine
|
|
69
|
+
<ProductEngine engine={{
|
|
48
70
|
code: 'CAYA',
|
|
49
71
|
name: '1.6 TDI',
|
|
50
72
|
serie: { value: 'EA189', label: 'Seria silnika' },
|
|
51
73
|
power: { kw: 55, ps: 75, ps_label: 'KM', label: 'Moc' },
|
|
52
74
|
displacement: { value: 1598, label: 'Pojemność' },
|
|
53
75
|
euro: { value: 5, label: 'Norma Euro' }
|
|
54
|
-
}
|
|
76
|
+
}} />
|
|
55
77
|
|
|
56
78
|
<!-- Old API structure (backward compatible) -->
|
|
57
79
|
<ProductEngine
|
|
58
|
-
|
|
59
|
-
|
|
80
|
+
engine={{ code: 'CAYA', name: '1.6 TDI', kw: 55, ps: 75, cc: 1598, serie: 3, euro: 5 }}
|
|
81
|
+
translations={{ power: 'Moc', cc: 'Pojemność', euro: 'Euro', horsepowerUnit: 'KM' }}
|
|
60
82
|
/>
|
|
61
83
|
```
|
|
62
84
|
|
|
@@ -68,7 +90,7 @@ Engine codes for special editions are automatically color-coded:
|
|
|
68
90
|
<div class="bg-white p-6 w-full flex flex-wrap gap-3">
|
|
69
91
|
<div class="flex flex-col gap-1">
|
|
70
92
|
<small class="text-xs text-gray-500">GTI Engine (Red)</small>
|
|
71
|
-
<ProductEngine
|
|
93
|
+
<ProductEngine engine={{
|
|
72
94
|
code: "CAVE",
|
|
73
95
|
name: "1.4 TSI",
|
|
74
96
|
power: { kw: 132, ps: 180, ps_label: "KM", label: "Moc" },
|
|
@@ -78,7 +100,7 @@ Engine codes for special editions are automatically color-coded:
|
|
|
78
100
|
</div>
|
|
79
101
|
<div class="flex flex-col gap-1">
|
|
80
102
|
<small class="text-xs text-gray-500">WRC Engine (Blue)</small>
|
|
81
|
-
<ProductEngine
|
|
103
|
+
<ProductEngine engine={{
|
|
82
104
|
code: "CDLJ",
|
|
83
105
|
name: "1.6 TDI",
|
|
84
106
|
power: { kw: 165, ps: 220, ps_label: "KM", label: "Moc" },
|
|
@@ -88,7 +110,7 @@ Engine codes for special editions are automatically color-coded:
|
|
|
88
110
|
</div>
|
|
89
111
|
<div class="flex flex-col gap-1">
|
|
90
112
|
<small class="text-xs text-gray-500">Special Blue</small>
|
|
91
|
-
<ProductEngine
|
|
113
|
+
<ProductEngine engine={{
|
|
92
114
|
code: "CPTA",
|
|
93
115
|
name: "1.0 TSI",
|
|
94
116
|
power: { kw: 70, ps: 95, ps_label: "KM", label: "Moc" },
|
|
@@ -99,41 +121,41 @@ Engine codes for special editions are automatically color-coded:
|
|
|
99
121
|
</div>
|
|
100
122
|
</div>
|
|
101
123
|
|
|
102
|
-
```
|
|
124
|
+
```astro
|
|
103
125
|
<!-- GTI Engine - Red -->
|
|
104
|
-
<ProductEngine
|
|
126
|
+
<ProductEngine engine={{
|
|
105
127
|
code: 'CAVE',
|
|
106
128
|
name: '1.4 TSI',
|
|
107
129
|
power: { kw: 132, ps: 180, ps_label: 'KM', label: 'Moc' },
|
|
108
130
|
displacement: { value: 1390, label: 'Pojemność' },
|
|
109
131
|
euro: { value: 5, label: 'Norma Euro' }
|
|
110
|
-
}
|
|
132
|
+
}} />
|
|
111
133
|
|
|
112
134
|
<!-- WRC Engine - Blue -->
|
|
113
|
-
<ProductEngine
|
|
135
|
+
<ProductEngine engine={{
|
|
114
136
|
code: 'CDLJ',
|
|
115
137
|
name: '1.6 TDI',
|
|
116
138
|
power: { kw: 165, ps: 220, ps_label: 'KM', label: 'Moc' },
|
|
117
139
|
displacement: { value: 1598, label: 'Pojemność' },
|
|
118
140
|
euro: { value: 6, label: 'Norma Euro' }
|
|
119
|
-
}
|
|
141
|
+
}} />
|
|
120
142
|
```
|
|
121
143
|
|
|
122
144
|
## Engine List
|
|
123
145
|
|
|
124
146
|
Display multiple engine codes from an array (from API response).
|
|
125
147
|
|
|
126
|
-
###
|
|
148
|
+
### Import:
|
|
127
149
|
|
|
128
150
|
```js
|
|
129
|
-
import ProductEngines from 'spoko-design-system
|
|
151
|
+
import { ProductEngines } from 'spoko-design-system'
|
|
130
152
|
```
|
|
131
153
|
|
|
132
154
|
### Usage:
|
|
133
155
|
|
|
134
156
|
<div class="component-preview">
|
|
135
157
|
<div class="bg-white p-6 w-full">
|
|
136
|
-
<ProductEngines
|
|
158
|
+
<ProductEngines engines={[
|
|
137
159
|
{
|
|
138
160
|
id: 13,
|
|
139
161
|
code: "CAYA",
|
|
@@ -165,8 +187,12 @@ import ProductEngines from 'spoko-design-system/src/components/Product/ProductEn
|
|
|
165
187
|
</div>
|
|
166
188
|
</div>
|
|
167
189
|
|
|
168
|
-
```
|
|
169
|
-
|
|
190
|
+
```astro
|
|
191
|
+
---
|
|
192
|
+
import { ProductEngines } from 'spoko-design-system';
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
<ProductEngines engines={[
|
|
170
196
|
{
|
|
171
197
|
id: 13,
|
|
172
198
|
code: 'CAYA',
|
|
@@ -185,17 +211,17 @@ import ProductEngines from 'spoko-design-system/src/components/Product/ProductEn
|
|
|
185
211
|
displacement: { value: 1598, label: 'Pojemność' },
|
|
186
212
|
euro: { value: 5, label: 'Norma Euro' }
|
|
187
213
|
}
|
|
188
|
-
]
|
|
214
|
+
]} />
|
|
189
215
|
```
|
|
190
216
|
|
|
191
217
|
### With API Data:
|
|
192
218
|
|
|
193
219
|
When using data from the API, pass the `part_engines` array directly. The component automatically extracts translations from the nested structure:
|
|
194
220
|
|
|
195
|
-
```
|
|
221
|
+
```astro
|
|
196
222
|
<ProductEngines
|
|
197
|
-
|
|
198
|
-
|
|
223
|
+
engines={product.part_engines}
|
|
224
|
+
isPdp={true}
|
|
199
225
|
/>
|
|
200
226
|
```
|
|
201
227
|
|
|
@@ -334,7 +360,9 @@ When using data from the API, pass the `part_engines` array directly. The compon
|
|
|
334
360
|
|
|
335
361
|
## Features
|
|
336
362
|
|
|
337
|
-
- ✅ **Rich Tooltips**: Shows detailed engine specifications on hover (Tippy.js)
|
|
363
|
+
- ✅ **Rich Tooltips**: Shows detailed engine specifications on hover (Tippy.js delegation)
|
|
364
|
+
- ✅ **Performance Optimized**: One global script handles all tooltips efficiently
|
|
365
|
+
- ✅ **SEO-Friendly**: Engine codes rendered as static HTML (no hydration)
|
|
338
366
|
- ✅ **API-Driven Translations**: Labels and units come directly from API
|
|
339
367
|
- ✅ **Multi-language**: Supports Polish (KM), German (PS), English (HP)
|
|
340
368
|
- ✅ **Two-Tone Design**: Dark blue header with light body for better hierarchy
|
|
@@ -342,7 +370,6 @@ When using data from the API, pass the `part_engines` array directly. The compon
|
|
|
342
370
|
- ✅ **Clean Layout**: Flexbox-based design with semantic CSS classes
|
|
343
371
|
- ✅ **Smart Data**: Only shows relevant fields (power, displacement, euro)
|
|
344
372
|
- ✅ **Auto-sorting**: Engines automatically sorted alphabetically by code
|
|
345
|
-
- ✅ **SEO-Friendly**: Static HTML enhanced with client-side tooltips
|
|
346
373
|
- ✅ **Backward Compatible**: Works with both new and old API structures
|
|
347
374
|
- ✅ **Touch Friendly**: Works on mobile devices
|
|
348
375
|
|
|
@@ -358,8 +385,10 @@ Special engine codes are automatically color-coded:
|
|
|
358
385
|
|
|
359
386
|
## Notes
|
|
360
387
|
|
|
388
|
+
- **Setup required**: Call `initTooltips()` once in your layout to enable tooltips
|
|
361
389
|
- Hover over any engine code to see full specifications in a detailed tooltip
|
|
362
|
-
- Tooltips use Tippy.js
|
|
390
|
+
- Tooltips use Tippy.js delegation pattern (one global handler for all tooltips)
|
|
391
|
+
- **Performance**: No client-side hydration needed - pure Astro components
|
|
363
392
|
- **New API structure**: Labels come from API (e.g., "Moc", "Pojemność", "KM")
|
|
364
393
|
- **No manual translations needed**: The API provides all labels and units
|
|
365
394
|
- The component automatically extracts series value from nested structure
|
|
@@ -368,3 +397,18 @@ Special engine codes are automatically color-coded:
|
|
|
368
397
|
- Commas between engine codes are automatically handled in lists
|
|
369
398
|
- **Backward compatible**: Still works with old flat API structure
|
|
370
399
|
- Tooltip has dark blue header (#001e50) with light gray body (#f3f4f6)
|
|
400
|
+
|
|
401
|
+
## Migration from Vue to Astro
|
|
402
|
+
|
|
403
|
+
If upgrading from the old Vue version:
|
|
404
|
+
|
|
405
|
+
1. **Remove `client:load`** directive from all ProductEngine/ProductEngines usage
|
|
406
|
+
2. **Add tooltip initialization** to your layout:
|
|
407
|
+
```astro
|
|
408
|
+
<script>
|
|
409
|
+
import { initTooltips } from 'spoko-design-system';
|
|
410
|
+
initTooltips();
|
|
411
|
+
</script>
|
|
412
|
+
```
|
|
413
|
+
3. **Update imports** to use Astro components (automatically handled by package exports)
|
|
414
|
+
4. **No other changes needed** - props and API structure remain the same
|
package/src/scripts/tooltips.ts
CHANGED
|
@@ -1,46 +1,51 @@
|
|
|
1
|
-
import tippy from 'tippy.js';
|
|
2
|
-
import 'tippy.js/dist/tippy.css';
|
|
3
|
-
import '../styles/tippy-theme.css';
|
|
4
|
-
|
|
5
1
|
/**
|
|
6
|
-
* Global
|
|
7
|
-
*
|
|
8
|
-
*
|
|
2
|
+
* Global tooltip delegation script for SDS
|
|
3
|
+
* Uses Tippy.js delegation pattern for performance
|
|
4
|
+
* Handles tooltips for:
|
|
5
|
+
* - Engine codes (.engine-code)
|
|
6
|
+
* - PR codes (.btn-prcode)
|
|
7
|
+
* - Any other elements with data-tippy-content
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
// Destroy existing tooltips to avoid duplicates
|
|
13
|
-
document.querySelectorAll('[data-tippy-content]').forEach((el: any) => {
|
|
14
|
-
if (el._tippy) {
|
|
15
|
-
el._tippy.destroy();
|
|
16
|
-
}
|
|
17
|
-
});
|
|
10
|
+
import { delegate } from 'tippy.js';
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Initialize tooltips with delegation pattern
|
|
14
|
+
* Call this once in your layout after page load
|
|
15
|
+
*/
|
|
16
|
+
export function initTooltips() {
|
|
17
|
+
// Delegate to body for all tooltip targets
|
|
18
|
+
delegate('body', {
|
|
19
|
+
target: '[data-tippy-content]', // Any element with data-tippy-content
|
|
20
|
+
allowHTML: true,
|
|
21
21
|
theme: 'sds',
|
|
22
22
|
placement: 'top',
|
|
23
23
|
arrow: true,
|
|
24
24
|
animation: 'shift-away',
|
|
25
25
|
duration: [200, 150],
|
|
26
26
|
maxWidth: 280,
|
|
27
|
-
|
|
27
|
+
// Only show tooltip if there's actual content
|
|
28
|
+
onShow(instance) {
|
|
29
|
+
const content = instance.props.content;
|
|
30
|
+
if (!content || content === '' || content === 'undefined') {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
28
34
|
});
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
//
|
|
32
|
-
if (typeof
|
|
33
|
-
|
|
34
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
37
|
+
// Auto-initialize on Astro page load (for View Transitions)
|
|
38
|
+
if (typeof document !== 'undefined') {
|
|
39
|
+
document.addEventListener('astro:page-load', () => {
|
|
35
40
|
initTooltips();
|
|
36
|
-
// Reinitialize after a short delay to catch Vue components
|
|
37
|
-
setTimeout(initTooltips, 100);
|
|
38
41
|
});
|
|
39
42
|
|
|
40
|
-
//
|
|
41
|
-
document.
|
|
43
|
+
// Fallback for non-Astro or initial load
|
|
44
|
+
if (document.readyState === 'loading') {
|
|
45
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
46
|
+
initTooltips();
|
|
47
|
+
});
|
|
48
|
+
} else {
|
|
42
49
|
initTooltips();
|
|
43
|
-
|
|
44
|
-
setTimeout(initTooltips, 100);
|
|
45
|
-
});
|
|
50
|
+
}
|
|
46
51
|
}
|
package/src/styles/main.css
CHANGED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates HTML content for engine tooltip
|
|
3
|
+
* Used by both SSR (Astro) and CSR (Tippy.js delegation)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface Engine {
|
|
7
|
+
id?: number;
|
|
8
|
+
code: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
info?: string | null;
|
|
11
|
+
serie?: {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
};
|
|
15
|
+
type?: {
|
|
16
|
+
value: string;
|
|
17
|
+
translated: string;
|
|
18
|
+
label: string;
|
|
19
|
+
};
|
|
20
|
+
power?: {
|
|
21
|
+
kw: number;
|
|
22
|
+
ps: number;
|
|
23
|
+
ps_label: string;
|
|
24
|
+
label: string;
|
|
25
|
+
};
|
|
26
|
+
date?: {
|
|
27
|
+
value: string;
|
|
28
|
+
label: string;
|
|
29
|
+
};
|
|
30
|
+
displacement?: {
|
|
31
|
+
value: number;
|
|
32
|
+
label: string;
|
|
33
|
+
};
|
|
34
|
+
compression_ratio?: {
|
|
35
|
+
value: string | null;
|
|
36
|
+
label: string;
|
|
37
|
+
};
|
|
38
|
+
valves?: {
|
|
39
|
+
value: number | null;
|
|
40
|
+
label: string;
|
|
41
|
+
};
|
|
42
|
+
euro?: {
|
|
43
|
+
value: number;
|
|
44
|
+
label: string;
|
|
45
|
+
};
|
|
46
|
+
pivot?: any;
|
|
47
|
+
|
|
48
|
+
// Backward compatibility - old flat structure
|
|
49
|
+
kw?: number;
|
|
50
|
+
ps?: number;
|
|
51
|
+
cc?: number;
|
|
52
|
+
c_ratio?: string | null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface EngineTranslations {
|
|
56
|
+
power?: string;
|
|
57
|
+
cc?: string;
|
|
58
|
+
compressionRatio?: string;
|
|
59
|
+
valves?: string;
|
|
60
|
+
euro?: string;
|
|
61
|
+
horsepowerUnit?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const defaultTranslations: EngineTranslations = {
|
|
65
|
+
power: 'Power',
|
|
66
|
+
cc: 'CC',
|
|
67
|
+
compressionRatio: 'C. Ratio',
|
|
68
|
+
valves: 'Valves',
|
|
69
|
+
euro: 'Euro',
|
|
70
|
+
horsepowerUnit: 'PS',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Helper to get series value (supports both old and new API)
|
|
74
|
+
function getSerieValue(engine: Engine): string {
|
|
75
|
+
if (engine.serie && typeof engine.serie === 'object') {
|
|
76
|
+
return engine.serie.value;
|
|
77
|
+
}
|
|
78
|
+
// Backward compatibility - old API
|
|
79
|
+
const serie = engine.serie as any;
|
|
80
|
+
if (!serie) return '';
|
|
81
|
+
return serie === 3 ? 'EA288' : serie === 2 ? 'EA189' : `Serie ${serie}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function getEngineTooltipContent(
|
|
85
|
+
engine: Engine,
|
|
86
|
+
translations: EngineTranslations = {}
|
|
87
|
+
): string {
|
|
88
|
+
const t = { ...defaultTranslations, ...translations };
|
|
89
|
+
|
|
90
|
+
// Header section
|
|
91
|
+
let headerContent = `<strong>${engine.name || engine.code}</strong>`;
|
|
92
|
+
if (engine.info) {
|
|
93
|
+
headerContent += ` <span class="info">${engine.info}</span>`;
|
|
94
|
+
}
|
|
95
|
+
const serieValue = getSerieValue(engine);
|
|
96
|
+
if (serieValue) {
|
|
97
|
+
headerContent += `<div class="series-badge">${serieValue}</div>`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const header = `<div class="tooltip-header">${headerContent}</div>`;
|
|
101
|
+
|
|
102
|
+
// Specs rows
|
|
103
|
+
const rows: string[] = [];
|
|
104
|
+
|
|
105
|
+
// Power (supports both new and old API structure)
|
|
106
|
+
const power = engine.power;
|
|
107
|
+
const oldKw = engine.kw;
|
|
108
|
+
const oldPs = engine.ps;
|
|
109
|
+
|
|
110
|
+
if (power || oldKw || oldPs) {
|
|
111
|
+
const powerValues: string[] = [];
|
|
112
|
+
const kw = power?.kw || oldKw;
|
|
113
|
+
const ps = power?.ps || oldPs;
|
|
114
|
+
const psLabel = power?.ps_label || t.horsepowerUnit;
|
|
115
|
+
const powerLabel = power?.label || t.power;
|
|
116
|
+
|
|
117
|
+
if (kw) powerValues.push(`${kw} kW`);
|
|
118
|
+
if (ps) powerValues.push(`${ps} ${psLabel}`);
|
|
119
|
+
|
|
120
|
+
if (powerValues.length) {
|
|
121
|
+
rows.push(
|
|
122
|
+
`<div class="tooltip-row"><span class="tooltip-label">${powerLabel}:</span><span class="tooltip-value">${powerValues.join(' / ')}</span></div>`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Displacement (CC)
|
|
128
|
+
const displacement = engine.displacement;
|
|
129
|
+
const oldCc = engine.cc;
|
|
130
|
+
|
|
131
|
+
if (displacement || oldCc) {
|
|
132
|
+
const ccValue = displacement?.value || oldCc;
|
|
133
|
+
const ccLabel = displacement?.label || t.cc;
|
|
134
|
+
|
|
135
|
+
if (ccValue) {
|
|
136
|
+
rows.push(
|
|
137
|
+
`<div class="tooltip-row"><span class="tooltip-label">${ccLabel}:</span><span class="tooltip-value">${ccValue} cm³</span></div>`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Euro standard
|
|
143
|
+
const euro = engine.euro;
|
|
144
|
+
|
|
145
|
+
if (euro && typeof euro === 'object') {
|
|
146
|
+
if (euro.value) {
|
|
147
|
+
rows.push(
|
|
148
|
+
`<div class="tooltip-row"><span class="tooltip-label">${euro.label}:</span><span class="tooltip-value">Euro ${euro.value}</span></div>`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
} else if (euro) {
|
|
152
|
+
// Backward compatibility - old API
|
|
153
|
+
rows.push(
|
|
154
|
+
`<div class="tooltip-row"><span class="tooltip-label">${t.euro}:</span><span class="tooltip-value">Euro ${euro}</span></div>`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const specsContent = rows.length
|
|
159
|
+
? `<div class="tooltip-specs">${rows.join('')}</div>`
|
|
160
|
+
: '';
|
|
161
|
+
|
|
162
|
+
return header + specsContent;
|
|
163
|
+
}
|
package/uno-config/index.ts
CHANGED
|
@@ -207,7 +207,7 @@ export function createSdsConfig(customConfig: CustomConfig = {}) {
|
|
|
207
207
|
presetAttributify(),
|
|
208
208
|
presetIcons({
|
|
209
209
|
scale: 1.2,
|
|
210
|
-
warn:
|
|
210
|
+
warn: false, // Disabled to prevent false positives from JS code scanning
|
|
211
211
|
prefix: 'i-',
|
|
212
212
|
extraProperties: {
|
|
213
213
|
'display': 'inline-block',
|
|
@@ -1,240 +0,0 @@
|
|
|
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>
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { computed } from 'vue';
|
|
3
|
-
import type { PropType } from 'vue';
|
|
4
|
-
import ProductEngine from './ProductEngine.vue';
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
ProductEngines wrapper component
|
|
8
|
-
Displays a list of engine codes with tooltips
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
interface Engine {
|
|
12
|
-
id?: number;
|
|
13
|
-
code: string;
|
|
14
|
-
name?: string;
|
|
15
|
-
info?: string | null;
|
|
16
|
-
serie?: {
|
|
17
|
-
value: string;
|
|
18
|
-
label: string;
|
|
19
|
-
} | number;
|
|
20
|
-
type?: {
|
|
21
|
-
value: string;
|
|
22
|
-
translated: string;
|
|
23
|
-
label: string;
|
|
24
|
-
};
|
|
25
|
-
power?: {
|
|
26
|
-
kw: number;
|
|
27
|
-
ps: number;
|
|
28
|
-
ps_label: string;
|
|
29
|
-
label: string;
|
|
30
|
-
};
|
|
31
|
-
date?: {
|
|
32
|
-
value: string;
|
|
33
|
-
label: string;
|
|
34
|
-
};
|
|
35
|
-
displacement?: {
|
|
36
|
-
value: number;
|
|
37
|
-
label: string;
|
|
38
|
-
};
|
|
39
|
-
compression_ratio?: {
|
|
40
|
-
value: string | null;
|
|
41
|
-
label: string;
|
|
42
|
-
};
|
|
43
|
-
valves?: {
|
|
44
|
-
value: number | null;
|
|
45
|
-
label: string;
|
|
46
|
-
};
|
|
47
|
-
euro?: {
|
|
48
|
-
value: number;
|
|
49
|
-
label: string;
|
|
50
|
-
} | number;
|
|
51
|
-
pivot?: any;
|
|
52
|
-
|
|
53
|
-
// Backward compatibility
|
|
54
|
-
kw?: number;
|
|
55
|
-
ps?: number;
|
|
56
|
-
cc?: number;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const props = defineProps({
|
|
60
|
-
engines: {
|
|
61
|
-
type: Array as PropType<Engine[]>,
|
|
62
|
-
default: () => [],
|
|
63
|
-
required: true,
|
|
64
|
-
},
|
|
65
|
-
isPdp: {
|
|
66
|
-
type: Boolean,
|
|
67
|
-
default: false,
|
|
68
|
-
required: false,
|
|
69
|
-
},
|
|
70
|
-
translations: {
|
|
71
|
-
type: Object as PropType<{
|
|
72
|
-
power?: string;
|
|
73
|
-
cc?: string;
|
|
74
|
-
compressionRatio?: string;
|
|
75
|
-
valves?: string;
|
|
76
|
-
euro?: string;
|
|
77
|
-
horsepowerUnit?: string;
|
|
78
|
-
}>,
|
|
79
|
-
default: () => ({
|
|
80
|
-
power: 'Power',
|
|
81
|
-
cc: 'CC',
|
|
82
|
-
compressionRatio: 'C. Ratio',
|
|
83
|
-
valves: 'Valves',
|
|
84
|
-
euro: 'Euro',
|
|
85
|
-
horsepowerUnit: 'PS',
|
|
86
|
-
}),
|
|
87
|
-
required: false,
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Sort engines by code
|
|
92
|
-
const sortedEngines = computed(() => {
|
|
93
|
-
if (!props.engines || !props.engines.length) return [];
|
|
94
|
-
return [...props.engines].sort((a, b) => {
|
|
95
|
-
return (a.code || '').localeCompare(b.code || '');
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
</script>
|
|
99
|
-
|
|
100
|
-
<template>
|
|
101
|
-
<div v-if="sortedEngines.length" class="engines-list inline-flex flex-wrap items-center gap-x-0.5">
|
|
102
|
-
<ProductEngine
|
|
103
|
-
v-for="(engine, index) in sortedEngines"
|
|
104
|
-
:key="engine.id || index"
|
|
105
|
-
:engine="engine"
|
|
106
|
-
:show-comma="index !== sortedEngines.length - 1"
|
|
107
|
-
:translations="translations"
|
|
108
|
-
/>
|
|
109
|
-
</div>
|
|
110
|
-
</template>
|
|
111
|
-
|
|
112
|
-
<style scoped>
|
|
113
|
-
.engines-list {
|
|
114
|
-
@apply leading-none;
|
|
115
|
-
}
|
|
116
|
-
</style>
|