spoko-design-system 0.2.40 → 0.2.42
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/.env.example +2 -0
- package/.github/dependabot.yml +11 -11
- package/.github/todo.yml +3 -3
- package/.github/workflows/deploy.yml +39 -39
- package/.stackblitzrc +5 -5
- package/.vscode/extensions.json +5 -5
- package/.vscode/launch.json +11 -11
- package/.vscode/settings.json +5 -5
- package/LICENSE +21 -21
- package/README.md +113 -113
- package/astro-i18next.config.mjs +17 -17
- package/astro-i18next.config.ts +10 -10
- package/astro.config.mjs +147 -147
- package/dev-dist/sw.js +91 -91
- package/dev-dist/workbox-c676b6d3.js +3391 -3391
- package/index.ts +58 -33
- package/package.json +1 -1
- package/public/fonts/lg.svg +53 -53
- package/public/fonts/vwhead-bold-demo.html +549 -549
- package/public/fonts/vwhead-regular-demo.html +549 -549
- package/public/fonts/vwtext-bold-demo.html +549 -549
- package/public/fonts/vwtext-regular-demo.html +549 -549
- package/public/github.svg +3 -3
- package/public/grid_dot.svg +4 -4
- package/public/linkedin.svg +44 -44
- package/public/locales/en/translation.json +8 -8
- package/public/locales/pl/translation.json +8 -8
- package/public/make-scrollable-code-focusable.js +3 -3
- package/public/pagefind.yml +3 -3
- package/public/polo.blue.svg +29 -29
- package/public/spoko.space.svg +71 -71
- package/public/twitter.svg +46 -46
- package/renovate.json +6 -6
- package/sandbox.config.json +11 -11
- package/src/MyComponent.astro +8 -8
- package/src/components/Badge.vue +19 -19
- package/src/components/Badges.vue +21 -21
- package/src/components/Breadcrumbs.vue +110 -110
- package/src/components/Button.vue +55 -55
- package/src/components/ButtonCopy.vue +47 -47
- package/src/components/Card.astro +27 -27
- package/src/components/Carousel.astro +26 -26
- package/src/components/Category/CategoriesCarousel.astro +101 -0
- package/src/components/Category/CategoriesSidebar.astro +187 -0
- package/src/components/Category/CategoryDetails.astro +82 -0
- package/src/components/Category/CategoryLink.vue +23 -0
- package/src/components/Category/CategorySection.astro +70 -0
- package/src/components/Category/CategorySidebarToggler.vue +10 -0
- package/src/components/Category/SubCategoryLink.vue +29 -0
- package/src/components/CategoryLink.astro +18 -18
- package/src/components/Copyright.astro +12 -12
- package/src/components/Date.astro +7 -7
- package/src/components/Faq.astro +33 -33
- package/src/components/FaqItem.astro +96 -96
- package/src/components/FeaturesList.vue +41 -41
- package/src/components/FuckRussia.vue +80 -80
- package/src/components/HandDrive.astro +29 -29
- package/src/components/Header/Header.astro +214 -214
- package/src/components/Header/SkipToContent.astro +1 -1
- package/src/components/Headline.vue +48 -48
- package/src/components/Image.astro +30 -30
- package/src/components/Jumbatron.vue +40 -40
- package/src/components/LeftSidebar.astro +53 -53
- package/src/components/MainColors.vue +23 -23
- package/src/components/MainInput.vue +15 -15
- package/src/components/Modal.astro +27 -27
- package/src/components/PageContent.astro +5 -5
- package/src/components/PartNumber.vue +27 -27
- package/src/components/PostHeader.astro +103 -103
- package/src/components/PrCode.vue +156 -156
- package/src/components/Product/ProductButton.vue +18 -0
- package/src/components/Product/ProductCodes.vue +167 -0
- package/src/components/Product/ProductEngineType.vue +42 -42
- package/src/components/Product/ProductImage.astro +41 -41
- package/src/components/Product/ProductLinkInfo.astro +37 -37
- package/src/components/Product/ProductNumber.astro +104 -104
- package/src/components/ProductCarousel.astro +38 -38
- package/src/components/ProductCodes.vue +39 -39
- package/src/components/ProductDetailName.vue +52 -52
- package/src/components/ProductDetailsList.vue +65 -65
- package/src/components/ProductNumber copy.astro +116 -116
- package/src/components/ProductNumber.astro +104 -104
- package/src/components/ProductTile.astro +48 -48
- package/src/components/Quote.vue +23 -23
- package/src/components/ReloadPrompt.astro +50 -50
- package/src/components/SlimBanner.vue +72 -72
- package/src/components/Table.vue +32 -32
- package/src/components/TableOfContents.astro +15 -15
- package/src/components/Translations.vue +23 -23
- package/src/components/flags/FlagPL.vue +3 -3
- package/src/components/flags/FlagUA.vue +2 -2
- package/src/components/layout/Container.astro +7 -7
- package/src/components/layout/Header.astro +80 -80
- package/src/config.ts +56 -56
- package/src/design.config.ts +81 -81
- package/src/env.d.ts +1 -1
- package/src/layouts/Layout.astro +60 -60
- package/src/layouts/MainLayout.astro +81 -81
- package/src/layouts/partials/FooterCommon.astro +4 -4
- package/src/layouts/partials/HeadCommon.astro +44 -44
- package/src/layouts/partials/HeadSEO.astro +41 -41
- package/src/pages/components/badges.mdx +57 -57
- package/src/pages/components/breadcrumbs.mdx +139 -139
- package/src/pages/components/buttons.mdx +236 -236
- package/src/pages/components/card.mdx +294 -294
- package/src/pages/components/carousel.mdx +62 -62
- package/src/pages/components/copyright.mdx +42 -42
- package/src/pages/components/details-list.mdx +115 -115
- package/src/pages/components/features-list.mdx +37 -37
- package/src/pages/components/flags.mdx +49 -49
- package/src/pages/components/fuck-russia.mdx +39 -39
- package/src/pages/components/hand-drive.mdx +38 -38
- package/src/pages/components/headline.mdx +152 -152
- package/src/pages/components/icons.astro +48 -48
- package/src/pages/components/image.mdx +513 -513
- package/src/pages/components/input.mdx +45 -45
- package/src/pages/components/jumbatron.mdx +95 -95
- package/src/pages/components/modal.mdx +64 -64
- package/src/pages/components/post-header.mdx +60 -60
- package/src/pages/components/pr-code.mdx +65 -65
- package/src/pages/components/product-number.mdx +66 -66
- package/src/pages/components/product-tile.mdx +51 -51
- package/src/pages/components/quote.mdx +33 -33
- package/src/pages/components/slimbanner.mdx +35 -35
- package/src/pages/components/table.mdx +108 -108
- package/src/pages/core/colors.mdx +10 -10
- package/src/pages/core/grid.mdx +89 -89
- package/src/pages/core/introduction.mdx +77 -77
- package/src/pages/core/shadows.astro +20 -20
- package/src/pages/core/typography.astro +47 -47
- package/src/pages/index.astro +126 -126
- package/src/pages/patterns/introduction.mdx +60 -60
- package/src/pwa.ts +12 -12
- package/src/styles/_variables.scss +70 -70
- package/src/styles/base/base.css +184 -184
- package/src/styles/base/grid.css +92 -92
- package/src/styles/base/typography.css +70 -70
- package/src/styles/content.css +73 -73
- package/src/styles/main.css +7 -7
- package/src/types/Product.ts +31 -31
- package/src/types/astro.d.ts +4 -4
- package/src/types/index.ts +237 -0
- package/src/utils/api/getCategories.ts +3 -0
- package/src/utils/category/getMainCategoryList.ts +31 -0
- package/src/utils/category/getSortedCategories.ts +11 -0
- package/src/utils/getData.ts +52 -0
- package/src/utils/product/getPriceFormatted.ts +14 -0
- package/src/utils/product/getProductChecklist.ts +15 -0
- package/src/utils/seo/getShorterDescription.ts +12 -0
- package/src/utils/text/formatDate.ts +6 -0
- package/src/utils/text/formatLocaleNumber.ts +7 -0
- package/src/utils/text/formatPad.ts +12 -0
- package/src/utils/text/getNumberFormatted.ts +33 -0
- package/src/utils/text/getTranslatedLink.ts +5 -0
- package/src/utils/text.ts +42 -42
- package/tailwind.config.cjs +8 -8
- package/tsconfig.json +28 -11
- package/uno.config.ts +256 -256
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { t } from "i18next";
|
|
3
|
-
import ButtonCopy from "./ButtonCopy.vue";
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
copyDisabled,
|
|
7
|
-
productNumber,
|
|
8
|
-
isPdp,
|
|
9
|
-
small,
|
|
10
|
-
big,
|
|
11
|
-
class: className,
|
|
12
|
-
} = Astro.props;
|
|
13
|
-
|
|
14
|
-
const butonTexts = {
|
|
15
|
-
copy: t("copy"),
|
|
16
|
-
copied: t("copied"),
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const isLetter = (string: string) => {
|
|
20
|
-
return string.toLowerCase() !== string.toUpperCase();
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const niceName = (string: string, separator = "\u00A0") => {
|
|
24
|
-
let word = string; //e.g. to 6Q0947106EY20 .split('+')
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
isLetter(string[0]) === true &&
|
|
28
|
-
isLetter(string[1]) === false &&
|
|
29
|
-
string.length === 9
|
|
30
|
-
) {
|
|
31
|
-
// mainly liquids
|
|
32
|
-
word = string
|
|
33
|
-
.replace(
|
|
34
|
-
/^(\w{1})(\w{3})(\w{3})(.*)$/,
|
|
35
|
-
`$1${separator}$2${separator}$3${separator}$4`,
|
|
36
|
-
)
|
|
37
|
-
.replace(/(^\s+|\s+$)/, "");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// wheels / emblems e.g. 6R0601025AK8Z8 --> 6R0 601 025 AB 8Z8 || 6C0601025DFZZ --> 6C0 601 025 D FZZ || 6R0853433ADA1 -> 6R0 853 433 A DA1
|
|
41
|
-
else if (string.length >= 13) {
|
|
42
|
-
// console.log('part number: wheels / emblems', word)
|
|
43
|
-
word = string
|
|
44
|
-
.replace(
|
|
45
|
-
/^(\w{3})(\w{3})(\w{3})(.*)(\w{3})$/,
|
|
46
|
-
`$1${separator}$2${separator}$3${separator}$4${separator}$5`,
|
|
47
|
-
)
|
|
48
|
-
.replace(/(^\s+|\s+$)/, "");
|
|
49
|
-
} else if (string.length > 12) {
|
|
50
|
-
// accessories / mats
|
|
51
|
-
word = string
|
|
52
|
-
.replace(
|
|
53
|
-
/^(\w{3})(\w{3})(\w{3})(\w{1})(.*)$/,
|
|
54
|
-
`$1${separator}$2${separator}$3${separator}$4${separator}$5`,
|
|
55
|
-
)
|
|
56
|
-
.replace(/(^\s+|\s+$)/, "");
|
|
57
|
-
} else {
|
|
58
|
-
// other parts
|
|
59
|
-
word = string
|
|
60
|
-
.replace(/(\w{3})/g, `$1${separator}`)
|
|
61
|
-
.replace(/(^\s+|\s+$)/, "");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return word.replace(" ", separator);
|
|
65
|
-
};
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
productNumber !== null && (
|
|
70
|
-
<div
|
|
71
|
-
class={`product-number ${
|
|
72
|
-
big ? "text-lg" : `text-sm ${className ? className : "mt-2 sm:mt-0"}`
|
|
73
|
-
}`}
|
|
74
|
-
>
|
|
75
|
-
<div
|
|
76
|
-
class={`inline-flex leading-none relative ${small ? "w-full" : ""}`}
|
|
77
|
-
itemprop="identifier"
|
|
78
|
-
>
|
|
79
|
-
{isPdp ? (
|
|
80
|
-
<h2 id={productNumber} class="product-code">
|
|
81
|
-
{productNumber}
|
|
82
|
-
</h2>
|
|
83
|
-
) : (
|
|
84
|
-
<div id={productNumber} class="product-code">
|
|
85
|
-
{productNumber}
|
|
86
|
-
</div>
|
|
87
|
-
)}
|
|
88
|
-
|
|
89
|
-
{copyDisabled && (
|
|
90
|
-
<ButtonCopy
|
|
91
|
-
productNumber={String(productNumber)}
|
|
92
|
-
texts={butonTexts}
|
|
93
|
-
tooltipClasses=""
|
|
94
|
-
client:only
|
|
95
|
-
/>
|
|
96
|
-
)}
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
<div
|
|
100
|
-
class={`code-formatted ${small ? "tracking-wide" : "tracking-tight"}`}
|
|
101
|
-
>
|
|
102
|
-
<div class="relative inset-0" data-pagefind-ignore>
|
|
103
|
-
{niceName(productNumber, ".")}
|
|
104
|
-
</div>
|
|
105
|
-
<div class="absolute inset-0" data-pagefind-ignore>
|
|
106
|
-
{niceName(productNumber, "-")}
|
|
107
|
-
</div>
|
|
108
|
-
{isPdp ? (
|
|
109
|
-
<h3 class="number-secondary">{niceName(productNumber)}</h3>
|
|
110
|
-
) : (
|
|
111
|
-
<div class="number-secondary">{niceName(productNumber)}</div>
|
|
112
|
-
)}
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
)
|
|
116
|
-
}
|
|
1
|
+
---
|
|
2
|
+
import { t } from "i18next";
|
|
3
|
+
import ButtonCopy from "./ButtonCopy.vue";
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
copyDisabled,
|
|
7
|
+
productNumber,
|
|
8
|
+
isPdp,
|
|
9
|
+
small,
|
|
10
|
+
big,
|
|
11
|
+
class: className,
|
|
12
|
+
} = Astro.props;
|
|
13
|
+
|
|
14
|
+
const butonTexts = {
|
|
15
|
+
copy: t("copy"),
|
|
16
|
+
copied: t("copied"),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const isLetter = (string: string) => {
|
|
20
|
+
return string.toLowerCase() !== string.toUpperCase();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const niceName = (string: string, separator = "\u00A0") => {
|
|
24
|
+
let word = string; //e.g. to 6Q0947106EY20 .split('+')
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
isLetter(string[0]) === true &&
|
|
28
|
+
isLetter(string[1]) === false &&
|
|
29
|
+
string.length === 9
|
|
30
|
+
) {
|
|
31
|
+
// mainly liquids
|
|
32
|
+
word = string
|
|
33
|
+
.replace(
|
|
34
|
+
/^(\w{1})(\w{3})(\w{3})(.*)$/,
|
|
35
|
+
`$1${separator}$2${separator}$3${separator}$4`,
|
|
36
|
+
)
|
|
37
|
+
.replace(/(^\s+|\s+$)/, "");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// wheels / emblems e.g. 6R0601025AK8Z8 --> 6R0 601 025 AB 8Z8 || 6C0601025DFZZ --> 6C0 601 025 D FZZ || 6R0853433ADA1 -> 6R0 853 433 A DA1
|
|
41
|
+
else if (string.length >= 13) {
|
|
42
|
+
// console.log('part number: wheels / emblems', word)
|
|
43
|
+
word = string
|
|
44
|
+
.replace(
|
|
45
|
+
/^(\w{3})(\w{3})(\w{3})(.*)(\w{3})$/,
|
|
46
|
+
`$1${separator}$2${separator}$3${separator}$4${separator}$5`,
|
|
47
|
+
)
|
|
48
|
+
.replace(/(^\s+|\s+$)/, "");
|
|
49
|
+
} else if (string.length > 12) {
|
|
50
|
+
// accessories / mats
|
|
51
|
+
word = string
|
|
52
|
+
.replace(
|
|
53
|
+
/^(\w{3})(\w{3})(\w{3})(\w{1})(.*)$/,
|
|
54
|
+
`$1${separator}$2${separator}$3${separator}$4${separator}$5`,
|
|
55
|
+
)
|
|
56
|
+
.replace(/(^\s+|\s+$)/, "");
|
|
57
|
+
} else {
|
|
58
|
+
// other parts
|
|
59
|
+
word = string
|
|
60
|
+
.replace(/(\w{3})/g, `$1${separator}`)
|
|
61
|
+
.replace(/(^\s+|\s+$)/, "");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return word.replace(" ", separator);
|
|
65
|
+
};
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
{
|
|
69
|
+
productNumber !== null && (
|
|
70
|
+
<div
|
|
71
|
+
class={`product-number ${
|
|
72
|
+
big ? "text-lg" : `text-sm ${className ? className : "mt-2 sm:mt-0"}`
|
|
73
|
+
}`}
|
|
74
|
+
>
|
|
75
|
+
<div
|
|
76
|
+
class={`inline-flex leading-none relative ${small ? "w-full" : ""}`}
|
|
77
|
+
itemprop="identifier"
|
|
78
|
+
>
|
|
79
|
+
{isPdp ? (
|
|
80
|
+
<h2 id={productNumber} class="product-code">
|
|
81
|
+
{productNumber}
|
|
82
|
+
</h2>
|
|
83
|
+
) : (
|
|
84
|
+
<div id={productNumber} class="product-code">
|
|
85
|
+
{productNumber}
|
|
86
|
+
</div>
|
|
87
|
+
)}
|
|
88
|
+
|
|
89
|
+
{copyDisabled && (
|
|
90
|
+
<ButtonCopy
|
|
91
|
+
productNumber={String(productNumber)}
|
|
92
|
+
texts={butonTexts}
|
|
93
|
+
tooltipClasses=""
|
|
94
|
+
client:only
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div
|
|
100
|
+
class={`code-formatted ${small ? "tracking-wide" : "tracking-tight"}`}
|
|
101
|
+
>
|
|
102
|
+
<div class="relative inset-0" data-pagefind-ignore>
|
|
103
|
+
{niceName(productNumber, ".")}
|
|
104
|
+
</div>
|
|
105
|
+
<div class="absolute inset-0" data-pagefind-ignore>
|
|
106
|
+
{niceName(productNumber, "-")}
|
|
107
|
+
</div>
|
|
108
|
+
{isPdp ? (
|
|
109
|
+
<h3 class="number-secondary">{niceName(productNumber)}</h3>
|
|
110
|
+
) : (
|
|
111
|
+
<div class="number-secondary">{niceName(productNumber)}</div>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { t } from "i18next";
|
|
3
|
-
import ButtonCopy from "./ButtonCopy.vue";
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
productNumber: string | null;
|
|
7
|
-
copyDisabled?: boolean;
|
|
8
|
-
isPdp?: boolean;
|
|
9
|
-
small?: boolean;
|
|
10
|
-
big?: boolean;
|
|
11
|
-
class?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const {
|
|
15
|
-
productNumber,
|
|
16
|
-
copyDisabled,
|
|
17
|
-
isPdp,
|
|
18
|
-
small,
|
|
19
|
-
big,
|
|
20
|
-
class: className
|
|
21
|
-
} = Astro.props;
|
|
22
|
-
|
|
23
|
-
const buttonTexts = {
|
|
24
|
-
copy: t('copy'),
|
|
25
|
-
copied: t('copied')
|
|
26
|
-
} as const;
|
|
27
|
-
|
|
28
|
-
// Regex patterns:
|
|
29
|
-
const LIQUIDS_PATTERN = /^(\w{1})(\w{3})(\w{3})(.*)$/;
|
|
30
|
-
const WHEELS_EMBLEMS_PATTERN = /^(\w{3})(\w{3})(\w{3})(.*)(\w{3})$/;
|
|
31
|
-
const ACCESSORIES_MATS_PATTERN = /^(\w{3})(\w{3})(\w{3})(\w{1})(.*)$/;
|
|
32
|
-
const OTHER_PARTS_PATTERN = /(\w{3})/g;
|
|
33
|
-
|
|
34
|
-
const isLetter = (char: string): boolean =>
|
|
35
|
-
char.toLowerCase() !== char.toUpperCase();
|
|
36
|
-
|
|
37
|
-
const formatProductNumber = (number: string, separator: string): string => {
|
|
38
|
-
if (!number) return '';
|
|
39
|
-
|
|
40
|
-
let formatted = number;
|
|
41
|
-
|
|
42
|
-
if (isLetter(number[0]) && !isLetter(number[1]) && number.length === 9) {
|
|
43
|
-
formatted = formatted.replace(LIQUIDS_PATTERN, `$1${separator}$2${separator}$3${separator}$4`);
|
|
44
|
-
} else if (number.length >= 13) {
|
|
45
|
-
formatted = formatted.replace(WHEELS_EMBLEMS_PATTERN, `$1${separator}$2${separator}$3${separator}$4${separator}$5`);
|
|
46
|
-
} else if (number.length > 12) {
|
|
47
|
-
formatted = formatted.replace(ACCESSORIES_MATS_PATTERN, `$1${separator}$2${separator}$3${separator}$4${separator}$5`);
|
|
48
|
-
} else {
|
|
49
|
-
formatted = formatted.replace(OTHER_PARTS_PATTERN, `$1${separator}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return formatted
|
|
53
|
-
.replace(' ', separator)
|
|
54
|
-
.replace(/[^a-zA-Z0-9]$/, ''); // Remove the end trailing separator
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// Memoization of formatted numbers
|
|
58
|
-
const formattedNumbers = productNumber ? {
|
|
59
|
-
standard: formatProductNumber(productNumber, '\u00A0'),
|
|
60
|
-
dot: formatProductNumber(productNumber, '.'),
|
|
61
|
-
dash: formatProductNumber(productNumber, '-')
|
|
62
|
-
} : null;
|
|
63
|
-
|
|
64
|
-
const classNames = [
|
|
65
|
-
'product-number',
|
|
66
|
-
big ? 'text-4.5' : 'number-big',
|
|
67
|
-
className
|
|
68
|
-
].filter(Boolean).join(' ');
|
|
69
|
-
|
|
70
|
-
const trackingClass = small ? 'tracking-wide' : 'tracking-tight';
|
|
71
|
-
|
|
72
|
-
const ProductWrapper = isPdp ? 'h2' : 'div';
|
|
73
|
-
const FormattedWrapper = isPdp ? 'h3' : 'div';
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
{productNumber && formattedNumbers && (
|
|
77
|
-
<div class={classNames}>
|
|
78
|
-
<div class={`p-number ${small ? "w-full" : ""}`} itemprop="identifier">
|
|
79
|
-
<ProductWrapper id={productNumber} class="product-code">
|
|
80
|
-
{productNumber}
|
|
81
|
-
</ProductWrapper>
|
|
82
|
-
|
|
83
|
-
{big && (
|
|
84
|
-
<ButtonCopy
|
|
85
|
-
productNumber={productNumber}
|
|
86
|
-
copyDisabled={!big}
|
|
87
|
-
texts={buttonTexts}
|
|
88
|
-
client:idle
|
|
89
|
-
/>
|
|
90
|
-
)}
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
<div class={`code-formatted ${trackingClass}`}>
|
|
94
|
-
<div class="relative inset-0" data-pagefind-ignore>
|
|
95
|
-
{formattedNumbers.dot}
|
|
96
|
-
</div>
|
|
97
|
-
<div class="absolute inset-0" data-pagefind-ignore>
|
|
98
|
-
{formattedNumbers.dash}
|
|
99
|
-
</div>
|
|
100
|
-
<FormattedWrapper class="number-secondary">
|
|
101
|
-
{formattedNumbers.standard}
|
|
102
|
-
</FormattedWrapper>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
1
|
+
---
|
|
2
|
+
import { t } from "i18next";
|
|
3
|
+
import ButtonCopy from "./ButtonCopy.vue";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
productNumber: string | null;
|
|
7
|
+
copyDisabled?: boolean;
|
|
8
|
+
isPdp?: boolean;
|
|
9
|
+
small?: boolean;
|
|
10
|
+
big?: boolean;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
productNumber,
|
|
16
|
+
copyDisabled,
|
|
17
|
+
isPdp,
|
|
18
|
+
small,
|
|
19
|
+
big,
|
|
20
|
+
class: className
|
|
21
|
+
} = Astro.props;
|
|
22
|
+
|
|
23
|
+
const buttonTexts = {
|
|
24
|
+
copy: t('copy'),
|
|
25
|
+
copied: t('copied')
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
// Regex patterns:
|
|
29
|
+
const LIQUIDS_PATTERN = /^(\w{1})(\w{3})(\w{3})(.*)$/;
|
|
30
|
+
const WHEELS_EMBLEMS_PATTERN = /^(\w{3})(\w{3})(\w{3})(.*)(\w{3})$/;
|
|
31
|
+
const ACCESSORIES_MATS_PATTERN = /^(\w{3})(\w{3})(\w{3})(\w{1})(.*)$/;
|
|
32
|
+
const OTHER_PARTS_PATTERN = /(\w{3})/g;
|
|
33
|
+
|
|
34
|
+
const isLetter = (char: string): boolean =>
|
|
35
|
+
char.toLowerCase() !== char.toUpperCase();
|
|
36
|
+
|
|
37
|
+
const formatProductNumber = (number: string, separator: string): string => {
|
|
38
|
+
if (!number) return '';
|
|
39
|
+
|
|
40
|
+
let formatted = number;
|
|
41
|
+
|
|
42
|
+
if (isLetter(number[0]) && !isLetter(number[1]) && number.length === 9) {
|
|
43
|
+
formatted = formatted.replace(LIQUIDS_PATTERN, `$1${separator}$2${separator}$3${separator}$4`);
|
|
44
|
+
} else if (number.length >= 13) {
|
|
45
|
+
formatted = formatted.replace(WHEELS_EMBLEMS_PATTERN, `$1${separator}$2${separator}$3${separator}$4${separator}$5`);
|
|
46
|
+
} else if (number.length > 12) {
|
|
47
|
+
formatted = formatted.replace(ACCESSORIES_MATS_PATTERN, `$1${separator}$2${separator}$3${separator}$4${separator}$5`);
|
|
48
|
+
} else {
|
|
49
|
+
formatted = formatted.replace(OTHER_PARTS_PATTERN, `$1${separator}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return formatted
|
|
53
|
+
.replace(' ', separator)
|
|
54
|
+
.replace(/[^a-zA-Z0-9]$/, ''); // Remove the end trailing separator
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Memoization of formatted numbers
|
|
58
|
+
const formattedNumbers = productNumber ? {
|
|
59
|
+
standard: formatProductNumber(productNumber, '\u00A0'),
|
|
60
|
+
dot: formatProductNumber(productNumber, '.'),
|
|
61
|
+
dash: formatProductNumber(productNumber, '-')
|
|
62
|
+
} : null;
|
|
63
|
+
|
|
64
|
+
const classNames = [
|
|
65
|
+
'product-number',
|
|
66
|
+
big ? 'text-4.5' : 'number-big',
|
|
67
|
+
className
|
|
68
|
+
].filter(Boolean).join(' ');
|
|
69
|
+
|
|
70
|
+
const trackingClass = small ? 'tracking-wide' : 'tracking-tight';
|
|
71
|
+
|
|
72
|
+
const ProductWrapper = isPdp ? 'h2' : 'div';
|
|
73
|
+
const FormattedWrapper = isPdp ? 'h3' : 'div';
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
{productNumber && formattedNumbers && (
|
|
77
|
+
<div class={classNames}>
|
|
78
|
+
<div class={`p-number ${small ? "w-full" : ""}`} itemprop="identifier">
|
|
79
|
+
<ProductWrapper id={productNumber} class="product-code">
|
|
80
|
+
{productNumber}
|
|
81
|
+
</ProductWrapper>
|
|
82
|
+
|
|
83
|
+
{big && (
|
|
84
|
+
<ButtonCopy
|
|
85
|
+
productNumber={productNumber}
|
|
86
|
+
copyDisabled={!big}
|
|
87
|
+
texts={buttonTexts}
|
|
88
|
+
client:idle
|
|
89
|
+
/>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class={`code-formatted ${trackingClass}`}>
|
|
94
|
+
<div class="relative inset-0" data-pagefind-ignore>
|
|
95
|
+
{formattedNumbers.dot}
|
|
96
|
+
</div>
|
|
97
|
+
<div class="absolute inset-0" data-pagefind-ignore>
|
|
98
|
+
{formattedNumbers.dash}
|
|
99
|
+
</div>
|
|
100
|
+
<FormattedWrapper class="number-secondary">
|
|
101
|
+
{formattedNumbers.standard}
|
|
102
|
+
</FormattedWrapper>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
105
|
)}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
---
|
|
2
|
-
const { productObject, locale, index } = Astro.props;
|
|
3
|
-
import Image from "./Image.astro"
|
|
4
|
-
import ProductNumber from "./Product/ProductNumber.astro"
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
{ productObject &&
|
|
9
|
-
(
|
|
10
|
-
|
|
11
|
-
<!-- product image -->
|
|
12
|
-
<div class="img--4/3 img--small">
|
|
13
|
-
{ productObject.photo !== null ?
|
|
14
|
-
<Image
|
|
15
|
-
imageObject={
|
|
16
|
-
{
|
|
17
|
-
src: 'https://img.freepik.com/darmowe-wektory/tlo-retro-modeli-geometrycznych_52683-17902.jpg?w=1380&t=st=1706311337',
|
|
18
|
-
alt: 'Image Alt',
|
|
19
|
-
height: '180',
|
|
20
|
-
width: '240',
|
|
21
|
-
class: 'img--overlay object-cover'
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
/>
|
|
25
|
-
:
|
|
26
|
-
<img src="/1x1.png" class="bg-gray-100/70" alt={productObject.name} />
|
|
27
|
-
}
|
|
28
|
-
</div>
|
|
29
|
-
|
|
30
|
-
<!-- product deails -->
|
|
31
|
-
<div class="sm:pl-4 flex flex-col" >
|
|
32
|
-
<a class="font-light leading-none mb-2 pr-4 line-clamp-2 h-[2em] before:(content-empty absolute left-0 right-4 h-full top-0)"
|
|
33
|
-
href={productObject.url} itemprop="url"
|
|
34
|
-
title={productObject.number}
|
|
35
|
-
>
|
|
36
|
-
{ productObject.name }
|
|
37
|
-
</a>
|
|
38
|
-
|
|
39
|
-
<ProductNumber productNumber={productObject.number} copyDisabled={false} />
|
|
40
|
-
|
|
41
|
-
{ index !== null &&
|
|
42
|
-
( <meta itemprop="position" content={String(index)} />
|
|
43
|
-
<meta itemprop="name" content={productObject.name} /> )
|
|
44
|
-
}
|
|
45
|
-
</div>
|
|
46
|
-
|
|
47
|
-
)}
|
|
48
|
-
|
|
1
|
+
---
|
|
2
|
+
const { productObject, locale, index } = Astro.props;
|
|
3
|
+
import Image from "./Image.astro"
|
|
4
|
+
import ProductNumber from "./Product/ProductNumber.astro"
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
{ productObject &&
|
|
9
|
+
(
|
|
10
|
+
|
|
11
|
+
<!-- product image -->
|
|
12
|
+
<div class="img--4/3 img--small">
|
|
13
|
+
{ productObject.photo !== null ?
|
|
14
|
+
<Image
|
|
15
|
+
imageObject={
|
|
16
|
+
{
|
|
17
|
+
src: 'https://img.freepik.com/darmowe-wektory/tlo-retro-modeli-geometrycznych_52683-17902.jpg?w=1380&t=st=1706311337',
|
|
18
|
+
alt: 'Image Alt',
|
|
19
|
+
height: '180',
|
|
20
|
+
width: '240',
|
|
21
|
+
class: 'img--overlay object-cover'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/>
|
|
25
|
+
:
|
|
26
|
+
<img src="/1x1.png" class="bg-gray-100/70" alt={productObject.name} />
|
|
27
|
+
}
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<!-- product deails -->
|
|
31
|
+
<div class="sm:pl-4 flex flex-col" >
|
|
32
|
+
<a class="font-light leading-none mb-2 pr-4 line-clamp-2 h-[2em] before:(content-empty absolute left-0 right-4 h-full top-0)"
|
|
33
|
+
href={productObject.url} itemprop="url"
|
|
34
|
+
title={productObject.number}
|
|
35
|
+
>
|
|
36
|
+
{ productObject.name }
|
|
37
|
+
</a>
|
|
38
|
+
|
|
39
|
+
<ProductNumber productNumber={productObject.number} copyDisabled={false} />
|
|
40
|
+
|
|
41
|
+
{ index !== null &&
|
|
42
|
+
( <meta itemprop="position" content={String(index)} />
|
|
43
|
+
<meta itemprop="name" content={productObject.name} /> )
|
|
44
|
+
}
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
)}
|
|
48
|
+
|
package/src/components/Quote.vue
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { PropType } from 'vue';
|
|
3
|
-
|
|
4
|
-
const props = defineProps({
|
|
5
|
-
as: {
|
|
6
|
-
type: String as PropType<'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'div' | 'span'>,
|
|
7
|
-
default: 'div',
|
|
8
|
-
required: false,
|
|
9
|
-
},
|
|
10
|
-
text: {
|
|
11
|
-
type: String,
|
|
12
|
-
default: '',
|
|
13
|
-
required: false,
|
|
14
|
-
}
|
|
15
|
-
})
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
<template>
|
|
19
|
-
<component :is="props.as"
|
|
20
|
-
class="px-8 sm:px-8 mx-4 sm:mx-6 lg:max-w-4xl drop-shadow-primary text-2xl md:text-4xl lg:text-4.5xl relative font-light after:left-0 after:content-empty after:rounded-3xl after:top-0 after:absolute after:h-full after:border-solid after:border-[var(--primary)] after:border-l-3 after:z-0">
|
|
21
|
-
<slot>{{ props.text }}</slot>
|
|
22
|
-
</component>
|
|
23
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { PropType } from 'vue';
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
as: {
|
|
6
|
+
type: String as PropType<'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'div' | 'span'>,
|
|
7
|
+
default: 'div',
|
|
8
|
+
required: false,
|
|
9
|
+
},
|
|
10
|
+
text: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: '',
|
|
13
|
+
required: false,
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<component :is="props.as"
|
|
20
|
+
class="px-8 sm:px-8 mx-4 sm:mx-6 lg:max-w-4xl drop-shadow-primary text-2xl md:text-4xl lg:text-4.5xl relative font-light after:left-0 after:content-empty after:rounded-3xl after:top-0 after:absolute after:h-full after:border-solid after:border-[var(--primary)] after:border-l-3 after:z-0">
|
|
21
|
+
<slot>{{ props.text }}</slot>
|
|
22
|
+
</component>
|
|
23
|
+
</template>
|