@shopware/cms-base-layer 1.5.1 → 2.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.
- package/README.md +398 -12
- package/app/app.config.ts +18 -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 +83 -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/SwFilterDropdown.vue +54 -0
- package/app/components/SwListingProductPrice.vue +89 -0
- package/{components → app/components}/SwMedia3D.vue +4 -2
- 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 +169 -0
- package/app/components/SwProductCardDetails.vue +74 -0
- package/app/components/SwProductCardImage.vue +90 -0
- package/app/components/SwProductCardSkeleton.vue +33 -0
- package/app/components/SwProductGallery.vue +43 -0
- package/app/components/SwProductListingFilter.vue +75 -0
- package/app/components/SwProductListingFilters.vue +304 -0
- package/app/components/SwProductListingFiltersHorizontal.vue +306 -0
- package/{components → app/components}/SwProductPrice.vue +3 -3
- package/app/components/SwProductRating.vue +40 -0
- package/{components → app/components}/SwProductReviews.vue +25 -23
- package/app/components/SwProductReviewsForm.vue +292 -0
- package/{components → app/components}/SwProductUnits.vue +10 -15
- package/app/components/SwQuantitySelect.vue +103 -0
- package/{components → app/components}/SwSlider.vue +154 -55
- package/app/components/SwSortDropdown.vue +87 -0
- package/app/components/SwStockInfo.vue +44 -0
- package/{components → app/components}/SwVariantConfigurator.vue +13 -12
- package/app/components/listing-filters/SwFilterPrice.vue +219 -0
- package/app/components/listing-filters/SwFilterProperties.vue +120 -0
- package/app/components/listing-filters/SwFilterRating.vue +99 -0
- package/app/components/listing-filters/SwFilterShippingFree.vue +114 -0
- package/app/components/public/cms/CmsBlockSpatialViewer.vue +94 -0
- package/app/components/public/cms/CmsGenericBlock.md +42 -0
- package/{components → app/components}/public/cms/CmsGenericBlock.vue +15 -1
- package/{components → app/components}/public/cms/CmsPage.md +19 -2
- package/{components → app/components}/public/cms/CmsPage.vue +30 -5
- package/{components → app/components}/public/cms/block/CmsBlockCenterText.vue +1 -1
- 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/{components → app/components}/public/cms/block/CmsBlockTextOnImage.vue +8 -5
- 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 +22 -6
- package/{components → app/components}/public/cms/element/CmsElementImage.vue +58 -21
- package/app/components/public/cms/element/CmsElementImageGallery.vue +225 -0
- package/{components → app/components}/public/cms/element/CmsElementImageSlider.vue +2 -2
- package/{components → app/components}/public/cms/element/CmsElementProductBox.vue +8 -1
- package/app/components/public/cms/element/CmsElementProductDescriptionReviews.vue +217 -0
- package/{components → app/components}/public/cms/element/CmsElementProductListing.vue +31 -95
- package/app/components/public/cms/element/CmsElementProductName.vue +16 -0
- package/app/components/public/cms/element/CmsElementProductSlider.vue +101 -0
- package/app/components/public/cms/element/CmsElementSidebarFilter.vue +20 -0
- package/{components → app/components}/public/cms/element/CmsElementText.vue +17 -12
- package/app/components/public/cms/element/SwProductListingPagination.vue +70 -0
- package/{components → app/components}/public/cms/section/CmsSectionDefault.vue +2 -2
- package/app/components/public/cms/section/CmsSectionSidebar.vue +39 -0
- package/app/components/public/cms/skeleton/ProductCardSkeleton.vue +28 -0
- package/app/components/ui/BaseButton.vue +102 -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 +34 -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 +15 -0
- package/app/composables/useImagePlaceholder.ts +27 -0
- package/app/composables/useLcpImagePreload.test.ts +229 -0
- package/app/composables/useLcpImagePreload.ts +39 -0
- package/{helpers → app/helpers}/clientOnly.ts +5 -0
- package/app/helpers/cms/findFirstCmsImageUrl.ts +86 -0
- package/app/helpers/cms/getImageSizes.test.ts +50 -0
- package/app/helpers/cms/getImageSizes.ts +36 -0
- package/app/helpers/html-to-vue/ast.ts +106 -0
- package/{helpers → app/helpers}/html-to-vue/getOptionsFromNode.ts +1 -1
- package/{helpers → app/helpers}/html-to-vue/renderToHtml.ts +7 -11
- package/app/helpers/html-to-vue/renderer.ts +116 -0
- package/app/plugins/unocss-runtime.client.ts +23 -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 +36 -0
- package/nuxt.config.ts +100 -6
- package/package.json +33 -23
- package/uno.config.ts +94 -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/SwProductGallery.vue +0 -39
- 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/CmsGenericBlock.md +0 -27
- 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/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/element/CmsElementProductSlider.vue +0 -80
- package/components/public/cms/element/CmsElementSidebarFilter.vue +0 -12
- package/components/public/cms/section/CmsSectionSidebar.vue +0 -41
- package/components/public/cms/skeleton/ProductCardSkeleton.vue +0 -44
- package/helpers/html-to-vue/ast.ts +0 -72
- package/helpers/html-to-vue/renderer.ts +0 -56
- /package/{components → app/components}/SwSharedPrice.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/block/CmsBlockCategoryNavigation.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/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/getOptionsFromNode.test.ts +0 -0
- /package/{helpers → app/helpers}/media/isSpatial.ts +0 -0
package/README.md
CHANGED
|
@@ -87,15 +87,377 @@ Since all CMS components are registered in your Nuxt application, you can now st
|
|
|
87
87
|
|
|
88
88
|
See a [short guide](https://frontends.shopware.com/getting-started/cms/content-pages.html#use-the-cms-base-package) how to use `cms-base` package in your project based on Nuxt v3.
|
|
89
89
|
|
|
90
|
+
## Default styling
|
|
91
|
+
|
|
92
|
+
The components are styled using [Tailwind CSS](https://tailwindcss.com/) utility classes, so you can use them in your project without any additional configuration if your project uses Tailwind CSS.
|
|
93
|
+
|
|
94
|
+
This layer provides a default Tailwind CSS configuration (see [uno.config.ts](./uno.config.ts) for details), which is used to style the components. If you want to customize the styling, you can do so by creating your own Tailwind CSS configuration file and extending the default one:
|
|
95
|
+
|
|
96
|
+
```ts [nuxt.config.ts]
|
|
97
|
+
// nuxt.config.ts
|
|
98
|
+
export default defineNuxtConfig({
|
|
99
|
+
// ...
|
|
100
|
+
unocss: {
|
|
101
|
+
nuxtLayers: true, // enable Nuxt layers for UnoCSS
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```ts [uno.config.ts]
|
|
107
|
+
// uno.config.ts
|
|
108
|
+
import config from './.nuxt/uno.config.mjs'
|
|
109
|
+
|
|
110
|
+
export default config
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Thanks to this, you can **use the default configuration** provided by this layer, or **extend/overwrite** it with your own customizations in your end-project:
|
|
114
|
+
|
|
115
|
+
```ts [uno.config.ts]
|
|
116
|
+
// uno.config.ts
|
|
117
|
+
import { mergeConfigs } from '@unocss/core'
|
|
118
|
+
import config from './.nuxt/uno.config.mjs'
|
|
119
|
+
|
|
120
|
+
export default mergeConfigs([config, {
|
|
121
|
+
theme: {
|
|
122
|
+
colors: {
|
|
123
|
+
primary: '#ff3e00',
|
|
124
|
+
secondary: '#1c1c1c',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
}])
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
See the [UnoCSS reference](https://unocss.dev/integrations/nuxt#configuration) for more information on how to configure UnoCSS in Nuxt when work with layers.
|
|
132
|
+
|
|
133
|
+
## 🖼️ Image Optimization
|
|
134
|
+
|
|
135
|
+
This layer includes [Nuxt Image](https://image.nuxt.com/) configuration optimized for Shopware 6 instances, with a custom provider that maps Nuxt Image modifiers to Shopware's query parameters (`width`, `height`, `quality`, `format`, `fit`).
|
|
136
|
+
|
|
137
|
+
> **Note for Cloud (SaaS) Users:** Image optimization and all modifiers used in the Nuxt Image module are handled automatically by Shopware Cloud infrastructure powered by [Fastly CDN](https://developer.shopware.com/docs/products/paas/shopware/cdn/). No additional configuration or plugins are required - simply use `<NuxtImg>` and all transformations (format conversion, quality adjustment, responsive sizing) work out of the box through Fastly's Image Optimizer.
|
|
138
|
+
|
|
139
|
+
### Features
|
|
140
|
+
|
|
141
|
+
- ✅ Automatic WebP/AVIF format conversion
|
|
142
|
+
- ✅ Responsive image sizing based on viewport
|
|
143
|
+
- ✅ Lazy loading support
|
|
144
|
+
- ✅ Quality optimization
|
|
145
|
+
- ✅ Multiple image presets for common use cases
|
|
146
|
+
- ✅ Works with Shopware Cloud (SaaS) and self-hosted instances
|
|
147
|
+
|
|
148
|
+
### Configuration
|
|
149
|
+
|
|
150
|
+
The layer comes pre-configured with optimized settings. No additional setup is required! The configuration includes:
|
|
151
|
+
|
|
152
|
+
**Available Presets:**
|
|
153
|
+
- `productCard` - Product listing images (WebP, quality 90, cover fit)
|
|
154
|
+
- `productDetail` - Product detail page images (WebP, quality 90, contain fit)
|
|
155
|
+
- `thumbnail` - Small thumbnails (150x150, WebP, quality 90)
|
|
156
|
+
- `hero` - Hero banners (WebP, quality 95, cover fit)
|
|
157
|
+
|
|
158
|
+
**Responsive Breakpoints:**
|
|
159
|
+
- `xs: 320px`, `sm: 640px`, `md: 768px`, `lg: 1024px`, `xl: 1280px`, `xxl: 1536px`
|
|
160
|
+
|
|
161
|
+
### Usage in Components
|
|
162
|
+
|
|
163
|
+
Replace standard `<img>` tags with `<NuxtImg>` to enable automatic optimization:
|
|
164
|
+
|
|
165
|
+
```vue
|
|
166
|
+
<!-- Using presets -->
|
|
167
|
+
<NuxtImg
|
|
168
|
+
src="https://cdn.shopware.store/media/path/to/image.jpg"
|
|
169
|
+
preset="productCard"
|
|
170
|
+
:width="400"
|
|
171
|
+
alt="Product"
|
|
172
|
+
loading="lazy"
|
|
173
|
+
/>
|
|
174
|
+
|
|
175
|
+
<!-- Custom modifiers -->
|
|
176
|
+
<NuxtImg
|
|
177
|
+
src="https://cdn.shopware.store/media/path/to/image.jpg"
|
|
178
|
+
:width="800"
|
|
179
|
+
:height="600"
|
|
180
|
+
format="webp"
|
|
181
|
+
:quality="85"
|
|
182
|
+
fit="cover"
|
|
183
|
+
alt="Custom image"
|
|
184
|
+
/>
|
|
185
|
+
|
|
186
|
+
<!-- Using with dynamic Shopware media URLs -->
|
|
187
|
+
<NuxtImg
|
|
188
|
+
:src="product.cover.media.url"
|
|
189
|
+
preset="productDetail"
|
|
190
|
+
:width="800"
|
|
191
|
+
:alt="product.cover.media.alt"
|
|
192
|
+
/>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Supported Modifiers
|
|
196
|
+
|
|
197
|
+
Shopware supports the following URL parameters for image transformation:
|
|
198
|
+
|
|
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* |
|
|
206
|
+
|
|
207
|
+
*Advanced transformations (quality, format, fit) are available in:
|
|
208
|
+
- **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
|
+
- **Self-hosted instances**: Require thumbnail processor plugins like [FroshPlatformThumbnailProcessor](https://github.com/FriendsOfShopware/FroshPlatformThumbnailProcessor) or third-party CDN integration
|
|
210
|
+
|
|
211
|
+
### How It Works
|
|
212
|
+
|
|
213
|
+
This layer includes a custom Shopware provider for Nuxt Image that maps modifiers to Shopware's query parameters:
|
|
214
|
+
- `width` modifier → `?width=400`
|
|
215
|
+
- `height` modifier → `?height=300`
|
|
216
|
+
- `quality` modifier → `?quality=85`
|
|
217
|
+
- `format` modifier → `?format=webp`
|
|
218
|
+
- `fit` modifier → `?fit=cover`
|
|
219
|
+
|
|
220
|
+
When you use `<NuxtImg>`, the custom provider automatically converts your component props into the correct URL format for Shopware. The images are then processed on-the-fly by Shopware Cloud (SaaS) infrastructure or your configured thumbnail processor.
|
|
221
|
+
|
|
222
|
+
#### 🔍 Understanding Image Processing in Shopware
|
|
223
|
+
|
|
224
|
+
**Built-in Thumbnail Generation:**
|
|
225
|
+
Shopware has native thumbnail generation (using GD2 or ImageMagick) that creates predefined sizes (400x400, 800x800, 1920x1920) during image upload. These thumbnails are generated once and stored on your server.
|
|
226
|
+
|
|
227
|
+
**Dynamic On-the-Fly Transformations:**
|
|
228
|
+
For dynamic image transformations via query parameters (like `?width=800&format=webp`), you need **remote thumbnail generation** configured:
|
|
229
|
+
|
|
230
|
+
- **Shopware Cloud (SaaS)**: ✅ Fully supported out-of-the-box via Fastly CDN - all query parameters work automatically
|
|
231
|
+
- **Self-hosted**: ⚠️ Requires additional setup:
|
|
232
|
+
- Install a plugin like [FroshPlatformThumbnailProcessor](https://github.com/FriendsOfShopware/FroshPlatformThumbnailProcessor) for on-the-fly processing, OR
|
|
233
|
+
- Configure external middleware (Thumbor, Sharp, imgproxy) via [remote thumbnail generation](https://developer.shopware.com/docs/guides/plugins/plugins/content/media/remote-thumbnail-generation.html)
|
|
234
|
+
|
|
235
|
+
**Without remote thumbnail generation configured**, query parameters will be ignored and only the predefined static thumbnails will be served.
|
|
236
|
+
|
|
237
|
+
> **💡 Recommendation**: If you're self-hosting Shopware and want to use dynamic image transformations with Nuxt Image modifiers, install the FroshPlatformThumbnailProcessor plugin first to enable on-the-fly processing.
|
|
238
|
+
|
|
239
|
+
### Customizing Configuration
|
|
240
|
+
|
|
241
|
+
You can extend or override the default settings in your project's `nuxt.config.ts`:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
export default defineNuxtConfig({
|
|
245
|
+
extends: ["@shopware/cms-base-layer"],
|
|
246
|
+
|
|
247
|
+
image: {
|
|
248
|
+
// Change default quality
|
|
249
|
+
quality: 85,
|
|
250
|
+
|
|
251
|
+
// Add/change formats
|
|
252
|
+
formats: ['avif', 'webp', 'jpg'],
|
|
253
|
+
|
|
254
|
+
// Override or add presets
|
|
255
|
+
presets: {
|
|
256
|
+
// Override existing preset
|
|
257
|
+
productCard: {
|
|
258
|
+
modifiers: {
|
|
259
|
+
format: 'avif',
|
|
260
|
+
quality: 80,
|
|
261
|
+
fit: 'cover',
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
// Add custom preset
|
|
265
|
+
categoryBanner: {
|
|
266
|
+
modifiers: {
|
|
267
|
+
format: 'webp',
|
|
268
|
+
quality: 90,
|
|
269
|
+
width: 1200,
|
|
270
|
+
height: 400,
|
|
271
|
+
fit: 'cover',
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## 🖼️ Image Placeholder
|
|
280
|
+
|
|
281
|
+
This layer provides a `useImagePlaceholder` composable that generates an SVG placeholder for images during loading. The placeholder features a centered icon with a subtle background.
|
|
282
|
+
|
|
283
|
+
### Customizing Placeholder Color
|
|
284
|
+
|
|
285
|
+
You can customize the placeholder color globally in your project's `app.config.ts`:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
export default defineAppConfig({
|
|
289
|
+
imagePlaceholder: {
|
|
290
|
+
color: "#your-color-here", // Default: #543B95
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Or use a custom color for specific instances:
|
|
296
|
+
|
|
297
|
+
```vue
|
|
298
|
+
<script setup>
|
|
299
|
+
const customPlaceholder = useImagePlaceholder("#FF0000");
|
|
300
|
+
</script>
|
|
301
|
+
|
|
302
|
+
<template>
|
|
303
|
+
<NuxtImg :placeholder="customPlaceholder" src="..." />
|
|
304
|
+
</template>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## 🖼️ Background Image Optimization
|
|
308
|
+
|
|
309
|
+
CMS sections and blocks can have background images set via the Shopware admin. This layer automatically optimizes those background image URLs by appending `format` and `quality` query parameters — bringing the same optimization applied to `<NuxtImg>` components to CSS background images.
|
|
310
|
+
|
|
311
|
+
Both `CmsPage` (for section backgrounds) and `CmsGenericBlock` (for block backgrounds) read the configuration from `app.config.ts` and pass it to the `getBackgroundImageUrl` helper from `@shopware/helpers`.
|
|
312
|
+
|
|
313
|
+
### Configuration
|
|
314
|
+
|
|
315
|
+
Default values are set in `app.config.ts` and can be overridden in your project:
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
export default defineAppConfig({
|
|
319
|
+
backgroundImage: {
|
|
320
|
+
format: "webp", // Default: "webp" — output format ("webp" | "avif" | "jpg" | "png")
|
|
321
|
+
quality: 90, // Default: 90 — image quality (0-100)
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Setting `format` or `quality` to `undefined` (or omitting the key) will skip that parameter in the generated URL.
|
|
327
|
+
|
|
328
|
+
### How It Works
|
|
329
|
+
|
|
330
|
+
When a CMS section or block has a `backgroundMedia` set, the components call `getBackgroundImageUrl()` which:
|
|
331
|
+
|
|
332
|
+
1. Extracts the raw image URL from the CSS `url()` value
|
|
333
|
+
2. Appends `width` or `height` based on the image's original dimensions (capped at 1920px)
|
|
334
|
+
3. Adds `fit=crop,smart` for intelligent cropping
|
|
335
|
+
4. Appends `format` and `quality` from `app.config.ts` if provided
|
|
336
|
+
|
|
337
|
+
**Example generated URL:**
|
|
338
|
+
```
|
|
339
|
+
url("https://cdn.shopware.store/.../image.jpg?width=1000&fit=crop,smart&format=webp&quality=85")
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
> **Note:** Like other dynamic image transformations, background image optimization requires remote thumbnail generation support. See the [Image Optimization](#%EF%B8%8F-image-optimization) section above for Shopware Cloud vs. self-hosted requirements.
|
|
343
|
+
|
|
344
|
+
## LCP Image Preload
|
|
345
|
+
|
|
346
|
+
This layer includes a `useLcpImagePreload` composable that automatically preloads the first image found in CMS page content. This targets the [Largest Contentful Paint (LCP)](https://web.dev/lcp/) element, which is often a hero background image or the first visible image element.
|
|
347
|
+
|
|
348
|
+
### How it works
|
|
349
|
+
|
|
350
|
+
The composable scans CMS sections in document order, checking:
|
|
351
|
+
1. Section background images (`section.backgroundMedia`)
|
|
352
|
+
2. Block background images (`block.backgroundMedia`)
|
|
353
|
+
3. Image element media (`slot.data.media`)
|
|
354
|
+
|
|
355
|
+
The first image found is injected as a `<link rel="preload" as="image" fetchpriority="high">` in the `<head>` during SSR. This allows the browser to start fetching the LCP image immediately, before parsing CSS or executing JavaScript. The `fetchpriority="high"` attribute ensures the preload is prioritized — this is especially useful for background images which don't natively support `fetchpriority`.
|
|
356
|
+
|
|
357
|
+
### Usage
|
|
358
|
+
|
|
359
|
+
The composable is already called in `CmsPage.vue`. If you override `CmsPage`, you can use it in your custom component:
|
|
360
|
+
|
|
361
|
+
```vue
|
|
362
|
+
<script setup>
|
|
363
|
+
import { useLcpImagePreload } from "@shopware/cms-base-layer/composables/useLcpImagePreload";
|
|
364
|
+
|
|
365
|
+
const props = defineProps<{ content: Schemas["CmsPage"] }>();
|
|
366
|
+
|
|
367
|
+
useLcpImagePreload(props.content?.sections || []);
|
|
368
|
+
</script>
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
The preload URL includes the optimized `format` and `quality` parameters from `app.config.ts` for both background images and element images.
|
|
372
|
+
|
|
373
|
+
## Responsive CMS Images
|
|
374
|
+
|
|
375
|
+
CMS image elements (`CmsElementImage`) automatically serve appropriately-sized images using responsive `srcset` and `sizes` attributes. This prevents the browser from downloading images larger than their displayed dimensions — a common Lighthouse performance issue.
|
|
376
|
+
|
|
377
|
+
### How it works
|
|
378
|
+
|
|
379
|
+
1. **`CmsGenericBlock`** counts the number of slots in each block, `provide`s a responsive `sizes` value (e.g., a 2-slot block means images are ~50% viewport width on desktop), and `provide`s the slot count via `cms-block-slot-count` for slider SSR breakpoint scaling.
|
|
380
|
+
2. **`CmsElementImage`** `inject`s the sizes hint and applies it to `<NuxtImg>`.
|
|
381
|
+
3. If the media has **pre-generated thumbnails** from Shopware, the existing `srcset` from thumbnails is used.
|
|
382
|
+
4. If **no thumbnails** exist, a synthetic `srcset` is generated using CDN width-based resizing (`?width=400`, `?width=800`, etc.) via the `generateCdnSrcSet` helper from `@shopware/helpers`.
|
|
383
|
+
5. **Slider components** (`CmsElementProductSlider`, `CmsElementCrossSelling`) `inject` the slot count to scale their SSR breakpoints — ensuring media queries account for the container being a fraction of the viewport (e.g., a 2-slot block doubles the breakpoint thresholds).
|
|
384
|
+
|
|
385
|
+
The browser combines `sizes` + `srcset` to download only the image size it actually needs — during HTML parsing, before any JavaScript runs.
|
|
386
|
+
|
|
387
|
+
### Configuration
|
|
388
|
+
|
|
389
|
+
Default slot-count-to-sizes mappings are set in `app.config.ts` and can be overridden:
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
export default defineAppConfig({
|
|
393
|
+
imageSizes: {
|
|
394
|
+
// slot count → sizes attribute value
|
|
395
|
+
1: "(max-width: 768px) 100vw, 100vw", // full-width blocks
|
|
396
|
+
2: "(max-width: 768px) 100vw, 50vw", // two-column blocks (e.g., image-text)
|
|
397
|
+
3: "(max-width: 768px) 100vw, 33vw", // three-column blocks
|
|
398
|
+
default: "(max-width: 768px) 50vw, 25vw", // 4+ columns
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
For example, to cap image sizes at a fixed pixel width for boxed layouts:
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
export default defineAppConfig({
|
|
407
|
+
imageSizes: {
|
|
408
|
+
1: "(max-width: 768px) 100vw, 1200px",
|
|
409
|
+
2: "(max-width: 768px) 100vw, 600px",
|
|
410
|
+
3: "(max-width: 768px) 100vw, 400px",
|
|
411
|
+
default: "(max-width: 768px) 50vw, 300px",
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Per-block override
|
|
417
|
+
|
|
418
|
+
Individual block components can override the sizes value by calling `provide("cms-image-sizes", "custom value")` — this takes precedence over the default from `CmsGenericBlock`.
|
|
419
|
+
|
|
420
|
+
### Synthetic srcset fallback
|
|
421
|
+
|
|
422
|
+
When Shopware media has no thumbnails (common in Cloud/SaaS setups using CDN-based resizing), the layer generates a synthetic `srcset` using CDN query parameters:
|
|
423
|
+
|
|
424
|
+
```html
|
|
425
|
+
<img srcset="
|
|
426
|
+
...image.jpg?width=400&fit=crop,smart&format=webp&quality=90 400w,
|
|
427
|
+
...image.jpg?width=800&fit=crop,smart&format=webp&quality=90 800w,
|
|
428
|
+
...image.jpg?width=1200&fit=crop,smart&format=webp&quality=90 1200w,
|
|
429
|
+
...image.jpg?width=1600&fit=crop,smart&format=webp&quality=90 1600w"
|
|
430
|
+
sizes="(max-width: 768px) 100vw, 50vw"
|
|
431
|
+
>
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
The `format` and `quality` values are taken from the `backgroundImage` config in `app.config.ts`.
|
|
435
|
+
|
|
436
|
+
> **Note:** Synthetic srcset requires CDN-based image resizing support. See the [Image Optimization](#%EF%B8%8F-image-optimization) section for requirements.
|
|
437
|
+
|
|
438
|
+
## 🔄 UnoCSS Runtime
|
|
439
|
+
|
|
440
|
+
This layer includes a client-side [UnoCSS runtime](https://unocss.dev/integrations/runtime) plugin that resolves utility classes dynamically at runtime using a DOM MutationObserver. This is useful when CMS content from Shopware contains utility classes that aren't known at build time (e.g., inline styles or dynamic class bindings from the admin panel).
|
|
441
|
+
|
|
442
|
+
The runtime is **enabled by default**. To disable it, set `unocssRuntime` to `false` in your project's `app.config.ts`:
|
|
443
|
+
|
|
444
|
+
```ts
|
|
445
|
+
export default defineAppConfig({
|
|
446
|
+
unocssRuntime: false,
|
|
447
|
+
});
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
> **When to disable**: If you don't use dynamic CMS utility classes, or if you experience performance issues caused by the MutationObserver in pages with frequent DOM mutations.
|
|
451
|
+
|
|
90
452
|
## 📘 Available components
|
|
91
453
|
|
|
92
|
-
The list of available blocks and elements is [here](https://frontends.shopware.com/packages/cms-base.html#available-components).
|
|
454
|
+
The list of available blocks and elements is [here](https://frontends.shopware.com/packages/cms-base-layer.html#available-components).
|
|
93
455
|
|
|
94
456
|
## 🔄 Overwriting components
|
|
95
457
|
|
|
96
458
|
The procedure is:
|
|
97
459
|
|
|
98
|
-
- 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/components)
|
|
460
|
+
- 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)
|
|
99
461
|
- take its name
|
|
100
462
|
- create a file with the same name and place it into `~/components` dir in your nuxt project (or wherever according your nuxt config)
|
|
101
463
|
|
|
@@ -105,7 +467,7 @@ The procedure is:
|
|
|
105
467
|
|
|
106
468
|
❗**Internal components are not a part of public API. Once overwritten you need to track the changes on your own.**
|
|
107
469
|
|
|
108
|
-
There is also a possibility to override the internal components, shared between public blocks and elements, the ones starting with `Sw` prefix, like [SwSlider.vue](https://github.com/shopware/frontends/blob/main/packages/cms-base-layer/components/SwSlider.vue) or [SwProductCard.vue](https://github.com/shopware/frontends/blob/main/packages/cms-base-layer/components/SwProductCard.vue).
|
|
470
|
+
There is also a possibility to override the internal components, shared between public blocks and elements, the ones starting with `Sw` prefix, like [SwSlider.vue](https://github.com/shopware/frontends/blob/main/packages/cms-base-layer/app/components/SwSlider.vue) or [SwProductCard.vue](https://github.com/shopware/frontends/blob/main/packages/cms-base-layer/app/components/SwProductCard.vue).
|
|
109
471
|
|
|
110
472
|
An example: some components use `SwSharedPrice.vue` to show prices with corresponding currency for products in many places like product card, product details page and so on. In order to change the way how the price is displayed consistently - create a one component with a name `SwSharedPrice.vue` and that's it. The new component will be used everywhere where is "imported" (autoimported actually).
|
|
111
473
|
|
|
@@ -124,7 +486,7 @@ No additional packages needed to be installed.
|
|
|
124
486
|
|
|
125
487
|
- [📘 Documentation](https://frontends.shopware.com)
|
|
126
488
|
|
|
127
|
-
- [👥 Community](https://
|
|
489
|
+
- [👥 Community](https://discord.com/channels/1308047705309708348/1405501315160739951) (`#composable-frontend`)
|
|
128
490
|
|
|
129
491
|
<!-- AUTO GENERATED CHANGELOG -->
|
|
130
492
|
|
|
@@ -132,18 +494,42 @@ No additional packages needed to be installed.
|
|
|
132
494
|
|
|
133
495
|
Full changelog for stable version is available [here](https://github.com/shopware/frontends/blob/main/packages/cms-base-layer/CHANGELOG.md)
|
|
134
496
|
|
|
135
|
-
### Latest changes: 1.
|
|
497
|
+
### Latest changes: 2.1.0
|
|
498
|
+
|
|
499
|
+
### Minor Changes
|
|
500
|
+
|
|
501
|
+
- [#2275](https://github.com/shopware/frontends/pull/2275) [`432dd24`](https://github.com/shopware/frontends/commit/432dd246571dfa8c149293da97d5bb16f505e54c) Thanks [@mkucmus](https://github.com/mkucmus)! - - Add configurable UnoCSS runtime plugin for dynamic CMS class support
|
|
502
|
+
- Extend theme with overlay and fixed color tokens
|
|
503
|
+
|
|
504
|
+
- [#2223](https://github.com/shopware/frontends/pull/2223) [`1db8704`](https://github.com/shopware/frontends/commit/1db870413dcea13c690504ffcaee13526bc8035f) Thanks [@mkucmus](https://github.com/mkucmus)! - Add horizontal filter layout for product listings. When the sidebar filter element is placed outside a sidebar section, filters now display as horizontal dropdowns.
|
|
505
|
+
|
|
506
|
+
Includes new `SwFilterDropdown` and `SwProductListingFiltersHorizontal` components, with a `displayMode` prop added to all filter components to support both layouts.
|
|
507
|
+
|
|
508
|
+
- [#2287](https://github.com/shopware/frontends/pull/2287) [`c9bde38`](https://github.com/shopware/frontends/commit/c9bde38d497d5c6c2fbd97700a362eb44ce8881f) Thanks [@mkucmus](https://github.com/mkucmus)! - Add responsive image sizing (srcset/sizes) via provide/inject, LCP preload with fetchpriority, configurable imageSizes in app.config, ISR route rules, inline styles, and AppConfig type declarations
|
|
509
|
+
|
|
510
|
+
- [#2241](https://github.com/shopware/frontends/pull/2241) [`9ccbaa1`](https://github.com/shopware/frontends/commit/9ccbaa1fb6cc1f790d979c3dd3745c5402b6d8d1) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Replace `withDefaults` by props destructure
|
|
511
|
+
|
|
512
|
+
- [#2273](https://github.com/shopware/frontends/pull/2273) [`18455e7`](https://github.com/shopware/frontends/commit/18455e77221fcc77b119d0ba7eae89dfce0e2941) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Remove SwMedia3D.vue component from autoload
|
|
513
|
+
|
|
514
|
+
- [#2268](https://github.com/shopware/frontends/pull/2268) [`c3fff84`](https://github.com/shopware/frontends/commit/c3fff847e46a17c9c905bd893f1c1de287426c65) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Improve accessibility (A11y) of CMS base layer components.
|
|
515
|
+
|
|
516
|
+
- [#2214](https://github.com/shopware/frontends/pull/2214) [`ccb9384`](https://github.com/shopware/frontends/commit/ccb93849be07f1b6a4e192de02579a528b5b6ac4) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Add full TypeScript typing to html-to-vue helper functions
|
|
136
517
|
|
|
137
518
|
### Patch Changes
|
|
138
519
|
|
|
139
|
-
- [#
|
|
520
|
+
- [#2226](https://github.com/shopware/frontends/pull/2226) [`d77eacc`](https://github.com/shopware/frontends/commit/d77eaccdec6c56a6f2d999048c751fb9f01177d4) Thanks [@mkucmus](https://github.com/mkucmus)! - Add config prop support to `SwProductGallery` and make `CmsElementImageGallery` respect `minHeight`, `navigationArrows`, and `navigationDots` config values.
|
|
140
521
|
|
|
141
|
-
- [#
|
|
522
|
+
- [#2275](https://github.com/shopware/frontends/pull/2275) [`432dd24`](https://github.com/shopware/frontends/commit/432dd246571dfa8c149293da97d5bb16f505e54c) Thanks [@mkucmus](https://github.com/mkucmus)! - - Fix CMS block layout: height propagation in CmsBlockImageText, conditional `h-full` in CmsElementImage, `backgroundSize` forwarding in CmsGenericBlock, `w-full` for full_width sizing mode, and exclude `sizingMode` from section inline styles
|
|
523
|
+
- Fix vertical alignment support in CmsElementText and CmsElementProductSlider using `align-content` CSS property
|
|
524
|
+
- Remove rounded corners from image placeholder SVG and simplify CmsBlockTextOnImage structure
|
|
142
525
|
|
|
143
|
-
- [#
|
|
526
|
+
- [#2210](https://github.com/shopware/frontends/pull/2210) [`c6b88b7`](https://github.com/shopware/frontends/commit/c6b88b7d2c50054188356aeb0f83053554d442f5) Thanks [@mkucmus](https://github.com/mkucmus)! - Anchor tags with "btn btn-primary" classes from the API were not being
|
|
527
|
+
transformed to Tailwind utility classes due to condition matching issues
|
|
528
|
+
in the html-to-vue renderer.
|
|
144
529
|
|
|
145
|
-
- [#
|
|
530
|
+
- [#2318](https://github.com/shopware/frontends/pull/2318) [`b40305f`](https://github.com/shopware/frontends/commit/b40305f9e2ec51f29c279650e411bb773438faed) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Components (CmsElementBuyBox, SwListingProductPrice, SwProductPrice) updated to use `hasListPrice` instead of deprecated `isListPrice`.
|
|
146
531
|
|
|
147
|
-
- Updated dependencies [[`
|
|
148
|
-
- @shopware/
|
|
149
|
-
- @shopware/
|
|
532
|
+
- Updated dependencies [[`9604f22`](https://github.com/shopware/frontends/commit/9604f22678150d04c3c3156fd8ee2ce440c8c8bf), [`b40305f`](https://github.com/shopware/frontends/commit/b40305f9e2ec51f29c279650e411bb773438faed), [`432dd24`](https://github.com/shopware/frontends/commit/432dd246571dfa8c149293da97d5bb16f505e54c), [`b5f7e2a`](https://github.com/shopware/frontends/commit/b5f7e2a20c9dfdde1690e9006252d847f732bc0a), [`b5f7e2a`](https://github.com/shopware/frontends/commit/b5f7e2a20c9dfdde1690e9006252d847f732bc0a), [`9604f22`](https://github.com/shopware/frontends/commit/9604f22678150d04c3c3156fd8ee2ce440c8c8bf), [`a871c7b`](https://github.com/shopware/frontends/commit/a871c7b6256b75c2e40d93fc0354ba1971420062), [`c9bde38`](https://github.com/shopware/frontends/commit/c9bde38d497d5c6c2fbd97700a362eb44ce8881f)]:
|
|
533
|
+
- @shopware/api-client@1.5.0
|
|
534
|
+
- @shopware/composables@1.11.0
|
|
535
|
+
- @shopware/helpers@1.7.0
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineAppConfig } from "#imports";
|
|
2
|
+
|
|
3
|
+
export default defineAppConfig({
|
|
4
|
+
imagePlaceholder: {
|
|
5
|
+
color: "#543B95",
|
|
6
|
+
},
|
|
7
|
+
backgroundImage: {
|
|
8
|
+
format: "webp",
|
|
9
|
+
quality: 90,
|
|
10
|
+
},
|
|
11
|
+
imageSizes: {
|
|
12
|
+
1: "(max-width: 768px) 100vw, 100vw",
|
|
13
|
+
2: "(max-width: 768px) 100vw, 50vw",
|
|
14
|
+
3: "(max-width: 768px) 100vw, 33vw",
|
|
15
|
+
default: "(max-width: 768px) 50vw, 25vw",
|
|
16
|
+
},
|
|
17
|
+
unocssRuntime: true,
|
|
18
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 24C18.6274 24 24 18.6274 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24ZM7.56066 10.9393L10.5 13.8787L16.4393 7.93934C17.0251 7.35355 17.9749 7.35355 18.5607 7.93934C19.1464 8.52513 19.1464 9.47487 18.5607 10.0607L11.5607 17.0607C10.9749 17.6464 10.0251 17.6464 9.43934 17.0607L5.43934 13.0607C4.85355 12.4749 4.85355 11.5251 5.43934 10.9393C6.02513 10.3536 6.97487 10.3536 7.56066 10.9393Z" fill="#1E1E24"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.06066 10.4393C2.47487 9.85355 1.52513 9.85355 0.93934 10.4393C0.353553 11.0251 0.353553 11.9749 0.93934 12.5607L7.93934 19.5607C8.52513 20.1464 9.47487 20.1464 10.0607 19.5607L23.0607 6.56066C23.6464 5.97487 23.6464 5.02513 23.0607 4.43934C22.4749 3.85355 21.5251 3.85355 20.9393 4.43934L9 16.3787L3.06066 10.4393Z" fill="#1E1E24"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.70711 8.79289C8.31658 8.40237 7.68342 8.40237 7.29289 8.79289C6.90237 9.18342 6.90237 9.81658 7.29289 10.2071L11.2929 14.2071C11.6834 14.5976 12.3166 14.5976 12.7071 14.2071L16.7071 10.2071C17.0976 9.81658 17.0976 9.18342 16.7071 8.79289C16.3166 8.40237 15.6834 8.40237 15.2929 8.79289L12 12.0858L8.70711 8.79289Z" fill="currentColor"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12ZM10.5 7.5V12C10.5 12.8284 11.1716 13.5 12 13.5C12.8284 13.5 13.5 12.8284 13.5 12V7.5C13.5 6.67157 12.8284 6 12 6C11.1716 6 10.5 6.67157 10.5 7.5ZM12 18C12.8284 18 13.5 17.3284 13.5 16.5C13.5 15.6716 12.8284 15 12 15C11.1716 15 10.5 15.6716 10.5 16.5C10.5 17.3284 11.1716 18 12 18Z" fill="#1E1E24"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.00015 12.5599L12.0046 14.3245L11.5638 9.97075L14.4795 6.70761L10.2026 5.78147L8.00015 2.00012L5.79771 5.78147L1.52085 6.70761L4.43653 9.97075L3.99572 14.3245L8.00015 12.5599ZM4.53339 15.5446C3.85953 15.8416 3.07255 15.536 2.7756 14.8622C2.68261 14.6511 2.64594 14.4196 2.66917 14.1902L3.0508 10.421L0.526591 7.59599C0.0359476 7.04688 0.083346 6.20399 0.632458 5.71335C0.804415 5.5597 1.01328 5.45328 1.23866 5.40448L4.94128 4.60269L6.848 1.32905C7.21862 0.692736 8.0349 0.477347 8.67122 0.847967C8.87048 0.964028 9.03624 1.12979 9.1523 1.32905L11.059 4.60269L14.7616 5.40448C15.4813 5.56033 15.9384 6.2701 15.7826 6.9898C15.7338 7.21517 15.6274 7.42404 15.4737 7.59599L12.9495 10.421L13.3311 14.1902C13.4053 14.9228 12.8715 15.5769 12.1389 15.651C11.9095 15.6743 11.6779 15.6376 11.4669 15.5446L8.00015 14.0169L4.53339 15.5446Z" fill="#696470"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.53339 15.5446C3.85953 15.8416 3.07255 15.536 2.7756 14.8622C2.68261 14.6511 2.64594 14.4196 2.66917 14.1902L3.0508 10.421L0.526591 7.59599C0.0359476 7.04688 0.083346 6.20399 0.632458 5.71335C0.804415 5.5597 1.01328 5.45328 1.23866 5.40448L4.94128 4.60269L6.848 1.32905C7.21862 0.692736 8.0349 0.477347 8.67122 0.847967C8.87048 0.964028 9.03624 1.12979 9.1523 1.32905L11.059 4.60269L14.7616 5.40448C15.4813 5.56033 15.9384 6.2701 15.7826 6.9898C15.7338 7.21517 15.6274 7.42404 15.4737 7.59599L12.9495 10.421L13.3311 14.1902C13.4053 14.9228 12.8715 15.5769 12.1389 15.651C11.9095 15.6743 11.6779 15.6376 11.4669 15.5446L8.00015 14.0169L4.53339 15.5446Z" fill="#696470"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg id="meteor-icon-kit__regular-user" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M10 2C7.79086 2 6 3.79086 6 6C6 8.20914 7.79086 10 10 10C12.2091 10 14 8.20914 14 6C14 3.79086 12.2091 2 10 2zM10 0C13.3137 0 16 2.68629 16 6C16 9.3137 13.3137 12 10 12C6.68629 12 4 9.3137 4 6C4 2.68629 6.68629 0 10 0zM2 21.099C2 21.6513 1.55228 22.099 1 22.099C0.44772 22.099 0 21.6513 0 21.099V19C0 16.2386 2.23858 14 5 14H15.0007C17.7621 14 20.0007 16.2386 20.0007 19V21.099C20.0007 21.6513 19.553 22.099 19.0007 22.099C18.4484 22.099 18.0007 21.6513 18.0007 21.099V19C18.0007 17.3431 16.6576 16 15.0007 16H5C3.34315 16 2 17.3431 2 19V21.099z" fill="currentColor"/></svg>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from "vue";
|
|
3
|
+
import type { Schemas } from "#shopware";
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
activeCategory,
|
|
7
|
+
elements,
|
|
8
|
+
level = 0,
|
|
9
|
+
} = defineProps<{
|
|
10
|
+
activeCategory: Schemas["Category"];
|
|
11
|
+
elements: Schemas["Category"][];
|
|
12
|
+
level: number;
|
|
13
|
+
}>();
|
|
14
|
+
|
|
15
|
+
const expandedItems = ref<Set<string>>(new Set());
|
|
16
|
+
|
|
17
|
+
function isActive(navigationElement: Schemas["Category"]) {
|
|
18
|
+
return navigationElement.id === activeCategory?.id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function toggleExpanded(id: string) {
|
|
22
|
+
if (expandedItems.value.has(id)) {
|
|
23
|
+
expandedItems.value.delete(id);
|
|
24
|
+
} else {
|
|
25
|
+
expandedItems.value.add(id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isExpanded(id: string) {
|
|
30
|
+
return expandedItems.value.has(id);
|
|
31
|
+
}
|
|
32
|
+
</script>
|
|
33
|
+
<template>
|
|
34
|
+
<div
|
|
35
|
+
v-if="elements?.length"
|
|
36
|
+
class="self-stretch flex flex-col justify-start items-start gap-4"
|
|
37
|
+
>
|
|
38
|
+
<div
|
|
39
|
+
v-for="(navigationElement, index) in elements"
|
|
40
|
+
:key="index"
|
|
41
|
+
class="w-full"
|
|
42
|
+
>
|
|
43
|
+
<SwCategoryNavigationLink
|
|
44
|
+
:navigation-element="navigationElement"
|
|
45
|
+
:is-active="isActive(navigationElement)"
|
|
46
|
+
:is-expanded="isExpanded(navigationElement.id)"
|
|
47
|
+
:level="level"
|
|
48
|
+
@toggle="toggleExpanded(navigationElement.id)"
|
|
49
|
+
/>
|
|
50
|
+
<transition name="filter-collapse">
|
|
51
|
+
<div
|
|
52
|
+
v-if="navigationElement.children && isExpanded(navigationElement.id)"
|
|
53
|
+
class="self-stretch flex flex-col justify-start items-start"
|
|
54
|
+
>
|
|
55
|
+
<SwCategoryNavigation
|
|
56
|
+
:elements="navigationElement.children"
|
|
57
|
+
:active-category="activeCategory"
|
|
58
|
+
:level="level + 1"
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
</transition>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
<style scoped>
|
|
66
|
+
.filter-collapse-enter-active,
|
|
67
|
+
.filter-collapse-leave-active {
|
|
68
|
+
transition:
|
|
69
|
+
max-height 240ms ease,
|
|
70
|
+
opacity 200ms ease;
|
|
71
|
+
overflow: hidden;
|
|
72
|
+
}
|
|
73
|
+
.filter-collapse-enter-from,
|
|
74
|
+
.filter-collapse-leave-to {
|
|
75
|
+
max-height: 0;
|
|
76
|
+
opacity: 0;
|
|
77
|
+
}
|
|
78
|
+
.filter-collapse-enter-to,
|
|
79
|
+
.filter-collapse-leave-from {
|
|
80
|
+
max-height: 800px;
|
|
81
|
+
opacity: 1;
|
|
82
|
+
}
|
|
83
|
+
</style>
|