@shopware/cms-base-layer 1.5.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +330 -12
- package/app/app.config.ts +7 -0
- package/app/assets/icons/check-circle.svg +3 -0
- package/app/assets/icons/checkmark.svg +3 -0
- package/app/assets/icons/chevron.svg +3 -0
- package/app/assets/icons/exclamation-circle.svg +3 -0
- package/app/assets/icons/star-empty.svg +3 -0
- package/app/assets/icons/star-filled.svg +3 -0
- package/app/assets/icons/user.svg +1 -0
- package/app/components/SwCategoryNavigation.vue +76 -0
- package/app/components/SwCategoryNavigationLink.vue +128 -0
- package/{components → app/components}/SwContactForm.vue +27 -27
- package/app/components/SwFilterChips.vue +144 -0
- package/app/components/SwListingProductPrice.vue +89 -0
- package/{components → app/components}/SwNewsletterForm.vue +45 -34
- package/{components → app/components}/SwPagination.vue +3 -5
- package/{components → app/components}/SwProductAddToCart.vue +22 -27
- package/app/components/SwProductCard.vue +170 -0
- package/app/components/SwProductCardDetails.vue +57 -0
- package/app/components/SwProductCardImage.vue +87 -0
- package/app/components/SwProductCardSkeleton.vue +33 -0
- package/app/components/SwProductListingFilter.vue +64 -0
- package/app/components/SwProductListingFilters.vue +308 -0
- package/{components → app/components}/SwProductReviews.vue +28 -13
- package/app/components/SwProductReviewsForm.vue +292 -0
- package/app/components/SwQuantitySelect.vue +106 -0
- package/{components → app/components}/SwSlider.vue +4 -4
- package/app/components/SwSortDropdown.vue +83 -0
- package/app/components/SwStockInfo.vue +44 -0
- package/{components → app/components}/SwVariantConfigurator.vue +1 -1
- package/app/components/listing-filters/SwFilterPrice.vue +214 -0
- package/app/components/listing-filters/SwFilterProperties.vue +113 -0
- package/app/components/listing-filters/SwFilterRating.vue +90 -0
- package/app/components/listing-filters/SwFilterShippingFree.vue +107 -0
- package/{components → app/components}/public/cms/CmsPage.vue +19 -4
- package/{components → app/components}/public/cms/block/CmsBlockGalleryBuybox.vue +5 -5
- package/{components → app/components}/public/cms/block/CmsBlockImageBubbleRow.vue +5 -5
- package/app/components/public/cms/block/CmsBlockImageFourColumn.vue +41 -0
- package/app/components/public/cms/block/CmsBlockImageGalleryBig.vue +42 -0
- package/app/components/public/cms/block/CmsBlockImageHighlightRow.vue +37 -0
- package/{components → app/components}/public/cms/block/CmsBlockImageSimpleGrid.vue +11 -5
- package/{components → app/components}/public/cms/block/CmsBlockImageText.vue +7 -3
- package/{components → app/components}/public/cms/block/CmsBlockImageTextBubble.vue +13 -16
- package/{components → app/components}/public/cms/block/CmsBlockImageTextCover.vue +7 -9
- package/app/components/public/cms/block/CmsBlockImageTextGallery.vue +88 -0
- package/app/components/public/cms/block/CmsBlockImageTextRow.vue +53 -0
- package/{components → app/components}/public/cms/block/CmsBlockImageThreeColumn.vue +10 -4
- package/app/components/public/cms/block/CmsBlockImageThreeCover.vue +37 -0
- package/app/components/public/cms/block/CmsBlockImageTwoColumn.vue +37 -0
- package/{components → app/components}/public/cms/block/CmsBlockProductHeading.vue +1 -1
- package/{components → app/components}/public/cms/block/CmsBlockProductThreeColumn.vue +10 -4
- package/{components → app/components}/public/cms/block/CmsBlockSidebarFilter.vue +3 -1
- package/app/components/public/cms/block/CmsBlockTextOnImage.vue +30 -0
- package/app/components/public/cms/element/CmsElementBuyBox.vue +145 -0
- package/app/components/public/cms/element/CmsElementCategoryNavigation.vue +53 -0
- package/{components → app/components}/public/cms/element/CmsElementCrossSelling.vue +3 -3
- package/{components → app/components}/public/cms/element/CmsElementImage.vue +52 -13
- package/app/components/public/cms/element/CmsElementImageGallery.vue +158 -0
- package/{components → app/components}/public/cms/element/CmsElementImageSlider.vue +2 -2
- package/{components → app/components}/public/cms/element/CmsElementProductBox.vue +2 -1
- package/app/components/public/cms/element/CmsElementProductDescriptionReviews.vue +217 -0
- package/{components → app/components}/public/cms/element/CmsElementProductListing.vue +23 -94
- package/app/components/public/cms/element/CmsElementProductName.vue +11 -0
- package/{components → app/components}/public/cms/element/CmsElementProductSlider.vue +4 -4
- package/{components → app/components}/public/cms/element/CmsElementText.vue +8 -2
- package/app/components/public/cms/element/SwProductListingPagination.vue +70 -0
- package/{components → app/components}/public/cms/section/CmsSectionDefault.vue +1 -1
- package/app/components/public/cms/section/CmsSectionSidebar.vue +36 -0
- package/app/components/public/cms/skeleton/ProductCardSkeleton.vue +28 -0
- package/app/components/ui/BaseButton.vue +99 -0
- package/app/components/ui/BaseIcon.vue +15 -0
- package/app/components/ui/Checkbox.vue +49 -0
- package/app/components/ui/CheckmarkIcon.vue +23 -0
- package/app/components/ui/ChevronIcon.vue +37 -0
- package/app/components/ui/ExclamationIcon.vue +11 -0
- package/app/components/ui/IconButton.vue +32 -0
- package/app/components/ui/RadioButton.vue +26 -0
- package/app/components/ui/StarIcon.vue +18 -0
- package/app/components/ui/SwitchButton.vue +100 -0
- package/app/components/ui/UserIcon.vue +11 -0
- package/app/components/ui/WishlistIcon.vue +20 -0
- package/app/composables/useImagePlaceholder.ts +27 -0
- package/{helpers → app/helpers}/clientOnly.ts +5 -0
- package/app/providers/shopware.test.ts +213 -0
- package/app/providers/shopware.ts +107 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +2 -2
- package/index.d.ts +12 -0
- package/nuxt.config.ts +80 -6
- package/package.json +29 -21
- package/uno.config.ts +83 -0
- package/components/SwCategoryNavigation.vue +0 -44
- package/components/SwCategoryNavigationLink.vue +0 -57
- package/components/SwListingProductPrice.vue +0 -89
- package/components/SwProductCard.vue +0 -286
- package/components/SwProductListingFilter.vue +0 -42
- package/components/SwProductListingFilters.vue +0 -292
- package/components/listing-filters/SwFilterPrice.vue +0 -160
- package/components/listing-filters/SwFilterProperties.vue +0 -123
- package/components/listing-filters/SwFilterRating.vue +0 -101
- package/components/listing-filters/SwFilterShippingFree.vue +0 -104
- package/components/public/cms/block/CmsBlockImageFourColumn.vue +0 -29
- package/components/public/cms/block/CmsBlockImageHighlightRow.vue +0 -27
- package/components/public/cms/block/CmsBlockImageTextGallery.vue +0 -85
- package/components/public/cms/block/CmsBlockImageTextRow.vue +0 -43
- package/components/public/cms/block/CmsBlockImageThreeCover.vue +0 -27
- package/components/public/cms/block/CmsBlockImageTwoColumn.vue +0 -25
- package/components/public/cms/block/CmsBlockTextOnImage.vue +0 -20
- package/components/public/cms/element/CmsElementBuyBox.vue +0 -190
- package/components/public/cms/element/CmsElementCategoryNavigation.vue +0 -167
- package/components/public/cms/element/CmsElementImageGallery.vue +0 -249
- package/components/public/cms/element/CmsElementProductDescriptionReviews.vue +0 -123
- package/components/public/cms/element/CmsElementProductName.vue +0 -10
- package/components/public/cms/section/CmsSectionSidebar.vue +0 -41
- package/components/public/cms/skeleton/ProductCardSkeleton.vue +0 -44
- /package/{components → app/components}/SwMedia3D.vue +0 -0
- /package/{components → app/components}/SwProductGallery.vue +0 -0
- /package/{components → app/components}/SwProductPrice.vue +0 -0
- /package/{components → app/components}/SwProductUnits.vue +0 -0
- /package/{components → app/components}/SwSharedPrice.vue +0 -0
- /package/{components → app/components}/public/cms/CmsGenericBlock.md +0 -0
- /package/{components → app/components}/public/cms/CmsGenericBlock.vue +0 -0
- /package/{components → app/components}/public/cms/CmsGenericElement.md +0 -0
- /package/{components → app/components}/public/cms/CmsGenericElement.vue +0 -0
- /package/{components → app/components}/public/cms/CmsNoComponent.vue +0 -0
- /package/{components → app/components}/public/cms/CmsPage.md +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockCategoryNavigation.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockCenterText.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockCrossSelling.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockCustomForm.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockDefault.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockForm.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockHtml.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockImage.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockImageCover.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockImageGallery.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockImageSlider.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockProductDescriptionReviews.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockProductListing.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockProductSlider.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockText.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockTextHero.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockTextTeaser.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockTextTeaserSection.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockTextThreeColumn.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockTextTwoColumn.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockVimeoVideo.vue +0 -0
- /package/{components → app/components}/public/cms/block/CmsBlockYoutubeVideo.vue +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementBuyBox.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementCategoryNavigation.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementCrossSelling.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementCustomForm.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementCustomForm.vue +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementForm.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementForm.vue +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementHtml.vue +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementImage.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementImageGallery.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementImageGallery3dPlaceholder.vue +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementImageSlider.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementManufacturerLogo.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementManufacturerLogo.vue +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementProductBox.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementProductDescriptionReviews.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementProductListing.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementProductName.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementProductSlider.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementSidebarFilter.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementSidebarFilter.vue +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementText.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementVimeoVideo.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementVimeoVideo.vue +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementYoutubeVideo.md +0 -0
- /package/{components → app/components}/public/cms/element/CmsElementYoutubeVideo.vue +0 -0
- /package/{components → app/components}/public/cms/section/CmsSectionDefault.md +0 -0
- /package/{components → app/components}/public/cms/section/CmsSectionSidebar.md +0 -0
- /package/{helpers → app/helpers}/html-to-vue/ast.ts +0 -0
- /package/{helpers → app/helpers}/html-to-vue/getOptionsFromNode.test.ts +0 -0
- /package/{helpers → app/helpers}/html-to-vue/getOptionsFromNode.ts +0 -0
- /package/{helpers → app/helpers}/html-to-vue/renderToHtml.ts +0 -0
- /package/{helpers → app/helpers}/html-to-vue/renderer.ts +0 -0
- /package/{helpers → app/helpers}/media/isSpatial.ts +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { defineProvider } from "@nuxt/image/runtime";
|
|
2
|
+
import { encodeUrlPath } from "@shopware/helpers";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shopware Image Provider for Nuxt Image
|
|
6
|
+
*
|
|
7
|
+
* Generates optimized image URLs compatible with Shopware's remote thumbnail generation feature.
|
|
8
|
+
* This provider appends transformation parameters as query strings to image URLs, which are then
|
|
9
|
+
* processed by your configured image transformation backend.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* Shopware has built-in thumbnail generation (using GD2 or ImageMagick) that creates predefined
|
|
13
|
+
* sizes (400x400, 800x800, 1920x1920) during upload. However, for **on-the-fly transformations**
|
|
14
|
+
* via query parameters (like `?width=800`), you need to configure remote thumbnail generation:
|
|
15
|
+
* - Shopware Cloud: Uses Fastly CDN (configured automatically)
|
|
16
|
+
* - Self-hosted: Requires external middleware (Thumbor, Sharp, imgproxy) or plugins like FroshPlatformThumbnailProcessor
|
|
17
|
+
*
|
|
18
|
+
* Without remote thumbnail generation, query parameters will have no effect and original/predefined thumbnails are served
|
|
19
|
+
*
|
|
20
|
+
* @param src - The source image URL (e.g., "/media/image/product.jpg")
|
|
21
|
+
* @param options - Configuration options
|
|
22
|
+
* @param options.modifiers - Image transformation modifiers
|
|
23
|
+
* @param options.modifiers.width - Target width in pixels
|
|
24
|
+
* @param options.modifiers.height - Target height in pixels
|
|
25
|
+
* @param options.modifiers.quality - Image quality (0-100)
|
|
26
|
+
* @param options.modifiers.format - Output format (jpg, png, webp, avif)
|
|
27
|
+
* @param options.modifiers.fit - Resize mode (cover, contain, crop, bounds, crop_center)
|
|
28
|
+
*
|
|
29
|
+
* @returns Object containing the transformed image URL with query parameters
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // Basic usage
|
|
34
|
+
* const result = getImage('/media/image/product.jpg', {
|
|
35
|
+
* modifiers: {
|
|
36
|
+
* width: 800,
|
|
37
|
+
* height: 600,
|
|
38
|
+
* quality: 85,
|
|
39
|
+
* format: 'webp',
|
|
40
|
+
* fit: 'cover'
|
|
41
|
+
* }
|
|
42
|
+
* });
|
|
43
|
+
* // Returns: { url: '/media/image/product.jpg?width=800&height=600&quality=85&format=webp&fit=cover' }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* // With existing query parameters
|
|
49
|
+
* const result = getImage('/media/image/product.jpg?v=123', {
|
|
50
|
+
* modifiers: { width: 400 }
|
|
51
|
+
* });
|
|
52
|
+
* // Returns: { url: '/media/image/product.jpg?v=123&width=400' }
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* // No modifiers - returns original URL
|
|
58
|
+
* const result = getImage('/media/image/product.jpg');
|
|
59
|
+
* // Returns: { url: '/media/image/product.jpg' }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @see {@link https://developer.shopware.com/docs/guides/plugins/plugins/content/media/remote-thumbnail-generation.html | Shopware Remote Thumbnail Generation}
|
|
63
|
+
* @see {@link https://image.nuxt.com/providers/custom | Nuxt Image Custom Providers}
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
export default defineProvider(() => ({
|
|
67
|
+
getImage(src, { modifiers }, _ctx) {
|
|
68
|
+
// Encode special characters in the URL pathname (commas, spaces, etc.)
|
|
69
|
+
const encodedSrc = encodeUrlPath(src);
|
|
70
|
+
|
|
71
|
+
const params = new URLSearchParams();
|
|
72
|
+
|
|
73
|
+
// Map Nuxt Image modifiers to Shopware query parameters
|
|
74
|
+
if (modifiers.width) {
|
|
75
|
+
params.set("width", String(modifiers.width));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (modifiers.height) {
|
|
79
|
+
params.set("height", String(modifiers.height));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (modifiers.quality) {
|
|
83
|
+
params.set("quality", String(modifiers.quality));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (modifiers.format) {
|
|
87
|
+
params.set("format", String(modifiers.format));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (modifiers.fit) {
|
|
91
|
+
params.set("fit", String(modifiers.fit));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const query = params.toString();
|
|
95
|
+
|
|
96
|
+
if (!query) {
|
|
97
|
+
return { url: encodedSrc };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check if URL already has query parameters
|
|
101
|
+
const separator = encodedSrc.includes("?") ? "&" : "?";
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
url: `${encodedSrc}${separator}${query}`,
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
}));
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
|
-
declare const
|
|
3
|
+
declare const _default: _nuxt_schema.NuxtModule<_nuxt_schema.ModuleOptions, _nuxt_schema.ModuleOptions, false>;
|
|
4
4
|
|
|
5
|
-
export {
|
|
5
|
+
export { _default as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
2
|
|
|
3
|
-
declare const
|
|
3
|
+
declare const _default: _nuxt_schema.NuxtModule<_nuxt_schema.ModuleOptions, _nuxt_schema.ModuleOptions, false>;
|
|
4
4
|
|
|
5
|
-
export {
|
|
5
|
+
export { _default as default };
|
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import __cjs_mod__ from 'module';
|
|
|
10
10
|
const __filename = __cjs_url__.fileURLToPath(import.meta.url);
|
|
11
11
|
const __dirname = __cjs_path__.dirname(__filename);
|
|
12
12
|
const require = __cjs_mod__.createRequire(import.meta.url);
|
|
13
|
-
const
|
|
13
|
+
const index = defineNuxtModule({
|
|
14
14
|
meta: {
|
|
15
15
|
name: "@shopware/cms-base",
|
|
16
16
|
configKey: "shopware-cms"
|
|
@@ -28,4 +28,4 @@ const nuxtModule = defineNuxtModule({
|
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
export {
|
|
31
|
+
export { index as default };
|
package/index.d.ts
ADDED
package/nuxt.config.ts
CHANGED
|
@@ -1,19 +1,93 @@
|
|
|
1
|
+
import { createResolver } from "@nuxt/kit";
|
|
1
2
|
import type { NuxtConfig } from "@nuxt/schema";
|
|
2
3
|
import { defineNuxtConfig } from "nuxt/config";
|
|
4
|
+
|
|
5
|
+
const { resolve: resolveLayer } = createResolver(import.meta.url);
|
|
6
|
+
|
|
3
7
|
export default defineNuxtConfig({
|
|
8
|
+
modules: ["@unocss/nuxt", "@nuxt/image"],
|
|
9
|
+
|
|
10
|
+
// @ts-ignore - @nuxt/image config may not be typed in some layer contexts
|
|
11
|
+
image: {
|
|
12
|
+
quality: 90,
|
|
13
|
+
format: ["webp", "avif", "jpg"],
|
|
14
|
+
// Custom Shopware provider that maps Nuxt Image modifiers to Shopware query parameters
|
|
15
|
+
provider: "shopware",
|
|
16
|
+
providers: {
|
|
17
|
+
shopware: {
|
|
18
|
+
name: "shopware",
|
|
19
|
+
provider: resolveLayer("./app/providers/shopware.ts"),
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Responsive breakpoints matching UnoCSS
|
|
24
|
+
screens: {
|
|
25
|
+
xs: 320,
|
|
26
|
+
sm: 640,
|
|
27
|
+
md: 768,
|
|
28
|
+
lg: 1024,
|
|
29
|
+
xl: 1280,
|
|
30
|
+
xxl: 1536,
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
// Presets for common CMS use cases
|
|
34
|
+
presets: {
|
|
35
|
+
productCard: {
|
|
36
|
+
modifiers: {
|
|
37
|
+
format: "webp",
|
|
38
|
+
quality: 90,
|
|
39
|
+
fit: "cover",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
productDetail: {
|
|
43
|
+
modifiers: {
|
|
44
|
+
format: "webp",
|
|
45
|
+
quality: 90,
|
|
46
|
+
fit: "contain",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
thumbnail: {
|
|
50
|
+
modifiers: {
|
|
51
|
+
format: "webp",
|
|
52
|
+
quality: 90,
|
|
53
|
+
width: 150,
|
|
54
|
+
height: 150,
|
|
55
|
+
fit: "cover",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
hero: {
|
|
59
|
+
modifiers: {
|
|
60
|
+
format: "webp",
|
|
61
|
+
quality: 95,
|
|
62
|
+
fit: "cover",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
|
|
4
68
|
components: [
|
|
5
69
|
{
|
|
6
|
-
path: "./components
|
|
7
|
-
|
|
8
|
-
|
|
70
|
+
path: resolveLayer("./app/components"),
|
|
71
|
+
pattern: "Sw*",
|
|
72
|
+
extensions: [".vue"],
|
|
73
|
+
global: true,
|
|
9
74
|
},
|
|
10
75
|
{
|
|
11
|
-
path: "./components/",
|
|
12
|
-
|
|
76
|
+
path: resolveLayer("./app/components/ui"),
|
|
77
|
+
extensions: [".vue"],
|
|
78
|
+
prefix: "Sw",
|
|
79
|
+
global: true,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
path: resolveLayer("./app/components/public"),
|
|
83
|
+
pathPrefix: false,
|
|
84
|
+
global: true,
|
|
13
85
|
extensions: [".vue"],
|
|
14
|
-
global: false,
|
|
15
86
|
},
|
|
16
87
|
],
|
|
88
|
+
alias: {
|
|
89
|
+
"@cms-assets": resolveLayer("./app/assets"),
|
|
90
|
+
},
|
|
17
91
|
build: {
|
|
18
92
|
transpile: ["@shopware/cms-base-layer"],
|
|
19
93
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopware/cms-base-layer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Vue CMS Nuxt Layer for Shopware",
|
|
5
5
|
"author": "Shopware",
|
|
6
6
|
"repository": {
|
|
@@ -23,47 +23,55 @@
|
|
|
23
23
|
"main": "./nuxt.config.ts",
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
|
-
"
|
|
26
|
+
"app",
|
|
27
27
|
"helpers",
|
|
28
28
|
"index.cjs",
|
|
29
|
-
"
|
|
29
|
+
"index.d.ts",
|
|
30
|
+
"app.config.ts",
|
|
31
|
+
"nuxt.config.ts",
|
|
32
|
+
"uno.config.ts"
|
|
30
33
|
],
|
|
31
34
|
"dependencies": {
|
|
32
|
-
"@
|
|
33
|
-
"@
|
|
34
|
-
"@
|
|
35
|
+
"@iconify-json/carbon": "1.2.14",
|
|
36
|
+
"@nuxt/image": "2.0.0",
|
|
37
|
+
"@nuxt/kit": "4.2.1",
|
|
38
|
+
"@tresjs/cientos": "4.3.1",
|
|
39
|
+
"@tresjs/core": "4.3.6",
|
|
40
|
+
"@unocss/nuxt": "66.5.4",
|
|
35
41
|
"@vuelidate/core": "2.0.3",
|
|
36
42
|
"@vuelidate/validators": "2.0.4",
|
|
37
|
-
"@vueuse/core": "
|
|
43
|
+
"@vueuse/core": "14.0.0",
|
|
38
44
|
"entities": "6.0.0",
|
|
39
45
|
"html-to-ast": "0.0.6",
|
|
40
46
|
"three": "0.173.0",
|
|
41
|
-
"
|
|
47
|
+
"unocss": "66.5.4",
|
|
48
|
+
"vue": "3.5.24",
|
|
42
49
|
"xss": "1.0.15",
|
|
43
|
-
"@shopware/composables": "1.
|
|
44
|
-
"@shopware/api-client": "1.
|
|
45
|
-
"@shopware/helpers": "1.
|
|
50
|
+
"@shopware/composables": "1.10.0",
|
|
51
|
+
"@shopware/api-client": "1.4.0",
|
|
52
|
+
"@shopware/helpers": "1.6.0"
|
|
46
53
|
},
|
|
47
54
|
"devDependencies": {
|
|
48
55
|
"@biomejs/biome": "1.8.3",
|
|
49
|
-
"@nuxt/schema": "
|
|
56
|
+
"@nuxt/schema": "4.2.1",
|
|
50
57
|
"@types/three": "0.173.0",
|
|
51
|
-
"@vitest/coverage-v8": "3.2.
|
|
52
|
-
"nuxt": "
|
|
53
|
-
"typescript": "5.
|
|
58
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
59
|
+
"nuxt": "4.2.1",
|
|
60
|
+
"typescript": "5.9.3",
|
|
54
61
|
"unbuild": "2.0.0",
|
|
55
|
-
"vitest": "3.2.
|
|
56
|
-
"vue-router": "4.
|
|
57
|
-
"vue-tsc": "
|
|
62
|
+
"vitest": "3.2.4",
|
|
63
|
+
"vue-router": "4.6.3",
|
|
64
|
+
"vue-tsc": "3.1.4",
|
|
58
65
|
"tsconfig": "0.0.0"
|
|
59
66
|
},
|
|
60
67
|
"scripts": {
|
|
61
|
-
"build": "unbuild",
|
|
68
|
+
"build": "nuxt prepare && unbuild",
|
|
62
69
|
"dev": "unbuild --stub",
|
|
63
70
|
"lint": "biome check .",
|
|
64
71
|
"lint:fix": "biome check . --write && pnpm run typecheck",
|
|
65
|
-
"typecheck": "tsc --noEmit",
|
|
72
|
+
"typecheck": "pnpm nuxt prepare && tsc --noEmit",
|
|
66
73
|
"test": "vitest run",
|
|
67
|
-
"test:watch": "vitest"
|
|
74
|
+
"test:watch": "vitest",
|
|
75
|
+
"check-colors": "tsx scripts/check-unused-colors.ts"
|
|
68
76
|
}
|
|
69
77
|
}
|
package/uno.config.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineConfig,
|
|
3
|
+
presetAttributify,
|
|
4
|
+
presetIcons,
|
|
5
|
+
presetTypography,
|
|
6
|
+
presetWind3,
|
|
7
|
+
} from "unocss";
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
shortcuts: {},
|
|
11
|
+
preflights: [
|
|
12
|
+
{
|
|
13
|
+
getCSS: () => `
|
|
14
|
+
/* Filter collapse transition */
|
|
15
|
+
.filter-collapse-enter-active,
|
|
16
|
+
.filter-collapse-leave-active {
|
|
17
|
+
transition: all 0.3s ease-in-out;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.filter-collapse-enter-from,
|
|
22
|
+
.filter-collapse-leave-to {
|
|
23
|
+
max-height: 0;
|
|
24
|
+
opacity: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.filter-collapse-enter-to,
|
|
28
|
+
.filter-collapse-leave-from {
|
|
29
|
+
max-height: 1000px;
|
|
30
|
+
opacity: 1;
|
|
31
|
+
}
|
|
32
|
+
`,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
theme: {
|
|
36
|
+
colors: {
|
|
37
|
+
"brand-primary": "#543B95",
|
|
38
|
+
"surface-surface": "#FFFFFF",
|
|
39
|
+
"outline-outline": "#79747E",
|
|
40
|
+
"outline-outline-variant": "#CAC4D0",
|
|
41
|
+
"outline-outline-primary": "#543B95",
|
|
42
|
+
"surface-on-surface": "#1D1B20",
|
|
43
|
+
"surface-surface-variant": "#FBF6FF",
|
|
44
|
+
"states-success": "#15B31C",
|
|
45
|
+
"surface-on-surface-variant": "#696470",
|
|
46
|
+
"surface-surface-disabled": "#E8E8E8",
|
|
47
|
+
"surface-on-surface-disabled": "#9893A6",
|
|
48
|
+
"surface-surface-primary": "#D0BCFF",
|
|
49
|
+
"states-warning": "#F57C00",
|
|
50
|
+
"surface-surface-container": "#F3EDF7",
|
|
51
|
+
"surface-surface-container-highest": "#E6E0E9",
|
|
52
|
+
"states-error": "#D12D24",
|
|
53
|
+
"states-on-error": "#FFFFFF",
|
|
54
|
+
"brand-primary-hover": "#45317A",
|
|
55
|
+
"brand-on-primary": "#FFFFFF",
|
|
56
|
+
"brand-secondary": "#E1D5FF",
|
|
57
|
+
"brand-secondary-hover": "#D0BCFC",
|
|
58
|
+
"brand-on-secondary": "#3A276A",
|
|
59
|
+
"brand-tertiary": "#F1F1F1",
|
|
60
|
+
"brand-tertiary-hover": "#E3E3E3",
|
|
61
|
+
"brand-on-tertiary": "#1D1B20",
|
|
62
|
+
"other-sale": "#D12D24",
|
|
63
|
+
},
|
|
64
|
+
fontFamily: {
|
|
65
|
+
inter: "Inter",
|
|
66
|
+
Noto_Serif: "Noto Serif",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
safelist: ["states-success", "states-error", "states-info", "states-warning"],
|
|
70
|
+
presets: [
|
|
71
|
+
presetWind3(),
|
|
72
|
+
presetIcons({
|
|
73
|
+
collections: {
|
|
74
|
+
carbon: () =>
|
|
75
|
+
import("@iconify-json/carbon/icons.json", {
|
|
76
|
+
with: { type: "json" },
|
|
77
|
+
}).then((i) => i.default),
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
presetAttributify(),
|
|
81
|
+
presetTypography(),
|
|
82
|
+
],
|
|
83
|
+
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import type { Schemas } from "#shopware";
|
|
3
|
-
|
|
4
|
-
const props = withDefaults(
|
|
5
|
-
defineProps<{
|
|
6
|
-
activeCategory: Schemas["Category"];
|
|
7
|
-
elements: Schemas["Category"][];
|
|
8
|
-
level: number;
|
|
9
|
-
}>(),
|
|
10
|
-
{
|
|
11
|
-
level: 0,
|
|
12
|
-
},
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
function getHighlightCategory(navigationElement: Schemas["Category"]) {
|
|
16
|
-
return (
|
|
17
|
-
(props.activeCategory?.path || "").includes(navigationElement.id) ||
|
|
18
|
-
navigationElement.id === props.activeCategory?.id
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
</script>
|
|
22
|
-
<template>
|
|
23
|
-
<ul v-if="props.elements?.length" class="list-none m-0 px-5">
|
|
24
|
-
<li
|
|
25
|
-
v-for="(navigationElement, index) in props.elements"
|
|
26
|
-
:key="index"
|
|
27
|
-
:class="{
|
|
28
|
-
'border-b border-gray-200': props.level === 0,
|
|
29
|
-
}"
|
|
30
|
-
>
|
|
31
|
-
<SwCategoryNavigationLink
|
|
32
|
-
:navigation-element="navigationElement"
|
|
33
|
-
:is-highlighted="getHighlightCategory(navigationElement)"
|
|
34
|
-
:is-active="navigationElement.id === props.activeCategory?.id"
|
|
35
|
-
/>
|
|
36
|
-
<SwCategoryNavigation
|
|
37
|
-
v-if="navigationElement.children"
|
|
38
|
-
:elements="navigationElement.children"
|
|
39
|
-
:active-category="props.activeCategory"
|
|
40
|
-
:level="props.level + 1"
|
|
41
|
-
/>
|
|
42
|
-
</li>
|
|
43
|
-
</ul>
|
|
44
|
-
</template>
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
buildUrlPrefix,
|
|
4
|
-
getCategoryRoute,
|
|
5
|
-
getTranslatedProperty,
|
|
6
|
-
urlIsAbsolute,
|
|
7
|
-
} from "@shopware/helpers";
|
|
8
|
-
import { computed } from "vue";
|
|
9
|
-
import { RouterLink } from "vue-router";
|
|
10
|
-
import { useUrlResolver } from "#imports";
|
|
11
|
-
import type { Schemas } from "#shopware";
|
|
12
|
-
|
|
13
|
-
interface Props {
|
|
14
|
-
navigationElement: Schemas["Category"];
|
|
15
|
-
isActive?: boolean;
|
|
16
|
-
isHighlighted?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const props = defineProps<Props>();
|
|
20
|
-
const { getUrlPrefix } = useUrlResolver();
|
|
21
|
-
const url = computed(() => {
|
|
22
|
-
return buildUrlPrefix(
|
|
23
|
-
getCategoryRoute(props.navigationElement),
|
|
24
|
-
getUrlPrefix(),
|
|
25
|
-
);
|
|
26
|
-
});
|
|
27
|
-
</script>
|
|
28
|
-
<template>
|
|
29
|
-
<div
|
|
30
|
-
class="flex items-center py-2 px-5 text-base rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 my-2"
|
|
31
|
-
>
|
|
32
|
-
<RouterLink
|
|
33
|
-
v-if="!urlIsAbsolute(url.path)"
|
|
34
|
-
:to="url"
|
|
35
|
-
:class="[
|
|
36
|
-
props.isHighlighted ? 'font-bold' : 'font-normal',
|
|
37
|
-
props.isActive ? 'text-indigo-600' : 'text-gray-900',
|
|
38
|
-
]"
|
|
39
|
-
>
|
|
40
|
-
<span>{{ getTranslatedProperty(navigationElement, "name") }}</span>
|
|
41
|
-
</RouterLink>
|
|
42
|
-
<a
|
|
43
|
-
v-else
|
|
44
|
-
:href="url.path"
|
|
45
|
-
:class="[
|
|
46
|
-
props.isHighlighted ? 'font-bold' : 'font-normal',
|
|
47
|
-
props.isActive ? 'text-indigo-600' : 'text-gray-900',
|
|
48
|
-
]"
|
|
49
|
-
:target="
|
|
50
|
-
navigationElement.externalLink || navigationElement.linkNewTab
|
|
51
|
-
? '_blank'
|
|
52
|
-
: ''
|
|
53
|
-
"
|
|
54
|
-
><span>{{ getTranslatedProperty(navigationElement, "name") }}</span></a
|
|
55
|
-
>
|
|
56
|
-
</div>
|
|
57
|
-
</template>
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { useCmsTranslations, useProductPrice } from "@shopware/composables";
|
|
3
|
-
import { defu } from "defu";
|
|
4
|
-
import { toRefs } from "vue";
|
|
5
|
-
import type { Schemas } from "#shopware";
|
|
6
|
-
|
|
7
|
-
const props = defineProps<{
|
|
8
|
-
product: Schemas["Product"];
|
|
9
|
-
}>();
|
|
10
|
-
|
|
11
|
-
type Translations = {
|
|
12
|
-
listing: {
|
|
13
|
-
variantsFrom: string;
|
|
14
|
-
previously: string;
|
|
15
|
-
from: string;
|
|
16
|
-
to: string;
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
let translations: Translations = {
|
|
21
|
-
listing: {
|
|
22
|
-
variantsFrom: "variants from",
|
|
23
|
-
previously: "previously",
|
|
24
|
-
from: "from",
|
|
25
|
-
to: "to",
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
translations = defu(useCmsTranslations(), translations) as Translations;
|
|
30
|
-
|
|
31
|
-
const { product } = toRefs(props);
|
|
32
|
-
|
|
33
|
-
const {
|
|
34
|
-
price,
|
|
35
|
-
unitPrice,
|
|
36
|
-
displayFromVariants,
|
|
37
|
-
displayFrom,
|
|
38
|
-
isListPrice,
|
|
39
|
-
regulationPrice,
|
|
40
|
-
} = useProductPrice(product);
|
|
41
|
-
</script>
|
|
42
|
-
|
|
43
|
-
<template>
|
|
44
|
-
<div :id="product.id">
|
|
45
|
-
<SwSharedPrice
|
|
46
|
-
v-if="isListPrice"
|
|
47
|
-
class="text-l text-gray-900 basis-2/6 justify-end line-through"
|
|
48
|
-
:value="price?.listPrice?.price"
|
|
49
|
-
/>
|
|
50
|
-
<template v-if="!isListPrice">
|
|
51
|
-
<div class="h-6"><!-- placeholder --></div>
|
|
52
|
-
</template>
|
|
53
|
-
<SwSharedPrice
|
|
54
|
-
v-if="displayFromVariants"
|
|
55
|
-
class="text-xl text-gray-900 basis-2/6 justify-end"
|
|
56
|
-
:value="displayFromVariants"
|
|
57
|
-
>
|
|
58
|
-
<template #beforePrice
|
|
59
|
-
><span v-if="displayFromVariants" class="text-sm">{{
|
|
60
|
-
translations.listing.variantsFrom
|
|
61
|
-
}}</span></template
|
|
62
|
-
>
|
|
63
|
-
</SwSharedPrice>
|
|
64
|
-
<SwSharedPrice
|
|
65
|
-
class="text-gray-900 basis-2/6"
|
|
66
|
-
:class="{
|
|
67
|
-
'text-red-600 font-bold': isListPrice,
|
|
68
|
-
'justify-end text-xl':
|
|
69
|
-
regulationPrice || !regulationPrice || !displayFromVariants,
|
|
70
|
-
}"
|
|
71
|
-
:value="unitPrice"
|
|
72
|
-
>
|
|
73
|
-
<template #beforePrice
|
|
74
|
-
><span v-if="displayFrom || displayFromVariants" class="text-sm">{{
|
|
75
|
-
translations.listing.from
|
|
76
|
-
}}</span></template
|
|
77
|
-
>
|
|
78
|
-
</SwSharedPrice>
|
|
79
|
-
<template v-if="regulationPrice">
|
|
80
|
-
<div class="flex gap-2 justify-end text-gray-500 text-3.5 mb-2">
|
|
81
|
-
{{ translations.listing.previously }}
|
|
82
|
-
<SwSharedPrice :value="regulationPrice" />
|
|
83
|
-
</div>
|
|
84
|
-
</template>
|
|
85
|
-
<template v-if="!regulationPrice">
|
|
86
|
-
<div class="h-7"><!-- placeholder --></div>
|
|
87
|
-
</template>
|
|
88
|
-
</div>
|
|
89
|
-
</template>
|