@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.
Files changed (183) hide show
  1. package/README.md +330 -12
  2. package/app/app.config.ts +7 -0
  3. package/app/assets/icons/check-circle.svg +3 -0
  4. package/app/assets/icons/checkmark.svg +3 -0
  5. package/app/assets/icons/chevron.svg +3 -0
  6. package/app/assets/icons/exclamation-circle.svg +3 -0
  7. package/app/assets/icons/star-empty.svg +3 -0
  8. package/app/assets/icons/star-filled.svg +3 -0
  9. package/app/assets/icons/user.svg +1 -0
  10. package/app/components/SwCategoryNavigation.vue +76 -0
  11. package/app/components/SwCategoryNavigationLink.vue +128 -0
  12. package/{components → app/components}/SwContactForm.vue +27 -27
  13. package/app/components/SwFilterChips.vue +144 -0
  14. package/app/components/SwListingProductPrice.vue +89 -0
  15. package/{components → app/components}/SwNewsletterForm.vue +45 -34
  16. package/{components → app/components}/SwPagination.vue +3 -5
  17. package/{components → app/components}/SwProductAddToCart.vue +22 -27
  18. package/app/components/SwProductCard.vue +170 -0
  19. package/app/components/SwProductCardDetails.vue +57 -0
  20. package/app/components/SwProductCardImage.vue +87 -0
  21. package/app/components/SwProductCardSkeleton.vue +33 -0
  22. package/app/components/SwProductListingFilter.vue +64 -0
  23. package/app/components/SwProductListingFilters.vue +308 -0
  24. package/{components → app/components}/SwProductReviews.vue +28 -13
  25. package/app/components/SwProductReviewsForm.vue +292 -0
  26. package/app/components/SwQuantitySelect.vue +106 -0
  27. package/{components → app/components}/SwSlider.vue +4 -4
  28. package/app/components/SwSortDropdown.vue +83 -0
  29. package/app/components/SwStockInfo.vue +44 -0
  30. package/{components → app/components}/SwVariantConfigurator.vue +1 -1
  31. package/app/components/listing-filters/SwFilterPrice.vue +214 -0
  32. package/app/components/listing-filters/SwFilterProperties.vue +113 -0
  33. package/app/components/listing-filters/SwFilterRating.vue +90 -0
  34. package/app/components/listing-filters/SwFilterShippingFree.vue +107 -0
  35. package/{components → app/components}/public/cms/CmsPage.vue +19 -4
  36. package/{components → app/components}/public/cms/block/CmsBlockGalleryBuybox.vue +5 -5
  37. package/{components → app/components}/public/cms/block/CmsBlockImageBubbleRow.vue +5 -5
  38. package/app/components/public/cms/block/CmsBlockImageFourColumn.vue +41 -0
  39. package/app/components/public/cms/block/CmsBlockImageGalleryBig.vue +42 -0
  40. package/app/components/public/cms/block/CmsBlockImageHighlightRow.vue +37 -0
  41. package/{components → app/components}/public/cms/block/CmsBlockImageSimpleGrid.vue +11 -5
  42. package/{components → app/components}/public/cms/block/CmsBlockImageText.vue +7 -3
  43. package/{components → app/components}/public/cms/block/CmsBlockImageTextBubble.vue +13 -16
  44. package/{components → app/components}/public/cms/block/CmsBlockImageTextCover.vue +7 -9
  45. package/app/components/public/cms/block/CmsBlockImageTextGallery.vue +88 -0
  46. package/app/components/public/cms/block/CmsBlockImageTextRow.vue +53 -0
  47. package/{components → app/components}/public/cms/block/CmsBlockImageThreeColumn.vue +10 -4
  48. package/app/components/public/cms/block/CmsBlockImageThreeCover.vue +37 -0
  49. package/app/components/public/cms/block/CmsBlockImageTwoColumn.vue +37 -0
  50. package/{components → app/components}/public/cms/block/CmsBlockProductHeading.vue +1 -1
  51. package/{components → app/components}/public/cms/block/CmsBlockProductThreeColumn.vue +10 -4
  52. package/{components → app/components}/public/cms/block/CmsBlockSidebarFilter.vue +3 -1
  53. package/app/components/public/cms/block/CmsBlockTextOnImage.vue +30 -0
  54. package/app/components/public/cms/element/CmsElementBuyBox.vue +145 -0
  55. package/app/components/public/cms/element/CmsElementCategoryNavigation.vue +53 -0
  56. package/{components → app/components}/public/cms/element/CmsElementCrossSelling.vue +3 -3
  57. package/{components → app/components}/public/cms/element/CmsElementImage.vue +52 -13
  58. package/app/components/public/cms/element/CmsElementImageGallery.vue +158 -0
  59. package/{components → app/components}/public/cms/element/CmsElementImageSlider.vue +2 -2
  60. package/{components → app/components}/public/cms/element/CmsElementProductBox.vue +2 -1
  61. package/app/components/public/cms/element/CmsElementProductDescriptionReviews.vue +217 -0
  62. package/{components → app/components}/public/cms/element/CmsElementProductListing.vue +23 -94
  63. package/app/components/public/cms/element/CmsElementProductName.vue +11 -0
  64. package/{components → app/components}/public/cms/element/CmsElementProductSlider.vue +4 -4
  65. package/{components → app/components}/public/cms/element/CmsElementText.vue +8 -2
  66. package/app/components/public/cms/element/SwProductListingPagination.vue +70 -0
  67. package/{components → app/components}/public/cms/section/CmsSectionDefault.vue +1 -1
  68. package/app/components/public/cms/section/CmsSectionSidebar.vue +36 -0
  69. package/app/components/public/cms/skeleton/ProductCardSkeleton.vue +28 -0
  70. package/app/components/ui/BaseButton.vue +99 -0
  71. package/app/components/ui/BaseIcon.vue +15 -0
  72. package/app/components/ui/Checkbox.vue +49 -0
  73. package/app/components/ui/CheckmarkIcon.vue +23 -0
  74. package/app/components/ui/ChevronIcon.vue +37 -0
  75. package/app/components/ui/ExclamationIcon.vue +11 -0
  76. package/app/components/ui/IconButton.vue +32 -0
  77. package/app/components/ui/RadioButton.vue +26 -0
  78. package/app/components/ui/StarIcon.vue +18 -0
  79. package/app/components/ui/SwitchButton.vue +100 -0
  80. package/app/components/ui/UserIcon.vue +11 -0
  81. package/app/components/ui/WishlistIcon.vue +20 -0
  82. package/app/composables/useImagePlaceholder.ts +27 -0
  83. package/{helpers → app/helpers}/clientOnly.ts +5 -0
  84. package/app/providers/shopware.test.ts +213 -0
  85. package/app/providers/shopware.ts +107 -0
  86. package/dist/index.d.mts +3 -3
  87. package/dist/index.d.ts +3 -3
  88. package/dist/index.mjs +2 -2
  89. package/index.d.ts +12 -0
  90. package/nuxt.config.ts +80 -6
  91. package/package.json +29 -21
  92. package/uno.config.ts +83 -0
  93. package/components/SwCategoryNavigation.vue +0 -44
  94. package/components/SwCategoryNavigationLink.vue +0 -57
  95. package/components/SwListingProductPrice.vue +0 -89
  96. package/components/SwProductCard.vue +0 -286
  97. package/components/SwProductListingFilter.vue +0 -42
  98. package/components/SwProductListingFilters.vue +0 -292
  99. package/components/listing-filters/SwFilterPrice.vue +0 -160
  100. package/components/listing-filters/SwFilterProperties.vue +0 -123
  101. package/components/listing-filters/SwFilterRating.vue +0 -101
  102. package/components/listing-filters/SwFilterShippingFree.vue +0 -104
  103. package/components/public/cms/block/CmsBlockImageFourColumn.vue +0 -29
  104. package/components/public/cms/block/CmsBlockImageHighlightRow.vue +0 -27
  105. package/components/public/cms/block/CmsBlockImageTextGallery.vue +0 -85
  106. package/components/public/cms/block/CmsBlockImageTextRow.vue +0 -43
  107. package/components/public/cms/block/CmsBlockImageThreeCover.vue +0 -27
  108. package/components/public/cms/block/CmsBlockImageTwoColumn.vue +0 -25
  109. package/components/public/cms/block/CmsBlockTextOnImage.vue +0 -20
  110. package/components/public/cms/element/CmsElementBuyBox.vue +0 -190
  111. package/components/public/cms/element/CmsElementCategoryNavigation.vue +0 -167
  112. package/components/public/cms/element/CmsElementImageGallery.vue +0 -249
  113. package/components/public/cms/element/CmsElementProductDescriptionReviews.vue +0 -123
  114. package/components/public/cms/element/CmsElementProductName.vue +0 -10
  115. package/components/public/cms/section/CmsSectionSidebar.vue +0 -41
  116. package/components/public/cms/skeleton/ProductCardSkeleton.vue +0 -44
  117. /package/{components → app/components}/SwMedia3D.vue +0 -0
  118. /package/{components → app/components}/SwProductGallery.vue +0 -0
  119. /package/{components → app/components}/SwProductPrice.vue +0 -0
  120. /package/{components → app/components}/SwProductUnits.vue +0 -0
  121. /package/{components → app/components}/SwSharedPrice.vue +0 -0
  122. /package/{components → app/components}/public/cms/CmsGenericBlock.md +0 -0
  123. /package/{components → app/components}/public/cms/CmsGenericBlock.vue +0 -0
  124. /package/{components → app/components}/public/cms/CmsGenericElement.md +0 -0
  125. /package/{components → app/components}/public/cms/CmsGenericElement.vue +0 -0
  126. /package/{components → app/components}/public/cms/CmsNoComponent.vue +0 -0
  127. /package/{components → app/components}/public/cms/CmsPage.md +0 -0
  128. /package/{components → app/components}/public/cms/block/CmsBlockCategoryNavigation.vue +0 -0
  129. /package/{components → app/components}/public/cms/block/CmsBlockCenterText.vue +0 -0
  130. /package/{components → app/components}/public/cms/block/CmsBlockCrossSelling.vue +0 -0
  131. /package/{components → app/components}/public/cms/block/CmsBlockCustomForm.vue +0 -0
  132. /package/{components → app/components}/public/cms/block/CmsBlockDefault.vue +0 -0
  133. /package/{components → app/components}/public/cms/block/CmsBlockForm.vue +0 -0
  134. /package/{components → app/components}/public/cms/block/CmsBlockHtml.vue +0 -0
  135. /package/{components → app/components}/public/cms/block/CmsBlockImage.vue +0 -0
  136. /package/{components → app/components}/public/cms/block/CmsBlockImageCover.vue +0 -0
  137. /package/{components → app/components}/public/cms/block/CmsBlockImageGallery.vue +0 -0
  138. /package/{components → app/components}/public/cms/block/CmsBlockImageSlider.vue +0 -0
  139. /package/{components → app/components}/public/cms/block/CmsBlockProductDescriptionReviews.vue +0 -0
  140. /package/{components → app/components}/public/cms/block/CmsBlockProductListing.vue +0 -0
  141. /package/{components → app/components}/public/cms/block/CmsBlockProductSlider.vue +0 -0
  142. /package/{components → app/components}/public/cms/block/CmsBlockText.vue +0 -0
  143. /package/{components → app/components}/public/cms/block/CmsBlockTextHero.vue +0 -0
  144. /package/{components → app/components}/public/cms/block/CmsBlockTextTeaser.vue +0 -0
  145. /package/{components → app/components}/public/cms/block/CmsBlockTextTeaserSection.vue +0 -0
  146. /package/{components → app/components}/public/cms/block/CmsBlockTextThreeColumn.vue +0 -0
  147. /package/{components → app/components}/public/cms/block/CmsBlockTextTwoColumn.vue +0 -0
  148. /package/{components → app/components}/public/cms/block/CmsBlockVimeoVideo.vue +0 -0
  149. /package/{components → app/components}/public/cms/block/CmsBlockYoutubeVideo.vue +0 -0
  150. /package/{components → app/components}/public/cms/element/CmsElementBuyBox.md +0 -0
  151. /package/{components → app/components}/public/cms/element/CmsElementCategoryNavigation.md +0 -0
  152. /package/{components → app/components}/public/cms/element/CmsElementCrossSelling.md +0 -0
  153. /package/{components → app/components}/public/cms/element/CmsElementCustomForm.md +0 -0
  154. /package/{components → app/components}/public/cms/element/CmsElementCustomForm.vue +0 -0
  155. /package/{components → app/components}/public/cms/element/CmsElementForm.md +0 -0
  156. /package/{components → app/components}/public/cms/element/CmsElementForm.vue +0 -0
  157. /package/{components → app/components}/public/cms/element/CmsElementHtml.vue +0 -0
  158. /package/{components → app/components}/public/cms/element/CmsElementImage.md +0 -0
  159. /package/{components → app/components}/public/cms/element/CmsElementImageGallery.md +0 -0
  160. /package/{components → app/components}/public/cms/element/CmsElementImageGallery3dPlaceholder.vue +0 -0
  161. /package/{components → app/components}/public/cms/element/CmsElementImageSlider.md +0 -0
  162. /package/{components → app/components}/public/cms/element/CmsElementManufacturerLogo.md +0 -0
  163. /package/{components → app/components}/public/cms/element/CmsElementManufacturerLogo.vue +0 -0
  164. /package/{components → app/components}/public/cms/element/CmsElementProductBox.md +0 -0
  165. /package/{components → app/components}/public/cms/element/CmsElementProductDescriptionReviews.md +0 -0
  166. /package/{components → app/components}/public/cms/element/CmsElementProductListing.md +0 -0
  167. /package/{components → app/components}/public/cms/element/CmsElementProductName.md +0 -0
  168. /package/{components → app/components}/public/cms/element/CmsElementProductSlider.md +0 -0
  169. /package/{components → app/components}/public/cms/element/CmsElementSidebarFilter.md +0 -0
  170. /package/{components → app/components}/public/cms/element/CmsElementSidebarFilter.vue +0 -0
  171. /package/{components → app/components}/public/cms/element/CmsElementText.md +0 -0
  172. /package/{components → app/components}/public/cms/element/CmsElementVimeoVideo.md +0 -0
  173. /package/{components → app/components}/public/cms/element/CmsElementVimeoVideo.vue +0 -0
  174. /package/{components → app/components}/public/cms/element/CmsElementYoutubeVideo.md +0 -0
  175. /package/{components → app/components}/public/cms/element/CmsElementYoutubeVideo.vue +0 -0
  176. /package/{components → app/components}/public/cms/section/CmsSectionDefault.md +0 -0
  177. /package/{components → app/components}/public/cms/section/CmsSectionSidebar.md +0 -0
  178. /package/{helpers → app/helpers}/html-to-vue/ast.ts +0 -0
  179. /package/{helpers → app/helpers}/html-to-vue/getOptionsFromNode.test.ts +0 -0
  180. /package/{helpers → app/helpers}/html-to-vue/getOptionsFromNode.ts +0 -0
  181. /package/{helpers → app/helpers}/html-to-vue/renderToHtml.ts +0 -0
  182. /package/{helpers → app/helpers}/html-to-vue/renderer.ts +0 -0
  183. /package/{helpers → app/helpers}/media/isSpatial.ts +0 -0
@@ -1,27 +0,0 @@
1
- <script setup lang="ts">
2
- import type { CmsBlockImageThreeCover } from "@shopware/composables";
3
- import { useCmsBlock } from "#imports";
4
-
5
- const props = defineProps<{
6
- content: CmsBlockImageThreeCover;
7
- }>();
8
-
9
- const { getSlotContent } = useCmsBlock(props.content);
10
-
11
- const leftContent = getSlotContent("left");
12
- const rightContent = getSlotContent("right");
13
- const centerContent = getSlotContent("center");
14
- </script>
15
- <template>
16
- <div class="cms-block-image-three-cover grid md:grid-cols-3 gap-10">
17
- <CmsGenericElement :content="leftContent" />
18
- <CmsGenericElement :content="centerContent" />
19
- <CmsGenericElement :content="rightContent" />
20
- </div>
21
- </template>
22
-
23
- <style scoped>
24
- .cms-block-image-three-cover .cms-element-image {
25
- @apply aspect-square object-cover;
26
- }
27
- </style>
@@ -1,25 +0,0 @@
1
- <script setup lang="ts">
2
- import type { CmsBlockImageTwoColumn } from "@shopware/composables";
3
- import { useCmsBlock } from "#imports";
4
-
5
- const props = defineProps<{
6
- content: CmsBlockImageTwoColumn;
7
- }>();
8
-
9
- const { getSlotContent } = useCmsBlock(props.content);
10
-
11
- const leftContent = getSlotContent("left");
12
- const rightContent = getSlotContent("right");
13
- </script>
14
- <template>
15
- <div class="cms-block-image-two-column grid md:grid-cols-2 gap-10">
16
- <CmsGenericElement :content="leftContent" />
17
- <CmsGenericElement :content="rightContent" />
18
- </div>
19
- </template>
20
-
21
- <style scoped>
22
- .cms-block-image-two-column .cms-element-image {
23
- @apply object-cover;
24
- }
25
- </style>
@@ -1,20 +0,0 @@
1
- <script setup lang="ts">
2
- import type { CmsBlockTextOnImage } from "@shopware/composables";
3
- import { useCmsBlock } from "#imports";
4
-
5
- const props = defineProps<{
6
- content: CmsBlockTextOnImage;
7
- }>();
8
-
9
- const { getSlotContent } = useCmsBlock(props.content);
10
-
11
- const slotContent = getSlotContent("content");
12
- </script>
13
-
14
- <template>
15
- <CmsGenericElement
16
- v-if="slotContent"
17
- :content="slotContent"
18
- class="cms-block-text-on-image min-h-md"
19
- />
20
- </template>
@@ -1,190 +0,0 @@
1
- <script setup lang="ts">
2
- import type { CmsElementBuyBox } from "@shopware/composables";
3
- import { useCmsTranslations } from "@shopware/composables";
4
- import { defu } from "defu";
5
- import { computed } from "vue";
6
- import {
7
- useCmsElementConfig,
8
- usePrice,
9
- useProduct,
10
- useProductPrice,
11
- useSessionContext,
12
- } from "#imports";
13
-
14
- const props = defineProps<{
15
- content: CmsElementBuyBox;
16
- }>();
17
-
18
- type Translations = {
19
- product: {
20
- previously: string;
21
- amount: string;
22
- price: {
23
- [key: string]: string;
24
- };
25
- to: string;
26
- from: string;
27
- content: string;
28
- pricesIncl: string;
29
- pricesExcl: string;
30
- deliveryTime: string;
31
- days: string;
32
- noAvailable: string;
33
- productNumber: string;
34
- };
35
- };
36
-
37
- let translations: Translations = {
38
- product: {
39
- previously: "Previously",
40
- amount: "Amount",
41
- price: {
42
- label: "Price",
43
- to: "To",
44
- from: "From",
45
- },
46
- to: "To",
47
- from: "From",
48
- content: "Content",
49
- pricesIncl: "Prices incl. VAT plus shipping costs",
50
- pricesExcl: "Prices excl. VAT plus shipping costs",
51
- deliveryTime: "Available, delivery time",
52
- days: "days",
53
- noAvailable: "No longer available",
54
- productNumber: "Product number",
55
- },
56
- };
57
-
58
- translations = defu(useCmsTranslations(), translations) as Translations;
59
-
60
- const { getConfigValue } = useCmsElementConfig(props.content);
61
- const alignment = computed(() => getConfigValue("alignment"));
62
-
63
- const { taxState, currency } = useSessionContext();
64
-
65
- const { product, changeVariant } = useProduct(
66
- props.content.data.product,
67
- props.content.data.configuratorSettings || [],
68
- );
69
-
70
- const { unitPrice, price, tierPrices, isListPrice } = useProductPrice(product);
71
- const regulationPrice = computed(() => price.value?.regulationPrice?.price);
72
- const { getFormattedPrice } = usePrice();
73
-
74
- const referencePrice = computed(
75
- () => product.value?.calculatedPrice?.referencePrice,
76
- );
77
- const productNumber = computed(() => product.value?.productNumber);
78
- const purchaseUnit = computed(() => product.value?.purchaseUnit);
79
- const unitName = computed(() => product.value?.unit?.name);
80
- const availableStock = computed(() => product.value?.availableStock ?? 0);
81
- const minPurchase = computed(() => product.value?.minPurchase ?? 0);
82
- const deliveryTime = computed(() => product.value?.deliveryTime);
83
- const restockTime = computed(() => product.value?.restockTime);
84
- </script>
85
- <template>
86
- <div
87
- v-if="product"
88
- :class="{
89
- 'h-full flex flex-col': true,
90
- 'justify-start': alignment === 'flex-start',
91
- 'justify-end': alignment === 'flex-end',
92
- 'justify-center': alignment === 'center',
93
- }"
94
- >
95
- <div>
96
- <div v-if="tierPrices.length <= 1">
97
- <SwSharedPrice
98
- v-if="isListPrice"
99
- class="text-1xl text-secondary-900 basis-2/6 justify-start line-through"
100
- :value="price?.listPrice?.price"
101
- />
102
- <SwSharedPrice
103
- v-if="unitPrice"
104
- class="text-3xl text-secondary-900 basis-2/6 justify-start"
105
- :class="{
106
- 'text-red': isListPrice,
107
- }"
108
- :value="unitPrice"
109
- />
110
- <div v-if="regulationPrice" class="text-xs flex text-secondary-500">
111
- {{ translations.product.previously }}
112
- <SwSharedPrice class="ml-1" :value="regulationPrice" />
113
- </div>
114
- </div>
115
- <div v-else>
116
- <table class="border-collapse table-auto w-full text-sm mb-8">
117
- <thead>
118
- <tr>
119
- <th
120
- class="border-b dark:border-secondary-600 font-medium p-4 pl-8 pt-0 pb-3 text-secondary-600 dark:text-secondary-200 text-left"
121
- >
122
- {{ translations.product.amount }}
123
- </th>
124
-
125
- <th
126
- class="border-b dark:border-secondary-600 font-medium p-4 pr-8 pt-0 pb-3 text-secondary-600 dark:text-secondary-200 text-left"
127
- >
128
- {{ translations.product.price.label }}
129
- </th>
130
- </tr>
131
- </thead>
132
- <tbody class="bg-white dark:bg-secondary-800">
133
- <tr v-for="(tierPrice, index) in tierPrices" :key="tierPrice.label">
134
- <td
135
- class="border-b border-secondary-100 dark:border-secondary-700 p-4 pl-8 font-medium text-secondary-500 dark:text-secondary-400"
136
- >
137
- <span v-if="index < tierPrices.length - 1">{{
138
- translations.product.to
139
- }}</span
140
- ><span v-else>{{ translations.product.from }}</span>
141
- {{ tierPrice.quantity }}
142
- </td>
143
- <td
144
- class="border-b border-secondary-100 dark:border-secondary-700 p-4 pr-8 font-medium text-current-500 dark:text-secondary-400"
145
- >
146
- {{ getFormattedPrice(tierPrice.unitPrice) }}
147
- </td>
148
- </tr>
149
- </tbody>
150
- </table>
151
- </div>
152
- <div v-if="purchaseUnit && unitName" class="mt-1">
153
- <span class="font-light"> {{ translations.product.content }}: </span>
154
- <span class="font-light"> {{ purchaseUnit }} {{ unitName }} </span>
155
- <span v-if="referencePrice" class="font-light">
156
- {{ currency?.symbol }} {{ referencePrice?.price }} / /
157
- {{ referencePrice?.referenceUnit }} {{ referencePrice?.unitName }}
158
- </span>
159
- </div>
160
- <span class="text-indigo-600">
161
- <template v-if="taxState === 'gross'">
162
- {{ translations.product.pricesIncl }}
163
- </template>
164
- <template v-else> {{ translations.product.pricesExcl }} </template>
165
- </span>
166
- </div>
167
- <div class="mt-4">
168
- <span v-if="availableStock >= minPurchase && deliveryTime"
169
- >{{ translations.product.deliveryTime }} {{ deliveryTime?.name }}
170
- </span>
171
- <span
172
- v-else-if="availableStock < minPurchase && deliveryTime && restockTime"
173
- >
174
- {{ translations.product.deliveryTime }} {{ restockTime }}
175
- {{ translations.product.days }} {{ deliveryTime?.name }}</span
176
- >
177
- <span v-else>{{ translations.product.noAvailable }}</span>
178
- </div>
179
- <SwVariantConfigurator @change="changeVariant" />
180
- <SwProductAddToCart :product="product" />
181
- <div class="mt-3 product-detail-ordernumber-container">
182
- <span class="font-bold text-secondary-900">
183
- {{ translations.product.productNumber }}:
184
- </span>
185
- <span>
186
- {{ productNumber }}
187
- </span>
188
- </div>
189
- </div>
190
- </template>
@@ -1,167 +0,0 @@
1
- <script setup lang="ts">
2
- import { useCmsTranslations } from "@shopware/composables";
3
- import { getTranslatedProperty } from "@shopware/helpers";
4
- import { defu } from "defu";
5
- import type { Ref } from "vue";
6
- import { onMounted, ref } from "vue";
7
- import { useCategory, useNavigation } from "#imports";
8
- import type { Schemas } from "#shopware";
9
-
10
- type Translations = {
11
- listing: {
12
- category: string;
13
- subCategory: string;
14
- subCategoryOf: string;
15
- loading: string;
16
- categories: string;
17
- subCategories: string;
18
- };
19
- };
20
-
21
- let translations: Translations = {
22
- listing: {
23
- category: "Category",
24
- subCategory: "Sub-category",
25
- subCategoryOf: "of",
26
- loading: "Loading ...",
27
- categories: "Categories",
28
- subCategories: "Sub-categories",
29
- },
30
- };
31
-
32
- translations = defu(useCmsTranslations(), translations) as Translations;
33
-
34
- const { category: activeCategory } = useCategory();
35
- const loading: Ref<boolean> = ref(true);
36
- const flagAllowSubcategories: boolean = true; // could be passed maybe as a prop in the future
37
- const categoryNavigation: Ref<Schemas["Category"][]> = ref([]);
38
-
39
- const currentCategoryId = activeCategory.value?.id ?? "main-navigation";
40
- const type = flagAllowSubcategories ? currentCategoryId : "main-navigation";
41
- const { loadNavigationElements } = useNavigation({
42
- type: type,
43
- });
44
- const removeChildrenIfNotActiveCategory = () => {
45
- const navigation: Schemas["Category"][] = JSON.parse(
46
- JSON.stringify(categoryNavigation.value),
47
- );
48
- return navigation?.map((navigationElement) => {
49
- navigationElement.children =
50
- activeCategory.value?.id === navigationElement.id
51
- ? navigationElement.children
52
- : [];
53
- return navigationElement;
54
- });
55
- };
56
-
57
- onMounted(async () => {
58
- // depth 0 means, we load only first level of categories, depth 1 means we load first and second level of categories ...
59
- const depth = flagAllowSubcategories ? 0 : 1;
60
- categoryNavigation.value = await loadNavigationElements({ depth });
61
- if (!flagAllowSubcategories) {
62
- categoryNavigation.value = removeChildrenIfNotActiveCategory();
63
- }
64
- loading.value = false;
65
- });
66
- </script>
67
- <template>
68
- <div>
69
- <div
70
- v-if="categoryNavigation && categoryNavigation.length"
71
- class="cms-element-category-navigation max-w-screen-xl mx-auto"
72
- >
73
- <h2
74
- v-if="categoryNavigation.length > 0 && !flagAllowSubcategories"
75
- class="text-3xl tracking-tight text-secondary-900 m-0 px-5"
76
- >
77
- {{
78
- categoryNavigation.length > 1
79
- ? translations.listing.categories
80
- : translations.listing.category
81
- }}
82
- </h2>
83
- <h2
84
- v-if="categoryNavigation.length > 0 && flagAllowSubcategories"
85
- class="text-3xl tracking-tight text-secondary-900 m-0 px-5"
86
- >
87
- {{
88
- categoryNavigation.length > 1
89
- ? translations.listing.subCategories
90
- : translations.listing.subCategory
91
- }}
92
- {{ translations.listing.subCategoryOf }}
93
- {{ getTranslatedProperty(activeCategory, "name") }}
94
- </h2>
95
- <SwCategoryNavigation
96
- :level="0"
97
- :elements="categoryNavigation"
98
- :active-category="activeCategory"
99
- />
100
- </div>
101
- <div v-if="loading">
102
- <div class="px-5">
103
- <h2 class="text-3xl tracking-tight text-secondary-900 m-0 px-5 pl-0">
104
- {{ translations.listing.loading }}
105
- </h2>
106
- <div
107
- class="border border-secondary-200 shadow rounded-md p-4 max-w-screen-xl mx-auto"
108
- >
109
- <div class="animate-pulse flex space-x-4">
110
- <div class="flex-1 space-y-6 py-1">
111
- <div class="space-y-3">
112
- <div class="grid grid-cols-3 gap-4">
113
- <div class="h-2 bg-light-200 rounded col-span-2"></div>
114
- <div class="h-2 bg-light-200 rounded col-span-1"></div>
115
- </div>
116
- <div class="h-2 bg-secondary-200 rounded"></div>
117
- </div>
118
- <div class="space-y-3">
119
- <div class="grid grid-cols-3 gap-4">
120
- <div class="h-2 bg-light-200 rounded col-span-1"></div>
121
- <div class="h-2 bg-light-200 rounded col-span-2"></div>
122
- </div>
123
- <div class="h-2 bg-secondary-200 rounded"></div>
124
- </div>
125
- </div>
126
- </div>
127
- <div class="animate-pulse flex space-x-4">
128
- <div class="flex-1 space-y-6 py-4">
129
- <div class="space-y-3">
130
- <div class="grid grid-cols-3 gap-4">
131
- <div class="h-2 bg-light-200 rounded col-span-2"></div>
132
- <div class="h-2 bg-light-200 rounded col-span-1"></div>
133
- </div>
134
- <div class="h-2 bg-secondary-200 rounded"></div>
135
- </div>
136
- <div class="space-y-3">
137
- <div class="grid grid-cols-3 gap-4">
138
- <div class="h-2 bg-light-200 rounded col-span-1"></div>
139
- <div class="h-2 bg-light-200 rounded col-span-2"></div>
140
- </div>
141
- <div class="h-2 bg-secondary-200 rounded"></div>
142
- </div>
143
- </div>
144
- </div>
145
- <div class="animate-pulse flex space-x-4">
146
- <div class="flex-1 space-y-6 pt-4">
147
- <div class="space-y-3">
148
- <div class="grid grid-cols-6 gap-4">
149
- <div class="h-2 bg-light-200 rounded col-span-1"></div>
150
- <div class="h-2 bg-light-200 rounded col-span-5"></div>
151
- </div>
152
- <div class="h-2 bg-secondary-200 rounded"></div>
153
- </div>
154
- <div class="space-y-3">
155
- <div class="grid grid-cols-3 gap-4">
156
- <div class="h-2 bg-light-200 rounded col-span-2"></div>
157
- <div class="h-2 bg-light-200 rounded col-span-1"></div>
158
- </div>
159
- <div class="h-2 bg-secondary-200 rounded"></div>
160
- </div>
161
- </div>
162
- </div>
163
- </div>
164
- </div>
165
- </div>
166
- </div>
167
- </template>
@@ -1,249 +0,0 @@
1
- <script setup lang="ts">
2
- import type { CmsElementImageGallery } from "@shopware/composables";
3
- import { computed, onMounted, ref, useTemplateRef } from "vue";
4
- import { useCmsElementConfig } from "#imports";
5
- import { isSpatial } from "../../../../helpers/media/isSpatial";
6
-
7
- const props = withDefaults(
8
- defineProps<{
9
- content: CmsElementImageGallery;
10
- slidesToShow?: number;
11
- slidesToScroll?: number;
12
- }>(),
13
- {
14
- slidesToShow: 5,
15
- slidesToScroll: 4,
16
- },
17
- );
18
-
19
- const { getConfigValue } = useCmsElementConfig(props.content);
20
-
21
- const speed = ref<number>(300);
22
- const currentIndex = ref(0);
23
- const currentThumb = ref(0);
24
- const imageSlider = useTemplateRef("imageSlider");
25
- const imageThumbsTrack = useTemplateRef("imageThumbsTrack");
26
- const isLoading = ref(true);
27
- const imageThumbsTrackStyle = ref({});
28
- const imageThumbs = useTemplateRef("imageThumbs");
29
- const imageThumbsStyle = ref({});
30
- const mediaGallery = computed(() => props.content.data?.sliderItems ?? []);
31
- const galleryPosition = computed<string>(
32
- () => getConfigValue("galleryPosition") ?? "left",
33
- );
34
- const scrollPx = ref(0);
35
-
36
- onMounted(() => {
37
- initThumbs();
38
- });
39
-
40
- function initThumbs() {
41
- if (imageThumbsTrack.value) {
42
- setTimeout(() => {
43
- if (galleryPosition.value === "left") {
44
- const clientHeight = imageThumbsTrack.value?.clientHeight ?? 0;
45
- scrollPx.value = clientHeight / mediaGallery.value.length;
46
- imageThumbsStyle.value = {
47
- height: `${scrollPx.value * +props.slidesToShow}px`,
48
- };
49
- } else {
50
- const clientWidth = imageThumbs.value?.clientWidth ?? 0;
51
- scrollPx.value = clientWidth / props.slidesToShow;
52
- imageThumbsTrackStyle.value = {
53
- width: `${scrollPx.value * mediaGallery.value.length}px`,
54
- };
55
- }
56
- isLoading.value = false;
57
- }, 100);
58
- }
59
- }
60
-
61
- function changeCover(i: number) {
62
- if (i === currentIndex.value) return;
63
- imageSlider.value?.goToSlide(i);
64
- }
65
-
66
- function handleChangeSlide(e: number) {
67
- currentIndex.value = e;
68
- if (currentIndex.value > currentThumb.value + props.slidesToShow - 1) {
69
- move("next", currentIndex.value);
70
- return;
71
- }
72
- if (currentIndex.value < currentThumb.value) {
73
- move("previous", currentIndex.value);
74
- return;
75
- }
76
- }
77
-
78
- function move(type: "next" | "previous", specificIndex?: number | string) {
79
- let step: number;
80
- const index =
81
- typeof specificIndex !== "number"
82
- ? Number.parseInt(specificIndex as string)
83
- : specificIndex;
84
- if (index >= 0) {
85
- if (type === "next") {
86
- step =
87
- index + props.slidesToScroll < mediaGallery.value.length
88
- ? index
89
- : mediaGallery.value.length - props.slidesToShow;
90
- } else {
91
- step =
92
- index - props.slidesToScroll > 0 ? index - props.slidesToScroll : 0;
93
- }
94
- } else {
95
- if (type === "next") {
96
- step =
97
- currentThumb.value + props.slidesToShow - 1 + props.slidesToScroll <
98
- mediaGallery.value.length
99
- ? currentThumb.value + props.slidesToScroll
100
- : mediaGallery.value.length - props.slidesToShow;
101
- } else {
102
- step =
103
- currentThumb.value - props.slidesToScroll > 0
104
- ? currentThumb.value - props.slidesToScroll
105
- : 0;
106
- }
107
- }
108
- currentThumb.value = step;
109
- let xAxis = 0;
110
- let yAxis = 0;
111
- if (galleryPosition.value === "left") {
112
- yAxis = scrollPx.value * currentThumb.value;
113
- } else {
114
- xAxis = scrollPx.value * currentThumb.value;
115
- }
116
- imageThumbsTrackStyle.value = {
117
- ...imageThumbsTrackStyle.value,
118
- transform: `translate3d(-${xAxis}px, -${yAxis}px, 0px)`,
119
- transition: `transform ${speed.value}ms ease 0s`,
120
- };
121
- }
122
-
123
- function previous() {
124
- if (currentThumb.value <= 0) {
125
- return;
126
- }
127
- move("previous");
128
- }
129
-
130
- function next() {
131
- if (currentThumb.value + props.slidesToShow >= mediaGallery.value.length) {
132
- return;
133
- }
134
- move("next");
135
- }
136
- </script>
137
-
138
- <template>
139
- <div
140
- :class="{
141
- 'opacity-0': isLoading,
142
- 'flex gap-10': true,
143
- 'flex-col-reverse': galleryPosition === 'underneath',
144
- }"
145
- >
146
- <div
147
- :class="{
148
- 'hidden lg:flex basis-20 relative flex-col items-center':
149
- galleryPosition === 'left',
150
- 'flex relative w-full': galleryPosition === 'underneath',
151
- }"
152
- >
153
- <button
154
- v-if="mediaGallery.length > slidesToShow"
155
- class="disabled:opacity-10 p-1"
156
- aria-label="Previous image"
157
- @click="previous"
158
- >
159
- <div
160
- class="h-7 w-7"
161
- :class="{
162
- 'i-carbon-chevron-up': galleryPosition === 'left',
163
- 'i-carbon-chevron-left': galleryPosition !== 'left',
164
- }"
165
- />
166
- </button>
167
- <span class="sr-only">Previous image</span>
168
- <div
169
- ref="imageThumbs"
170
- class="overflow-hidden -my-2.5"
171
- :style="imageThumbsStyle"
172
- >
173
- <div
174
- ref="imageThumbsTrack"
175
- :class="{
176
- flex: true,
177
- 'flex-col': galleryPosition === 'left',
178
- }"
179
- :style="imageThumbsTrackStyle"
180
- >
181
- <div
182
- v-for="(image, i) in mediaGallery"
183
- :key="image.media.url"
184
- :class="{
185
- 'py-2.5': galleryPosition === 'left',
186
- 'flex-1 px-2.5': galleryPosition === 'underneath',
187
- }"
188
- >
189
- <div
190
- class="w-20 h-20 overflow-hidden cursor-pointer p-1 border-secondary-200 rounded transition duration-150 ease-in-out"
191
- :class="{
192
- border: i !== currentIndex,
193
- 'border-indigo-500 border-3': i === currentIndex,
194
- }"
195
- @click="() => changeCover(i)"
196
- >
197
- <div v-if="isSpatial(image.media)" class="h-full relative">
198
- <CmsElementImageGallery3dPlaceholder
199
- class="w-full h-full object-center"
200
- />
201
- <span
202
- class="absolute bottom-0 text-sm bg-gray rounded px-1 text-white"
203
- >
204
- 3D</span
205
- >
206
- </div>
207
- <img
208
- v-else
209
- loading="lazy"
210
- :src="image.media.url"
211
- class="w-full h-full object-center object-cover"
212
- alt="Product image"
213
- />
214
- </div>
215
- </div>
216
- </div>
217
- </div>
218
- <button
219
- v-if="mediaGallery.length > slidesToShow"
220
- class="disabled:opacity-10 p-1"
221
- aria-label="Next image"
222
- @click="next"
223
- >
224
- <span class="sr-only">Next image</span>
225
- <div
226
- class="h-7 w-7"
227
- :class="{
228
- 'i-carbon-chevron-down': galleryPosition === 'left',
229
- 'i-carbon-chevron-right': galleryPosition !== 'left',
230
- }"
231
- />
232
- </button>
233
- </div>
234
- <div class="flex-1 overflow-hidden">
235
- <SwSlider
236
- ref="imageSlider"
237
- :config="props.content.config"
238
- @change-slide="handleChangeSlide"
239
- >
240
- <CmsElementImage
241
- v-for="image of mediaGallery"
242
- :key="image.media.url"
243
- :image-gallery="true"
244
- :content="{ data: image, config: props.content.config } as any"
245
- />
246
- </SwSlider>
247
- </div>
248
- </div>
249
- </template>