spoko-design-system 0.2.41 → 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 -58
- 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 -101
- package/src/components/Category/CategoriesSidebar.astro +186 -186
- package/src/components/Category/CategoryDetails.astro +82 -82
- package/src/components/Category/CategoryLink.vue +23 -23
- package/src/components/Category/CategorySection.astro +69 -69
- package/src/components/Category/CategorySidebarToggler.vue +9 -9
- package/src/components/Category/SubCategoryLink.vue +29 -29
- 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 -18
- package/src/components/Product/ProductCodes.vue +167 -167
- 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 +13 -13
- package/src/utils/product/getProductChecklist.ts +15 -15
- package/src/utils/seo/getShorterDescription.ts +12 -12
- package/src/utils/text/formatDate.ts +5 -5
- package/src/utils/text/formatLocaleNumber.ts +6 -6
- package/src/utils/text/formatPad.ts +11 -11
- package/src/utils/text/getNumberFormatted.ts +32 -32
- package/src/utils/text/getTranslatedLink.ts +4 -4
- 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,43 +1,43 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
interface Engine {
|
|
3
|
-
id: number | string;
|
|
4
|
-
name: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const props = withDefaults(defineProps<{
|
|
8
|
-
engines: Engine[];
|
|
9
|
-
}>(), {
|
|
10
|
-
engines: () => []
|
|
11
|
-
});
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<template>
|
|
15
|
-
<ul v-if="engines.length" class="engine-types">
|
|
16
|
-
<li
|
|
17
|
-
v-for="(engine, index) in engines"
|
|
18
|
-
:key="engine.id"
|
|
19
|
-
class="engine-type"
|
|
20
|
-
:class="{ 'with-comma': index !== engines.length - 1 }"
|
|
21
|
-
>
|
|
22
|
-
{{ engine.name }}
|
|
23
|
-
</li>
|
|
24
|
-
</ul>
|
|
25
|
-
</template>
|
|
26
|
-
|
|
27
|
-
<style lang="scss">
|
|
28
|
-
.engine-types {
|
|
29
|
-
@apply flex flex-wrap items-center;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
.engine-type {
|
|
33
|
-
@apply inline-block;
|
|
34
|
-
|
|
35
|
-
&.with-comma {
|
|
36
|
-
@apply mr-1;
|
|
37
|
-
|
|
38
|
-
&:after {
|
|
39
|
-
content: ',';
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Engine {
|
|
3
|
+
id: number | string;
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
engines: Engine[];
|
|
9
|
+
}>(), {
|
|
10
|
+
engines: () => []
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<ul v-if="engines.length" class="engine-types">
|
|
16
|
+
<li
|
|
17
|
+
v-for="(engine, index) in engines"
|
|
18
|
+
:key="engine.id"
|
|
19
|
+
class="engine-type"
|
|
20
|
+
:class="{ 'with-comma': index !== engines.length - 1 }"
|
|
21
|
+
>
|
|
22
|
+
{{ engine.name }}
|
|
23
|
+
</li>
|
|
24
|
+
</ul>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<style lang="scss">
|
|
28
|
+
.engine-types {
|
|
29
|
+
@apply flex flex-wrap items-center;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.engine-type {
|
|
33
|
+
@apply inline-block;
|
|
34
|
+
|
|
35
|
+
&.with-comma {
|
|
36
|
+
@apply mr-1;
|
|
37
|
+
|
|
38
|
+
&:after {
|
|
39
|
+
content: ',';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
43
|
</style>
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
---
|
|
2
|
-
import { Image } from 'astro:assets'
|
|
3
|
-
|
|
4
|
-
interface ImageObject {
|
|
5
|
-
src: string;
|
|
6
|
-
alt?: string;
|
|
7
|
-
height?: string | number;
|
|
8
|
-
width?: string | number;
|
|
9
|
-
loading?: 'lazy' | 'eager';
|
|
10
|
-
srcset?: string[];
|
|
11
|
-
class?: string;
|
|
12
|
-
index?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const { imageObject, imagesApiUrl } = Astro.props as { imageObject: ImageObject; imagesApiUrl: string };
|
|
16
|
-
|
|
17
|
-
let inputProps = {};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (imageObject.index && imageObject.index === 1) {
|
|
21
|
-
inputProps['data-pagefind-meta'] = 'image[src], image_alt[alt]';
|
|
22
|
-
imageObject.loading = 'eager'
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (imageObject.srcset && imageObject.srcset.length) {
|
|
26
|
-
inputProps['widths'] = imageObject.srcset
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
<Image
|
|
31
|
-
src={`${imagesApiUrl}${imageObject.src}`}
|
|
32
|
-
alt={imageObject.alt}
|
|
33
|
-
height={imageObject.height}
|
|
34
|
-
width={imageObject.width}
|
|
35
|
-
loading={imageObject.loading ? imageObject.loading : 'lazy'}
|
|
36
|
-
itemprop="image"
|
|
37
|
-
format="avif"
|
|
38
|
-
data-pagefind-index-attrs={imageObject.alt}
|
|
39
|
-
onerror="this.style.display='none';"
|
|
40
|
-
class={`product-image ${imageObject.class || ''}`}
|
|
41
|
-
{ ...inputProps}
|
|
1
|
+
---
|
|
2
|
+
import { Image } from 'astro:assets'
|
|
3
|
+
|
|
4
|
+
interface ImageObject {
|
|
5
|
+
src: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
height?: string | number;
|
|
8
|
+
width?: string | number;
|
|
9
|
+
loading?: 'lazy' | 'eager';
|
|
10
|
+
srcset?: string[];
|
|
11
|
+
class?: string;
|
|
12
|
+
index?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { imageObject, imagesApiUrl } = Astro.props as { imageObject: ImageObject; imagesApiUrl: string };
|
|
16
|
+
|
|
17
|
+
let inputProps = {};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if (imageObject.index && imageObject.index === 1) {
|
|
21
|
+
inputProps['data-pagefind-meta'] = 'image[src], image_alt[alt]';
|
|
22
|
+
imageObject.loading = 'eager'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (imageObject.srcset && imageObject.srcset.length) {
|
|
26
|
+
inputProps['widths'] = imageObject.srcset
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
<Image
|
|
31
|
+
src={`${imagesApiUrl}${imageObject.src}`}
|
|
32
|
+
alt={imageObject.alt}
|
|
33
|
+
height={imageObject.height}
|
|
34
|
+
width={imageObject.width}
|
|
35
|
+
loading={imageObject.loading ? imageObject.loading : 'lazy'}
|
|
36
|
+
itemprop="image"
|
|
37
|
+
format="avif"
|
|
38
|
+
data-pagefind-index-attrs={imageObject.alt}
|
|
39
|
+
onerror="this.style.display='none';"
|
|
40
|
+
class={`product-image ${imageObject.class || ''}`}
|
|
41
|
+
{ ...inputProps}
|
|
42
42
|
/>
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { Product } from '../../types/Product';
|
|
3
|
-
import ProductNumber from "@components/Product/ProductNumber.astro"
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
product: Product;
|
|
7
|
-
nameFormatted: string;
|
|
8
|
-
price?: string;
|
|
9
|
-
url: string;
|
|
10
|
-
index?: number | null;
|
|
11
|
-
bigTile?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const { product, nameFormatted, price, url, index, bigTile } = Astro.props;
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
<div class={`flex flex-col ${bigTile ? '' : 'sm:pl-4'}`}>
|
|
19
|
-
{price && (
|
|
20
|
-
<p class="block mb-2 font-600 font-headbold text-5">
|
|
21
|
-
{price}
|
|
22
|
-
</p>
|
|
23
|
-
)}
|
|
24
|
-
<a
|
|
25
|
-
class="product-link--url"
|
|
26
|
-
href={url}
|
|
27
|
-
itemprop="url"
|
|
28
|
-
title={product.number}
|
|
29
|
-
set:html={nameFormatted}
|
|
30
|
-
/>
|
|
31
|
-
<ProductNumber productNumber={product.number} copyDisabled={true} />
|
|
32
|
-
{index !== null && (
|
|
33
|
-
<>
|
|
34
|
-
<meta itemprop="position" content={index} />
|
|
35
|
-
<meta itemprop="name" content={nameFormatted} />
|
|
36
|
-
</>
|
|
37
|
-
)}
|
|
1
|
+
---
|
|
2
|
+
import type { Product } from '../../types/Product';
|
|
3
|
+
import ProductNumber from "@components/Product/ProductNumber.astro"
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
product: Product;
|
|
7
|
+
nameFormatted: string;
|
|
8
|
+
price?: string;
|
|
9
|
+
url: string;
|
|
10
|
+
index?: number | null;
|
|
11
|
+
bigTile?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { product, nameFormatted, price, url, index, bigTile } = Astro.props;
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<div class={`flex flex-col ${bigTile ? '' : 'sm:pl-4'}`}>
|
|
19
|
+
{price && (
|
|
20
|
+
<p class="block mb-2 font-600 font-headbold text-5">
|
|
21
|
+
{price}
|
|
22
|
+
</p>
|
|
23
|
+
)}
|
|
24
|
+
<a
|
|
25
|
+
class="product-link--url"
|
|
26
|
+
href={url}
|
|
27
|
+
itemprop="url"
|
|
28
|
+
title={product.number}
|
|
29
|
+
set:html={nameFormatted}
|
|
30
|
+
/>
|
|
31
|
+
<ProductNumber productNumber={product.number} copyDisabled={true} />
|
|
32
|
+
{index !== null && (
|
|
33
|
+
<>
|
|
34
|
+
<meta itemprop="position" content={index} />
|
|
35
|
+
<meta itemprop="name" content={nameFormatted} />
|
|
36
|
+
</>
|
|
37
|
+
)}
|
|
38
38
|
</div>
|
|
@@ -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,38 +1,38 @@
|
|
|
1
|
-
---
|
|
2
|
-
import ProductTile from "../components/ProductTile.astro";
|
|
3
|
-
export const productObject = {
|
|
4
|
-
name: "Combi-instrument MFA+",
|
|
5
|
-
url: "https://google.com",
|
|
6
|
-
number: "6R0920870F",
|
|
7
|
-
photo:
|
|
8
|
-
"https://img.freepik.com/darmowe-wektory/tlo-retro-modeli-geometrycznych_52683-17902.jpg?w=1380&t=st=1706311337",
|
|
9
|
-
};
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
<div class="bg-white rounded-lg p-4 w-full">
|
|
13
|
-
<swiper-container
|
|
14
|
-
class="flex w-full max-w-full"
|
|
15
|
-
grid-rows="1"
|
|
16
|
-
mousewheel-force-to-axis="true"
|
|
17
|
-
navigation="false"
|
|
18
|
-
scrollbar="false"
|
|
19
|
-
slides-per-view="auto"
|
|
20
|
-
space-between="0"
|
|
21
|
-
>
|
|
22
|
-
<swiper-slide class="carousel-product-tile">
|
|
23
|
-
<ProductTile productObject={productObject} />
|
|
24
|
-
</swiper-slide>
|
|
25
|
-
<swiper-slide class="carousel-product-tile">
|
|
26
|
-
<ProductTile productObject={productObject} />
|
|
27
|
-
</swiper-slide>
|
|
28
|
-
<swiper-slide class="carousel-product-tile">
|
|
29
|
-
<ProductTile productObject={productObject} />
|
|
30
|
-
</swiper-slide>
|
|
31
|
-
<swiper-slide class="carousel-product-tile">
|
|
32
|
-
<ProductTile productObject={productObject} />
|
|
33
|
-
</swiper-slide>
|
|
34
|
-
<swiper-slide class="carousel-product-tile">
|
|
35
|
-
<ProductTile productObject={productObject} />
|
|
36
|
-
</swiper-slide>
|
|
37
|
-
</swiper-container>
|
|
38
|
-
</div>
|
|
1
|
+
---
|
|
2
|
+
import ProductTile from "../components/ProductTile.astro";
|
|
3
|
+
export const productObject = {
|
|
4
|
+
name: "Combi-instrument MFA+",
|
|
5
|
+
url: "https://google.com",
|
|
6
|
+
number: "6R0920870F",
|
|
7
|
+
photo:
|
|
8
|
+
"https://img.freepik.com/darmowe-wektory/tlo-retro-modeli-geometrycznych_52683-17902.jpg?w=1380&t=st=1706311337",
|
|
9
|
+
};
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<div class="bg-white rounded-lg p-4 w-full">
|
|
13
|
+
<swiper-container
|
|
14
|
+
class="flex w-full max-w-full"
|
|
15
|
+
grid-rows="1"
|
|
16
|
+
mousewheel-force-to-axis="true"
|
|
17
|
+
navigation="false"
|
|
18
|
+
scrollbar="false"
|
|
19
|
+
slides-per-view="auto"
|
|
20
|
+
space-between="0"
|
|
21
|
+
>
|
|
22
|
+
<swiper-slide class="carousel-product-tile">
|
|
23
|
+
<ProductTile productObject={productObject} />
|
|
24
|
+
</swiper-slide>
|
|
25
|
+
<swiper-slide class="carousel-product-tile">
|
|
26
|
+
<ProductTile productObject={productObject} />
|
|
27
|
+
</swiper-slide>
|
|
28
|
+
<swiper-slide class="carousel-product-tile">
|
|
29
|
+
<ProductTile productObject={productObject} />
|
|
30
|
+
</swiper-slide>
|
|
31
|
+
<swiper-slide class="carousel-product-tile">
|
|
32
|
+
<ProductTile productObject={productObject} />
|
|
33
|
+
</swiper-slide>
|
|
34
|
+
<swiper-slide class="carousel-product-tile">
|
|
35
|
+
<ProductTile productObject={productObject} />
|
|
36
|
+
</swiper-slide>
|
|
37
|
+
</swiper-container>
|
|
38
|
+
</div>
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import type { PropType } from 'vue'
|
|
3
|
-
import PrCode from './PrCode.vue';
|
|
4
|
-
|
|
5
|
-
const props = defineProps({
|
|
6
|
-
prcodes: {
|
|
7
|
-
type: Object as PropType<string[] | null>,
|
|
8
|
-
default: null,
|
|
9
|
-
required: true,
|
|
10
|
-
},
|
|
11
|
-
isPdp: {
|
|
12
|
-
type: Boolean,
|
|
13
|
-
default: false,
|
|
14
|
-
required: false,
|
|
15
|
-
},
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
const codes = props.prcodes || []
|
|
19
|
-
const decodedCodes = codes ? codes.sort() : []
|
|
20
|
-
|
|
21
|
-
const settings = {
|
|
22
|
-
prcodes: decodedCodes,
|
|
23
|
-
}
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
<template>
|
|
27
|
-
|
|
28
|
-
<span
|
|
29
|
-
v-for="(prcode, index) in settings.prcodes"
|
|
30
|
-
:key="index"
|
|
31
|
-
class="not-last:mr-1"
|
|
32
|
-
>
|
|
33
|
-
<PrCode :prcode="prcode" v-if="!String(prcode).includes('+')" />
|
|
34
|
-
<span v-else >
|
|
35
|
-
<PrCode v-for="(splittedCode, index2) in String(prcode).split('+')" :key="index2" :prcode="splittedCode" />
|
|
36
|
-
</span>
|
|
37
|
-
</span>
|
|
38
|
-
</template>
|
|
39
|
-
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { PropType } from 'vue'
|
|
3
|
+
import PrCode from './PrCode.vue';
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
prcodes: {
|
|
7
|
+
type: Object as PropType<string[] | null>,
|
|
8
|
+
default: null,
|
|
9
|
+
required: true,
|
|
10
|
+
},
|
|
11
|
+
isPdp: {
|
|
12
|
+
type: Boolean,
|
|
13
|
+
default: false,
|
|
14
|
+
required: false,
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const codes = props.prcodes || []
|
|
19
|
+
const decodedCodes = codes ? codes.sort() : []
|
|
20
|
+
|
|
21
|
+
const settings = {
|
|
22
|
+
prcodes: decodedCodes,
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
|
|
28
|
+
<span
|
|
29
|
+
v-for="(prcode, index) in settings.prcodes"
|
|
30
|
+
:key="index"
|
|
31
|
+
class="not-last:mr-1"
|
|
32
|
+
>
|
|
33
|
+
<PrCode :prcode="prcode" v-if="!String(prcode).includes('+')" />
|
|
34
|
+
<span v-else >
|
|
35
|
+
<PrCode v-for="(splittedCode, index2) in String(prcode).split('+')" :key="index2" :prcode="splittedCode" />
|
|
36
|
+
</span>
|
|
37
|
+
</span>
|
|
38
|
+
</template>
|
|
39
|
+
|