@shopware/cms-base-layer 3.0.0 → 3.1.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.
Files changed (107) hide show
  1. package/README.md +64 -58
  2. package/app/components/SwCategoryNavigation.vue +1 -0
  3. package/app/components/SwCategoryNavigationLink.vue +16 -8
  4. package/app/components/SwContactForm.vue +1 -0
  5. package/app/components/SwFilterChips.vue +1 -0
  6. package/app/components/SwListingProductPrice.vue +16 -4
  7. package/app/components/SwMedia3D.vue +1 -4
  8. package/app/components/SwNewsletterForm.vue +4 -3
  9. package/app/components/SwProductAddToCart.vue +24 -7
  10. package/app/components/SwProductCard.vue +20 -3
  11. package/app/components/SwProductCardDetails.vue +27 -9
  12. package/app/components/SwProductCardImage.vue +31 -12
  13. package/app/components/SwProductCardSkeleton.vue +37 -14
  14. package/app/components/SwProductGallery.vue +1 -0
  15. package/app/components/SwProductListingFilter.vue +5 -1
  16. package/app/components/SwProductListingFilters.vue +57 -76
  17. package/app/components/SwProductListingFiltersHorizontal.vue +40 -71
  18. package/app/components/SwProductPrice.vue +1 -0
  19. package/app/components/SwProductReviews.vue +14 -6
  20. package/app/components/SwProductReviewsForm.vue +15 -3
  21. package/app/components/SwProductUnits.vue +1 -0
  22. package/app/components/SwQuantitySelect.vue +10 -3
  23. package/app/components/SwSharedPrice.vue +1 -0
  24. package/app/components/SwSlider.vue +1 -0
  25. package/app/components/SwStockInfo.vue +12 -6
  26. package/app/components/SwVariantConfigurator.vue +4 -1
  27. package/app/components/listing-filters/SwFilterPrice.vue +79 -30
  28. package/app/components/listing-filters/SwFilterProperties.vue +40 -25
  29. package/app/components/listing-filters/SwFilterRating.vue +21 -9
  30. package/app/components/listing-filters/SwFilterShippingFree.vue +16 -8
  31. package/app/components/public/cms/CmsBlockSpatialViewer.vue +1 -0
  32. package/app/components/public/cms/CmsGenericBlock.vue +3 -1
  33. package/app/components/public/cms/CmsGenericElement.vue +2 -1
  34. package/app/components/public/cms/CmsNoComponent.vue +2 -1
  35. package/app/components/public/cms/CmsPage.md +1 -1
  36. package/app/components/public/cms/CmsPage.vue +2 -0
  37. package/app/components/public/cms/block/CmsBlockCategoryNavigation.vue +1 -0
  38. package/app/components/public/cms/block/CmsBlockCenterText.vue +3 -1
  39. package/app/components/public/cms/block/CmsBlockCrossSelling.vue +1 -0
  40. package/app/components/public/cms/block/CmsBlockCustomForm.vue +1 -0
  41. package/app/components/public/cms/block/CmsBlockForm.vue +1 -0
  42. package/app/components/public/cms/block/CmsBlockGalleryBuybox.vue +4 -1
  43. package/app/components/public/cms/block/CmsBlockImage.vue +1 -0
  44. package/app/components/public/cms/block/CmsBlockImageBubbleRow.vue +9 -2
  45. package/app/components/public/cms/block/CmsBlockImageCover.vue +1 -0
  46. package/app/components/public/cms/block/CmsBlockImageFourColumn.vue +10 -3
  47. package/app/components/public/cms/block/CmsBlockImageGallery.vue +1 -0
  48. package/app/components/public/cms/block/CmsBlockImageGalleryBig.vue +10 -5
  49. package/app/components/public/cms/block/CmsBlockImageHighlightRow.vue +11 -3
  50. package/app/components/public/cms/block/CmsBlockImageSimpleGrid.vue +1 -0
  51. package/app/components/public/cms/block/CmsBlockImageSlider.vue +1 -0
  52. package/app/components/public/cms/block/CmsBlockImageText.vue +4 -1
  53. package/app/components/public/cms/block/CmsBlockImageTextBubble.vue +4 -1
  54. package/app/components/public/cms/block/CmsBlockImageTextCover.vue +4 -1
  55. package/app/components/public/cms/block/CmsBlockImageTextGallery.vue +4 -12
  56. package/app/components/public/cms/block/CmsBlockImageTextRow.vue +13 -4
  57. package/app/components/public/cms/block/CmsBlockImageThreeColumn.vue +1 -0
  58. package/app/components/public/cms/block/CmsBlockImageThreeCover.vue +11 -3
  59. package/app/components/public/cms/block/CmsBlockImageTwoColumn.vue +11 -4
  60. package/app/components/public/cms/block/CmsBlockProductDescriptionReviews.vue +1 -0
  61. package/app/components/public/cms/block/CmsBlockProductHeading.vue +3 -1
  62. package/app/components/public/cms/block/CmsBlockProductListing.vue +1 -0
  63. package/app/components/public/cms/block/CmsBlockProductThreeColumn.vue +1 -0
  64. package/app/components/public/cms/block/CmsBlockSidebarFilter.vue +1 -0
  65. package/app/components/public/cms/block/CmsBlockText.vue +1 -0
  66. package/app/components/public/cms/block/CmsBlockTextHero.vue +1 -0
  67. package/app/components/public/cms/block/CmsBlockTextOnImage.vue +2 -4
  68. package/app/components/public/cms/block/CmsBlockTextThreeColumn.vue +1 -0
  69. package/app/components/public/cms/block/CmsBlockTextTwoColumn.vue +4 -1
  70. package/app/components/public/cms/block/CmsBlockVimeoVideo.vue +1 -0
  71. package/app/components/public/cms/block/CmsBlockYoutubeVideo.vue +1 -0
  72. package/app/components/public/cms/element/CmsElementBuyBox.vue +90 -72
  73. package/app/components/public/cms/element/CmsElementCategoryNavigation.vue +14 -3
  74. package/app/components/public/cms/element/CmsElementCrossSelling.vue +3 -1
  75. package/app/components/public/cms/element/CmsElementCustomForm.vue +1 -0
  76. package/app/components/public/cms/element/CmsElementForm.vue +1 -0
  77. package/app/components/public/cms/element/CmsElementHtml.vue +0 -1
  78. package/app/components/public/cms/element/CmsElementImage.vue +2 -1
  79. package/app/components/public/cms/element/CmsElementImageGallery.vue +6 -2
  80. package/app/components/public/cms/element/CmsElementManufacturerLogo.vue +1 -0
  81. package/app/components/public/cms/element/CmsElementProductBox.vue +1 -0
  82. package/app/components/public/cms/element/CmsElementProductDescriptionReviews.vue +93 -36
  83. package/app/components/public/cms/element/CmsElementProductListing.vue +16 -5
  84. package/app/components/public/cms/element/CmsElementProductSlider.vue +6 -4
  85. package/app/components/public/cms/element/CmsElementText.vue +2 -0
  86. package/app/components/public/cms/element/CmsElementVimeoVideo.vue +1 -0
  87. package/app/components/public/cms/element/CmsElementYoutubeVideo.vue +1 -0
  88. package/app/components/public/cms/element/SwProductListingPagination.vue +8 -2
  89. package/app/components/public/cms/section/CmsSectionSidebar.vue +13 -8
  90. package/app/components/public/cms/skeleton/ProductCardSkeleton.vue +16 -5
  91. package/app/components/ui/SwitchButton.vue +53 -14
  92. package/app/components/ui/WishlistIcon.vue +8 -2
  93. package/app/composables/useLcpImagePreload.test.ts +1 -0
  94. package/app/composables/useLcpImagePreload.ts +2 -0
  95. package/app/composables/useTypedAppConfig.ts +1 -0
  96. package/app/helpers/cms/getImageSizes.test.ts +1 -0
  97. package/app/helpers/html-to-vue/ast.ts +1 -0
  98. package/app/helpers/html-to-vue/getOptionsFromNode.test.ts +1 -0
  99. package/app/helpers/html-to-vue/renderToHtml.ts +1 -0
  100. package/app/helpers/html-to-vue/renderer.ts +1 -0
  101. package/app/providers/shopware.test.ts +1 -0
  102. package/app/utils/routeQuery.test.ts +65 -0
  103. package/app/utils/routeQuery.ts +26 -0
  104. package/app/utils/useSelectedListingFilters.test.ts +82 -0
  105. package/app/utils/useSelectedListingFilters.ts +105 -0
  106. package/nuxt.config.ts +24 -1
  107. package/package.json +22 -24
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![](https://img.shields.io/github/issues/shopware/frontends/cms-base?label=cms-base%20issues&logo=github)](https://github.com/shopware/frontends/issues?q=is%3Aopen+is%3Aissue+label%3Acms-base)
6
6
  [![](https://img.shields.io/github/license/shopware/frontends?color=blue)](#)
7
7
 
8
- Nuxt [layer](https://nuxt.com/docs/getting-started/layers) that provides an implementation of all CMS components in Shopware [based on utility-classes](https://frontends.shopware.com/framework/styling.html).
8
+ Nuxt [layer](https://nuxt.com/docs/getting-started/layers) that provides an implementation of all CMS components in Shopware [based on utility-classes](https://developer.shopware.com/frontends/concepts/styling.html).
9
9
 
10
10
  It is useful for projects that want to use the CMS components while keeping CMS functionality separate from the styling system and design tokens.
11
11
 
@@ -90,7 +90,7 @@ Since all CMS components are registered in your Nuxt application, you can now st
90
90
 
91
91
  > `@shopware/cms-base-layer` no longer owns the default UnoCSS theme. If you want the shared Shopware Frontends design tokens and UnoCSS defaults, extend `@shopware/unocss-design-tokens-layer` as shown above.
92
92
 
93
- See a [short guide](https://frontends.shopware.com/getting-started/cms/content-pages.html#use-the-cms-base-package) on how to use `cms-base-layer` in your Nuxt project.
93
+ See a [short guide](https://developer.shopware.com/frontends/guides/cms/content-pages.html#use-the-cms-base-package) on how to use `cms-base-layer` in your Nuxt project.
94
94
 
95
95
  ## Styling and Design Tokens
96
96
 
@@ -110,22 +110,24 @@ export default defineNuxtConfig({
110
110
  unocss: {
111
111
  nuxtLayers: true, // enable Nuxt layers for UnoCSS
112
112
  },
113
- })
113
+ });
114
114
  ```
115
115
 
116
116
  ```ts [uno.config.ts]
117
- import { mergeConfigs } from '@unocss/core'
118
- import baseConfig from './.nuxt/uno.config.mjs'
119
-
120
- export default mergeConfigs([baseConfig, {
121
- theme: {
122
- colors: {
123
- 'brand-primary': '#ff3e00',
124
- 'brand-secondary': '#1c1c1c',
117
+ import { mergeConfigs } from "@unocss/core";
118
+ import baseConfig from "./.nuxt/uno.config.mjs";
119
+
120
+ export default mergeConfigs([
121
+ baseConfig,
122
+ {
123
+ theme: {
124
+ colors: {
125
+ "brand-primary": "#ff3e00",
126
+ "brand-secondary": "#1c1c1c",
127
+ },
125
128
  },
126
129
  },
127
- }])
128
-
130
+ ]);
129
131
  ```
130
132
 
131
133
  See the [UnoCSS reference](https://unocss.dev/integrations/nuxt#configuration) for more information on how to configure UnoCSS in Nuxt when work with layers.
@@ -150,12 +152,14 @@ This layer includes [Nuxt Image](https://image.nuxt.com/) configuration optimize
150
152
  The layer comes pre-configured with optimized settings. No additional setup is required! The configuration includes:
151
153
 
152
154
  **Available Presets:**
155
+
153
156
  - `productCard` - Product listing images (WebP, quality 90, cover fit)
154
157
  - `productDetail` - Product detail page images (WebP, quality 90, contain fit)
155
158
  - `thumbnail` - Small thumbnails (150x150, WebP, quality 90)
156
159
  - `hero` - Hero banners (WebP, quality 95, cover fit)
157
160
 
158
161
  **Responsive Breakpoints:**
162
+
159
163
  - `xs: 320px`, `sm: 640px`, `md: 768px`, `lg: 1024px`, `xl: 1280px`, `xxl: 1536px`
160
164
 
161
165
  ### Usage in Components
@@ -196,21 +200,23 @@ Replace standard `<img>` tags with `<NuxtImg>` to enable automatic optimization:
196
200
 
197
201
  Shopware supports the following URL parameters for image transformation:
198
202
 
199
- | Modifier | Description | Example | Support |
200
- |----------|-------------|---------|---------|
201
- | `width` | Image width in pixels | `400` | ✅ Always supported |
202
- | `height` | Image height in pixels | `600` | ✅ Always supported |
203
- | `quality` | Image quality (0-100) | `85` | ⚠️ Cloud/Plugin required* |
204
- | `format` | Output format | `webp`, `avif`, `jpg`, `png` | ⚠️ Cloud/Plugin required* |
205
- | `fit` | Resize behavior | `cover`, `contain`, `fill` | ⚠️ Cloud/Plugin required* |
203
+ | Modifier | Description | Example | Support |
204
+ | --------- | ---------------------- | ---------------------------- | -------------------------- |
205
+ | `width` | Image width in pixels | `400` | ✅ Always supported |
206
+ | `height` | Image height in pixels | `600` | ✅ Always supported |
207
+ | `quality` | Image quality (0-100) | `85` | ⚠️ Cloud/Plugin required\* |
208
+ | `format` | Output format | `webp`, `avif`, `jpg`, `png` | ⚠️ Cloud/Plugin required\* |
209
+ | `fit` | Resize behavior | `cover`, `contain`, `fill` | ⚠️ Cloud/Plugin required\* |
210
+
211
+ \*Advanced transformations (quality, format, fit) are available in:
206
212
 
207
- *Advanced transformations (quality, format, fit) are available in:
208
213
  - **Shopware Cloud (SaaS)**: Built-in support via managed infrastructure. For a complete list of supported image transformation parameters, see [Fastly Image Optimizer Query Parameters](https://www.fastly.com/documentation/reference/io/#query-parameters).
209
214
  - **Self-hosted instances**: Require thumbnail processor plugins like [FroshPlatformThumbnailProcessor](https://github.com/FriendsOfShopware/FroshPlatformThumbnailProcessor) or third-party CDN integration
210
215
 
211
216
  ### How It Works
212
217
 
213
218
  This layer includes a custom Shopware provider for Nuxt Image that maps modifiers to Shopware's query parameters:
219
+
214
220
  - `width` modifier → `?width=400`
215
221
  - `height` modifier → `?height=300`
216
222
  - `quality` modifier → `?quality=85`
@@ -249,31 +255,31 @@ export default defineNuxtConfig({
249
255
  quality: 85,
250
256
 
251
257
  // Add/change formats
252
- formats: ['avif', 'webp', 'jpg'],
258
+ formats: ["avif", "webp", "jpg"],
253
259
 
254
260
  // Override or add presets
255
261
  presets: {
256
262
  // Override existing preset
257
263
  productCard: {
258
264
  modifiers: {
259
- format: 'avif',
265
+ format: "avif",
260
266
  quality: 80,
261
- fit: 'cover',
262
- }
267
+ fit: "cover",
268
+ },
263
269
  },
264
270
  // Add custom preset
265
271
  categoryBanner: {
266
272
  modifiers: {
267
- format: 'webp',
273
+ format: "webp",
268
274
  quality: 90,
269
275
  width: 1200,
270
276
  height: 400,
271
- fit: 'cover',
272
- }
273
- }
274
- }
275
- }
276
- })
277
+ fit: "cover",
278
+ },
279
+ },
280
+ },
281
+ },
282
+ });
277
283
  ```
278
284
 
279
285
  ## 🖼️ Image Placeholder
@@ -335,6 +341,7 @@ When a CMS section or block has a `backgroundMedia` set, the components call `ge
335
341
  4. Appends `format` and `quality` from `app.config.ts` if provided
336
342
 
337
343
  **Example generated URL:**
344
+
338
345
  ```
339
346
  url("https://cdn.shopware.store/.../image.jpg?width=1000&fit=crop,smart&format=webp&quality=85")
340
347
  ```
@@ -348,6 +355,7 @@ This layer includes a `useLcpImagePreload` composable that automatically preload
348
355
  ### How it works
349
356
 
350
357
  The composable scans CMS sections in document order, checking:
358
+
351
359
  1. Section background images (`section.backgroundMedia`)
352
360
  2. Block background images (`block.backgroundMedia`)
353
361
  3. Image element media (`slot.data.media`)
@@ -386,11 +394,14 @@ productCard: {
386
394
  ```
387
395
 
388
396
  ```vue
389
- <NuxtImg preset="productCard"
397
+ <NuxtImg
398
+ preset="productCard"
390
399
  :src="coverSrcPath"
391
- width="400" height="400"
400
+ width="400"
401
+ height="400"
392
402
  densities="1x"
393
- loading="lazy" />
403
+ loading="lazy"
404
+ />
394
405
  ```
395
406
 
396
407
  - **Fixed `width`/`height`** (400px) — avoid hydration mismatches caused by dynamic DOM measurement
@@ -431,13 +442,13 @@ export default defineAppConfig({
431
442
 
432
443
  ## 📘 Available components
433
444
 
434
- The list of available blocks and elements is [here](https://frontends.shopware.com/packages/cms-base-layer.html#available-components).
445
+ The list of available blocks and elements is [here](https://developer.shopware.com/frontends/packages/cms-base-layer.html#available-components).
435
446
 
436
447
  ## 🔄 Overwriting components
437
448
 
438
449
  The procedure is:
439
450
 
440
- - find a component in component's [list](https://frontends.shopware.com/packages/cms-base.html#available-components), using a [Vue devtools](https://devtools.vuejs.org/) or browsing the github [repository](https://github.com/shopware/frontends/tree/main/packages/cms-base-layer/app/components)
451
+ - find a component in component's [list](https://developer.shopware.com/frontends/packages/cms-base.html#available-components), using a [Vue devtools](https://devtools.vuejs.org/) or browsing the github [repository](https://github.com/shopware/frontends/tree/main/packages/cms-base-layer/app/components)
441
452
  - take its name
442
453
  - create a file with the same name and place it into `~/components` dir in your nuxt project (or wherever according your nuxt config)
443
454
 
@@ -464,7 +475,7 @@ No additional packages needed to be installed.
464
475
 
465
476
  ## Links
466
477
 
467
- - [📘 Documentation](https://frontends.shopware.com)
478
+ - [📘 Documentation](https://developer.shopware.com/frontends)
468
479
 
469
480
  - [👥 Community](https://discord.com/channels/1308047705309708348/1405501315160739951) (`#composable-frontend`)
470
481
 
@@ -474,36 +485,31 @@ No additional packages needed to be installed.
474
485
 
475
486
  Full changelog for stable version is available [here](https://github.com/shopware/frontends/blob/main/packages/cms-base-layer/CHANGELOG.md)
476
487
 
477
- ### Latest changes: 3.0.0
478
-
479
- ### Major Changes
480
-
481
- - [#2406](https://github.com/shopware/frontends/pull/2406) [`df93461`](https://github.com/shopware/frontends/commit/df93461434cb79ec9d722cdbd42a37a9af07fb03) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Remove bundled UnoCSS configuration and design tokens from the CMS layer. Consumers who relied on the previous default UnoCSS setup should extend `@shopware/unocss-design-tokens-layer` alongside this package. See the package README and framework docs for migration steps.
488
+ ### Latest changes: 3.1.0
482
489
 
483
490
  ### Minor Changes
484
491
 
485
- - [#2420](https://github.com/shopware/frontends/pull/2420) [`9e37ab6`](https://github.com/shopware/frontends/commit/9e37ab6897f501ed3d261fa619aee349e46342c2) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Add FrontendAccountCustomerGroupRegistrationPage component for customer group view
492
+ - [#2503](https://github.com/shopware/frontends/pull/2503) [`1b0ccc9`](https://github.com/shopware/frontends/commit/1b0ccc95fcef89de42931155fb40119b9075d21b) Thanks [@mkucmus](https://github.com/mkucmus)! - `SwProductListingFilters` and `SwProductListingFiltersHorizontal`: honor the `listingType` prop. When set to `productSearchListing` the components drive `useProductSearchListing` instead of `useCategoryListing` and keep the active search term in the request criteria and resulting URL, so the shared filter sidebar can be reused on the product search page without resetting the query. Category listing behavior is unchanged.
486
493
 
487
494
  ### Patch Changes
488
495
 
489
- - [#2439](https://github.com/shopware/frontends/pull/2439) [`a992e39`](https://github.com/shopware/frontends/commit/a992e396ea6aa3b44783d183561fa8b0605b77f0) Thanks [@patzick](https://github.com/patzick)! - Use Nuxt route composables in CMS components to keep route query access available during SSR.
490
-
491
- - [#2389](https://github.com/shopware/frontends/pull/2389) [`05438c6`](https://github.com/shopware/frontends/commit/05438c636a6c99b48e87d8f2ff5b03bf313c4e67) Thanks [@mkucmus](https://github.com/mkucmus)! - Fix product card and CMS image sizing to prevent duplicate/oversized image requests. Move fixed dimensions and `densities="1x"` into the `productCard` preset, and use `useElementSize`-based `width`/`height` props for `CmsElementImage`.
496
+ - [#2593](https://github.com/shopware/frontends/pull/2593) [`760635c`](https://github.com/shopware/frontends/commit/760635cdbaf3c80ae99fbe0f42a31a8ebaa14675) Thanks [@patzick](https://github.com/patzick)! - Stop registering the `@unocss/nuxt` module from the CMS layer. UnoCSS is not a dependency of this package anymore (it lives in `@shopware/unocss-design-tokens-layer`), so registering the module made every app that doesn't install UnoCSS itself fail to build with `Could not load @unocss/nuxt. Is it installed?` - for example the blank template. Apps that want the shared UnoCSS setup keep getting it from `@shopware/unocss-design-tokens-layer` or their own `@unocss/nuxt` registration.
492
497
 
493
- - [#2378](https://github.com/shopware/frontends/pull/2378) [`c36bc1f`](https://github.com/shopware/frontends/commit/c36bc1ff17e8e34c52fa91e6388ce210fffb7e8e) Thanks [@patzick](https://github.com/patzick)! - Add UnoCSS directive transformation for CMS block styles so layered Nuxt apps do not emit CSS minification warnings from raw `@apply` directives during production builds.
498
+ - [#2568](https://github.com/shopware/frontends/pull/2568) [`6315350`](https://github.com/shopware/frontends/commit/6315350add0464abef153343897d42f5808f2003) Thanks [@patzick](https://github.com/patzick)! - Add cacheable GET support for category details and product reviews while preserving the existing POST behavior when `cacheableReads` is disabled.
494
499
 
495
- - [#2369](https://github.com/shopware/frontends/pull/2369) [`3c16985`](https://github.com/shopware/frontends/commit/3c16985ddf3878bc207c514a5ab8e4a6409f809c) Thanks [@mkucmus](https://github.com/mkucmus)! - Fixed `xss` library loading issue in Vite dev server by adding it to `optimizeDeps.include`
500
+ - `useCategorySearch.search` now calls `GET /category/{navigationId}` and sends the complete encoded Criteria in the `_criteria` query parameter when cacheable reads are enabled.
501
+ - `useProductReviews.loadProductReviews` now calls `GET /product/{productId}/reviews` and sends its encoded Criteria in `_criteria` when cacheable reads are enabled.
502
+ - The CMS product description reviews element follows the same flag and endpoint behavior when it needs to fetch reviews directly.
496
503
 
497
- - [#2371](https://github.com/shopware/frontends/pull/2371) [`33e0c69`](https://github.com/shopware/frontends/commit/33e0c69afc3de854733ab61f866ba65cce1489f6) Thanks [@patzick](https://github.com/patzick)! - Disable automatic CMS LCP image preload by default.
504
+ - [#2571](https://github.com/shopware/frontends/pull/2571) [`c64ae1c`](https://github.com/shopware/frontends/commit/c64ae1c6e2d11e33bd52df4b55177cc26b646433) Thanks [@patzick](https://github.com/patzick)! - Show the category a shopper came from in product breadcrumbs, while direct product links continue to use the default category.
498
505
 
499
- The preload helper now only injects image preload tags when
500
- `appConfig.lcpImagePreload` is explicitly enabled, which avoids noisy preload
501
- warnings on storefront pages that do not immediately use the detected image.
506
+ - [#2596](https://github.com/shopware/frontends/pull/2596) [`6572aa8`](https://github.com/shopware/frontends/commit/6572aa84431e1f4a34d6cf04e549037692d638a6) Thanks [@patzick](https://github.com/patzick)! - Reduce Rolldown/Vite build noise: scope Nuxt global components, keep the three.js async chunk warning intentional, avoid shipping full carbon icon JSON via UnoCSS runtime, and bump @vueuse to 14.4.0.
502
507
 
503
- - [#2326](https://github.com/shopware/frontends/pull/2326) [`e7efff8`](https://github.com/shopware/frontends/commit/e7efff8c615ae8d0858572933285216cc533dd0b) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Gate wishlist button behind login when useLoginModal is provided via provide/inject. For guests, show login modal on wishlist click and add product to wishlist after successful login.
508
+ - [#2592](https://github.com/shopware/frontends/pull/2592) [`fcc3d71`](https://github.com/shopware/frontends/commit/fcc3d71af05c2f2d409aa63ecb562714e6bc0e69) Thanks [@patzick](https://github.com/patzick)! - Replace CMS layer `@apply` styles with plain CSS declarations to avoid Nuxt build warnings.
504
509
 
505
- - [#2346](https://github.com/shopware/frontends/pull/2346) [`a47143a`](https://github.com/shopware/frontends/commit/a47143a670f49deecc35dce4bb8b6bd12d9a3b47) Thanks [@joberthel](https://github.com/joberthel)! - Improve `SwMedia3D` model framing by fitting loaded 3D models into the viewport automatically. This fixes cases where very small 3D files appeared tiny and hard to inspect.
510
+ - [#2568](https://github.com/shopware/frontends/pull/2568) [`6315350`](https://github.com/shopware/frontends/commit/6315350add0464abef153343897d42f5808f2003) Thanks [@patzick](https://github.com/patzick)! - Preserve CMS image aspect ratios when responsive dimensions are calculated after hydration, preventing visible image scaling as optimized sources load.
506
511
 
507
- - Updated dependencies [[`22fc8a7`](https://github.com/shopware/frontends/commit/22fc8a7301f6a7d2612d907ab73555978b651c00), [`bea7f58`](https://github.com/shopware/frontends/commit/bea7f5882cb58c6d47c84a82db5c8ecaf9bcf8ef), [`b8c0091`](https://github.com/shopware/frontends/commit/b8c00913c3afb5e1e63de9565105f8f8e3bf299f)]:
508
- - @shopware/helpers@1.7.1
509
- - @shopware/composables@1.11.1
512
+ - Updated dependencies [[`6315350`](https://github.com/shopware/frontends/commit/6315350add0464abef153343897d42f5808f2003), [`b767721`](https://github.com/shopware/frontends/commit/b767721847bf3391f9067eca7a045089fb22fce0), [`f16c5a0`](https://github.com/shopware/frontends/commit/f16c5a0785d6187b73c3edcf37feab7c90bd7988), [`978b02c`](https://github.com/shopware/frontends/commit/978b02c969ca4b16f5fc1d7a953ec4cce3d98173), [`6572aa8`](https://github.com/shopware/frontends/commit/6572aa84431e1f4a34d6cf04e549037692d638a6), [`744833b`](https://github.com/shopware/frontends/commit/744833b9d7d2f8ea1f5dfe65be3fa554dbe4a09f), [`e03c91b`](https://github.com/shopware/frontends/commit/e03c91be172374894d90b7a0111855b76719fee1), [`33facb1`](https://github.com/shopware/frontends/commit/33facb178792c8cb26b47ab984ac48c08ab4b72b), [`9137475`](https://github.com/shopware/frontends/commit/91374753cedb2034385f642e6af11314f2971caa), [`474d3fe`](https://github.com/shopware/frontends/commit/474d3fed346816135b0c7c797990b215a8b691c0), [`38379a5`](https://github.com/shopware/frontends/commit/38379a5d52ab09008774533ef417ebe2cde3f7fd)]:
513
+ - @shopware/composables@1.12.1
514
+ - @shopware/api-client@1.5.1
515
+ - @shopware/helpers@1.7.2
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref } from "vue";
3
+
3
4
  import type { Schemas } from "#shopware";
4
5
 
5
6
  const {
@@ -7,6 +7,7 @@ import {
7
7
  } from "@shopware/helpers";
8
8
  import { computed } from "vue";
9
9
  import { RouterLink } from "vue-router";
10
+
10
11
  import { useUrlResolver } from "#imports";
11
12
  import type { Schemas } from "#shopware";
12
13
 
@@ -43,7 +44,9 @@ const hasChildren = computed(() => {
43
44
  v-if="props.level === 0"
44
45
  class="self-stretch flex flex-col justify-center items-center"
45
46
  >
46
- <div class="self-stretch py-3 border-b border-outline-outline-variant inline-flex justify-start items-center gap-1">
47
+ <div
48
+ class="self-stretch py-3 border-b border-outline-outline-variant inline-flex justify-start items-center gap-1"
49
+ >
47
50
  <div class="flex-1 flex justify-start items-center gap-2.5">
48
51
  <RouterLink
49
52
  v-if="!urlIsAbsolute(url.path)"
@@ -76,17 +79,19 @@ const hasChildren = computed(() => {
76
79
  type="button"
77
80
  :aria-label="props.isExpanded ? 'Collapse' : 'Expand'"
78
81
  >
79
- <SwChevronIcon :direction="props.isExpanded ? 'up' : 'down'" :size="20" />
82
+ <SwChevronIcon
83
+ :direction="props.isExpanded ? 'up' : 'down'"
84
+ :size="20"
85
+ />
80
86
  </button>
81
87
  </div>
82
88
  </div>
83
89
 
84
90
  <!-- Level 2+ Categories (Nested with left padding) -->
85
- <div
86
- v-else
87
- class="self-stretch flex flex-col justify-center items-center"
88
- >
89
- <div class="self-stretch pl-4 py-1.5 inline-flex justify-start items-center gap-2">
91
+ <div v-else class="self-stretch flex flex-col justify-center items-center">
92
+ <div
93
+ class="self-stretch pl-4 py-1.5 inline-flex justify-start items-center gap-2"
94
+ >
90
95
  <div class="py-0.5 flex-1 flex justify-start items-center gap-2.5">
91
96
  <RouterLink
92
97
  v-if="!urlIsAbsolute(url.path)"
@@ -121,7 +126,10 @@ const hasChildren = computed(() => {
121
126
  type="button"
122
127
  :aria-label="props.isExpanded ? 'Collapse' : 'Expand'"
123
128
  >
124
- <SwChevronIcon :direction="props.isExpanded ? 'up' : 'down'" :size="20" />
129
+ <SwChevronIcon
130
+ :direction="props.isExpanded ? 'up' : 'down'"
131
+ :size="20"
132
+ />
125
133
  </button>
126
134
  </div>
127
135
  </div>
@@ -7,6 +7,7 @@ import { useVuelidate } from "@vuelidate/core";
7
7
  import { email, minLength, required } from "@vuelidate/validators";
8
8
  import { defu } from "defu";
9
9
  import { computed, reactive, ref } from "vue";
10
+
10
11
  import {
11
12
  useCmsElementConfig,
12
13
  useNavigationContext,
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from "vue";
3
+
3
4
  import type { Schemas } from "#shopware";
4
5
 
5
6
  type FilterState = {
@@ -2,6 +2,7 @@
2
2
  import { useCmsTranslations, useProductPrice } from "@shopware/composables";
3
3
  import { defu } from "defu";
4
4
  import { toRefs } from "vue";
5
+
5
6
  import type { Schemas } from "#shopware";
6
7
 
7
8
  const props = defineProps<{
@@ -53,13 +54,18 @@ const {
53
54
  </template>
54
55
  </SwSharedPrice>
55
56
  </div>
56
- <div class="text-surface-on-surface-variant text-sm font-normal leading-tight line-through">
57
+ <div
58
+ class="text-surface-on-surface-variant text-sm font-normal leading-tight line-through"
59
+ >
57
60
  <SwSharedPrice :value="price?.listPrice?.price" />
58
61
  </div>
59
62
  </div>
60
63
 
61
64
  <!-- Regular price display -->
62
- <div v-else class="text-surface-on-surface text-base font-bold leading-normal">
65
+ <div
66
+ v-else
67
+ class="text-surface-on-surface text-base font-bold leading-normal"
68
+ >
63
69
  <SwSharedPrice :value="unitPrice">
64
70
  <template #beforePrice>
65
71
  <span v-if="displayFrom || displayFromVariants" class="text-sm">{{
@@ -70,7 +76,10 @@ const {
70
76
  </div>
71
77
 
72
78
  <!-- Variants from price -->
73
- <div v-if="displayFromVariants" class="text-surface-on-surface text-base font-bold leading-normal">
79
+ <div
80
+ v-if="displayFromVariants"
81
+ class="text-surface-on-surface text-base font-bold leading-normal"
82
+ >
74
83
  <SwSharedPrice :value="displayFromVariants">
75
84
  <template #beforePrice>
76
85
  <span v-if="displayFromVariants" class="text-sm">{{
@@ -81,7 +90,10 @@ const {
81
90
  </div>
82
91
 
83
92
  <!-- Regulation price -->
84
- <div v-if="regulationPrice" class="flex gap-2 text-surface-on-surface-variant text-sm">
93
+ <div
94
+ v-if="regulationPrice"
95
+ class="flex gap-2 text-surface-on-surface-variant text-sm"
96
+ >
85
97
  {{ translations.listing.previously }}
86
98
  <SwSharedPrice :value="regulationPrice" />
87
99
  </div>
@@ -29,10 +29,7 @@ function focusObject() {
29
29
  </script>
30
30
  <template>
31
31
  <TresCanvas v-bind="gl">
32
- <TresPerspectiveCamera
33
- :args="[75, 1, 0.1, 2000]"
34
- :position="[0, 0, 500]"
35
- />
32
+ <TresPerspectiveCamera :args="[75, 1, 0.1, 2000]" :position="[0, 0, 500]" />
36
33
  <OrbitControls make-default />
37
34
  <Bounds ref="boundsRef" clip use-mounted>
38
35
  <primitive v-if="model" :object="model" @click="focusObject" />
@@ -8,6 +8,7 @@ import type { ValidationRuleWithoutParams } from "@vuelidate/core";
8
8
  import { email, required } from "@vuelidate/validators";
9
9
  import { defu } from "defu";
10
10
  import { computed, reactive, ref } from "vue";
11
+
11
12
  import { useCmsElementConfig, useNewsletter, useSalutations } from "#imports";
12
13
 
13
14
  const props = defineProps<{
@@ -236,7 +237,7 @@ const invokeSubmit = async () => {
236
237
  id="salutation"
237
238
  v-model="state.salutationId"
238
239
  name="salutation"
239
- class=" border-outline-outline-variant focus:border-brand-primaryappearance-none relative block w-full px-3 py-2 border placeholder-surface-on-surface-variant text-surface-on-surface rounded-md focus:outline-none focus:ring-brand-primary focus:z-10 sm:text-sm"
240
+ class="border-outline-outline-variant focus:border-brand-primaryappearance-none relative block w-full px-3 py-2 border placeholder-surface-on-surface-variant text-surface-on-surface rounded-md focus:outline-none focus:ring-brand-primary focus:z-10 sm:text-sm"
240
241
  >
241
242
  <option disabled selected value="">
242
243
  {{ translations.form.salutationPlaceholder }}
@@ -261,8 +262,8 @@ const invokeSubmit = async () => {
261
262
  class="appearance-none relative block w-full px-3 py-2 border placeholder-surface-on-surface-variant text-surface-on-surface rounded-md focus:outline-none focus:ring-brand-primary focus:z-10 sm:text-sm"
262
263
  :class="[
263
264
  $v.firstName?.$error
264
- ? 'border-red-600 focus:border-red-600'
265
- : 'border-outline-outline-variant focus:border-brand-primary',
265
+ ? 'border-red-600 focus:border-red-600'
266
+ : 'border-outline-outline-variant focus:border-brand-primary',
266
267
  ]"
267
268
  :placeholder="translations.form.firstNamePlaceholder"
268
269
  @blur="$v.firstName?.$touch()"
@@ -3,6 +3,7 @@ import { useCmsTranslations } from "@shopware/composables";
3
3
  import { getCmsTranslate } from "@shopware/helpers";
4
4
  import { defu } from "defu";
5
5
  import { computed, toRefs } from "vue";
6
+
6
7
  import {
7
8
  useAddToCart,
8
9
  useCartErrorParamsResolver,
@@ -72,16 +73,32 @@ const addToCartProxy = async () => {
72
73
 
73
74
  <template>
74
75
  <div class="w-full inline-flex flex-col justify-start items-start gap-8">
75
- <SwQuantitySelect v-model="quantity" :min="product.minPurchase" :max="product.maxPurchase"
76
- :steps="product.purchaseSteps" />
77
- <SwStockInfo :availableStock="availableStock" :minPurchase="minPurchase" :deliveryTime="deliveryTime"
78
- :restockTime="restockTime" />
76
+ <SwQuantitySelect
77
+ v-model="quantity"
78
+ :min="product.minPurchase"
79
+ :max="product.maxPurchase"
80
+ :steps="product.purchaseSteps"
81
+ />
82
+ <SwStockInfo
83
+ :availableStock="availableStock"
84
+ :minPurchase="minPurchase"
85
+ :deliveryTime="deliveryTime"
86
+ :restockTime="restockTime"
87
+ />
79
88
  <div class="self-stretch flex flex-col justify-start items-start gap-1">
80
- <SwBaseButton variant="primary" size="medium" :disabled="!product?.available" block data-testid="add-to-cart-button"
81
- @click="addToCartProxy">
89
+ <SwBaseButton
90
+ variant="primary"
91
+ size="medium"
92
+ :disabled="!product?.available"
93
+ block
94
+ data-testid="add-to-cart-button"
95
+ @click="addToCartProxy"
96
+ >
82
97
  {{ translations.product.addToCart }}
83
98
  </SwBaseButton>
84
- <div class="self-stretch text-surface-on-surface text-xs font-normal leading-none">
99
+ <div
100
+ class="self-stretch text-surface-on-surface text-xs font-normal leading-none"
101
+ >
85
102
  {{ translations.product.productNumber }}: {{ productNumber }}
86
103
  </div>
87
104
  </div>
@@ -12,10 +12,12 @@ import {
12
12
  import { getCmsTranslate } from "@shopware/helpers";
13
13
  import { defu } from "defu";
14
14
  import { computed, ref, toRef } from "vue";
15
+
15
16
  import {
16
17
  useAddToCart,
17
18
  useCartErrorParamsResolver,
18
19
  useCartNotification,
20
+ useNavigationContext,
19
21
  useNotifications,
20
22
  useProductWishlist,
21
23
  useUrlResolver,
@@ -143,10 +145,25 @@ const productManufacturer = computed(() =>
143
145
  getProductManufacturerName(product.value),
144
146
  );
145
147
 
146
- const { getUrlPrefix } = useUrlResolver();
147
- const productLink = computed(() =>
148
- buildUrlPrefix(getProductRoute(product.value), getUrlPrefix()),
148
+ const { foreignKey: currentNavigationId, routeName } = useNavigationContext();
149
+ const referrerCategoryId = computed(() =>
150
+ isProductListing && routeName.value === "frontend.navigation.page"
151
+ ? currentNavigationId.value
152
+ : undefined,
149
153
  );
154
+ const { getUrlPrefix } = useUrlResolver();
155
+ const productLink = computed(() => {
156
+ const productRoute = getProductRoute(product.value);
157
+ const productRouteWithReferrer = {
158
+ ...productRoute,
159
+ state: {
160
+ ...productRoute.state,
161
+ referrerCategoryId: referrerCategoryId.value,
162
+ },
163
+ };
164
+
165
+ return buildUrlPrefix(productRouteWithReferrer, getUrlPrefix());
166
+ });
150
167
  </script>
151
168
 
152
169
  <template>
@@ -2,6 +2,7 @@
2
2
  import type { BoxLayout } from "@shopware/composables";
3
3
  import type { UrlRouteOutput } from "@shopware/helpers";
4
4
  import { computed } from "vue";
5
+
5
6
  import type { Schemas } from "#shopware";
6
7
 
7
8
  type Translations = {
@@ -28,18 +29,24 @@ const props = defineProps<{
28
29
  const isMinimalLayout = computed(() => props.layoutType === "minimal");
29
30
  </script>
30
31
  <template>
31
- <div class="self-stretch p-2 flex flex-col justify-between items-start gap-4 flex-1">
32
+ <div
33
+ class="self-stretch p-2 flex flex-col justify-between items-start gap-4 flex-1"
34
+ >
32
35
  <div class="self-stretch flex flex-col justify-start items-start gap-4">
33
36
  <div class="self-stretch flex flex-col justify-start items-start gap-2">
34
37
  <div class="self-stretch flex flex-col justify-start items-start gap-1">
35
- <div v-if="productManufacturer"
36
- class="self-stretch text-surface-on-surface text-sm font-bold leading-tight">
38
+ <div
39
+ v-if="productManufacturer"
40
+ class="self-stretch text-surface-on-surface text-sm font-bold leading-tight"
41
+ >
37
42
  {{ productManufacturer }}
38
43
  </div>
39
44
 
40
- <RouterLink :to="productLink"
45
+ <RouterLink
46
+ :to="productLink"
41
47
  class="self-stretch text-surface-on-surface text-2xl font-normal font-serif leading-9 overflow-hidden line-clamp-2 break-words min-h-[4.5rem]"
42
- data-testid="product-box-product-name-link">
48
+ data-testid="product-box-product-name-link"
49
+ >
43
50
  {{ productName }}
44
51
  </RouterLink>
45
52
  </div>
@@ -54,13 +61,24 @@ const isMinimalLayout = computed(() => props.layoutType === "minimal");
54
61
  />
55
62
 
56
63
  <!-- Price for standard layout -->
57
- <SwListingProductPrice v-else :product="product" data-testid="product-box-product-price" />
64
+ <SwListingProductPrice
65
+ v-else
66
+ :product="product"
67
+ data-testid="product-box-product-price"
68
+ />
58
69
  </div>
59
70
 
60
71
  <!-- CTA buttons only for non-minimal layout -->
61
72
  <template v-if="!isMinimalLayout">
62
- <SwBaseButton variant="primary" v-if="!fromPrice" size="medium" :disabled="!product?.available" block
63
- data-testid="add-to-cart-button" @click="addToCartProxy">
73
+ <SwBaseButton
74
+ variant="primary"
75
+ v-if="!fromPrice"
76
+ size="medium"
77
+ :disabled="!product?.available"
78
+ block
79
+ data-testid="add-to-cart-button"
80
+ @click="addToCartProxy"
81
+ >
64
82
  {{ translations.product.addToCart }}
65
83
  </SwBaseButton>
66
84
 
@@ -71,4 +89,4 @@ const isMinimalLayout = computed(() => props.layoutType === "minimal");
71
89
  </RouterLink>
72
90
  </template>
73
91
  </div>
74
- </template>
92
+ </template>