@shopware/cms-base-layer 3.1.0 → 4.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 +79 -23
- package/app/app.config.ts +3 -3
- package/app/assets/css/rich-text.css +80 -0
- package/app/components/SwCategoryNavigationLink.vue +4 -5
- package/app/components/SwContactForm.vue +2 -1
- package/app/components/SwFilterChips.vue +29 -7
- package/app/components/SwMedia3D.vue +8 -3
- package/app/components/SwNewsletterForm.vue +2 -1
- package/app/components/SwProductAddToCart.vue +27 -4
- package/app/components/SwProductCard.vue +26 -4
- package/app/components/SwProductCardDetails.vue +9 -2
- package/app/components/SwProductCardImage.vue +5 -1
- package/app/components/SwProductListingFilter.vue +6 -1
- package/app/components/SwProductListingFilters.vue +13 -189
- package/app/components/SwProductListingFiltersHorizontal.vue +20 -172
- package/app/components/SwStockInfo.vue +5 -2
- package/app/components/SwVariantConfigurator.vue +8 -3
- package/app/components/listing-filters/SwFilterCategories.vue +158 -0
- package/app/components/public/cms/CmsBlockSpatialViewer.vue +7 -7
- package/app/components/public/cms/CmsGenericBlock.vue +6 -10
- package/app/components/public/cms/CmsGenericElement.md +11 -5
- package/app/components/public/cms/CmsGenericElement.vue +17 -11
- package/app/components/public/cms/FrontendAccountCustomerGroupRegistrationPage.vue +1 -1
- package/app/components/public/cms/block/CmsBlockAppRenderer.vue +48 -0
- package/app/components/public/cms/block/CmsBlockCategoryHeading.vue +19 -0
- package/app/components/public/cms/block/CmsBlockCategoryNavigation.vue +3 -2
- package/app/components/public/cms/block/CmsBlockCenterText.vue +5 -4
- package/app/components/public/cms/block/CmsBlockCrossSelling.vue +3 -2
- package/app/components/public/cms/block/CmsBlockCustomForm.vue +3 -2
- package/app/components/public/cms/block/CmsBlockForm.vue +3 -2
- package/app/components/public/cms/block/CmsBlockGalleryBuybox.vue +4 -3
- package/app/components/public/cms/block/CmsBlockImage.vue +3 -2
- package/app/components/public/cms/block/CmsBlockImageBubbleRow.vue +5 -4
- package/app/components/public/cms/block/CmsBlockImageCover.vue +3 -2
- package/app/components/public/cms/block/CmsBlockImageFourColumn.vue +6 -5
- package/app/components/public/cms/block/CmsBlockImageGallery.vue +3 -2
- package/app/components/public/cms/block/CmsBlockImageGalleryBig.vue +3 -2
- package/app/components/public/cms/block/CmsBlockImageHighlightRow.vue +5 -4
- package/app/components/public/cms/block/CmsBlockImageSimpleGrid.vue +5 -4
- package/app/components/public/cms/block/CmsBlockImageSlider.vue +3 -2
- package/app/components/public/cms/block/CmsBlockImageText.vue +4 -3
- package/app/components/public/cms/block/CmsBlockImageTextBubble.vue +8 -7
- package/app/components/public/cms/block/CmsBlockImageTextCover.vue +4 -3
- package/app/components/public/cms/block/CmsBlockImageTextGallery.vue +39 -16
- package/app/components/public/cms/block/CmsBlockImageTextRow.vue +8 -7
- package/app/components/public/cms/block/CmsBlockImageThreeColumn.vue +5 -4
- package/app/components/public/cms/block/CmsBlockImageThreeCover.vue +5 -4
- package/app/components/public/cms/block/CmsBlockImageTwoColumn.vue +4 -3
- package/app/components/public/cms/block/CmsBlockProductDescriptionReviews.vue +3 -2
- package/app/components/public/cms/block/CmsBlockProductHeading.vue +4 -3
- package/app/components/public/cms/block/CmsBlockProductListing.vue +3 -2
- package/app/components/public/cms/block/CmsBlockProductThreeColumn.vue +5 -4
- package/app/components/public/cms/block/CmsBlockSidebarFilter.vue +7 -5
- package/app/components/public/cms/block/CmsBlockText.vue +3 -2
- package/app/components/public/cms/block/CmsBlockTextHero.vue +3 -2
- package/app/components/public/cms/block/CmsBlockTextOnImage.vue +3 -2
- package/app/components/public/cms/block/CmsBlockTextThreeColumn.vue +5 -4
- package/app/components/public/cms/block/CmsBlockTextTwoColumn.vue +4 -3
- package/app/components/public/cms/block/CmsBlockVideo.vue +19 -0
- package/app/components/public/cms/block/CmsBlockVimeoVideo.vue +3 -2
- package/app/components/public/cms/block/CmsBlockYoutubeVideo.vue +3 -2
- package/app/components/public/cms/element/CmsElementBuyBox.vue +4 -1
- package/app/components/public/cms/element/CmsElementCategoryName.md +8 -0
- package/app/components/public/cms/element/CmsElementCategoryName.vue +35 -0
- package/app/components/public/cms/element/CmsElementCrossSelling.vue +2 -1
- package/app/components/public/cms/element/CmsElementHtml.vue +1 -1
- package/app/components/public/cms/element/CmsElementImage.vue +41 -1
- package/app/components/public/cms/element/CmsElementImageGallery.vue +2 -1
- package/app/components/public/cms/element/CmsElementProductDescriptionReviews.vue +2 -2
- package/app/components/public/cms/element/CmsElementProductListing.vue +58 -25
- package/app/components/public/cms/element/CmsElementText.md +77 -0
- package/app/components/public/cms/element/CmsElementText.vue +15 -41
- package/app/components/public/cms/element/CmsElementVideo.md +27 -0
- package/app/components/public/cms/element/CmsElementVideo.vue +193 -0
- package/app/components/public/cms/section/CmsSectionSidebar.vue +4 -4
- package/app/composables/useListingFilters.ts +189 -0
- package/app/composables/useSelectedListingFilters.ts +46 -0
- package/app/helpers/cms/getAppRendererLayout.test.ts +93 -0
- package/app/helpers/cms/getAppRendererLayout.ts +36 -0
- package/app/helpers/cms/getCategoryNameContent.test.ts +57 -0
- package/app/helpers/cms/getCategoryNameContent.ts +15 -0
- package/app/helpers/cms/getTextElementContent.test.ts +79 -0
- package/app/helpers/cms/getTextElementContent.ts +37 -0
- package/app/helpers/cms/getVideoElementOptions.test.ts +384 -0
- package/app/helpers/cms/getVideoElementOptions.ts +142 -0
- package/app/helpers/html-to-vue/renderToHtml.test.ts +71 -0
- package/app/helpers/html-to-vue/renderToHtml.ts +5 -1
- package/app/helpers/html-to-vue/renderer.ts +13 -4
- package/app/helpers/product/shouldShowBuyButton.test.ts +21 -0
- package/app/helpers/product/shouldShowBuyButton.ts +6 -0
- package/app/utils/{useSelectedListingFilters.test.ts → listingFilterState.test.ts} +14 -4
- package/app/utils/{useSelectedListingFilters.ts → listingFilterState.ts} +4 -33
- package/app/utils/listingFilters.test.ts +39 -0
- package/app/utils/listingFilters.ts +19 -0
- package/component-dirs.json +20 -0
- package/index.d.ts +2 -2
- package/nuxt.config.ts +11 -28
- package/package.json +23 -26
- package/dist/index.d.mts +0 -5
- package/dist/index.d.ts +0 -5
- package/dist/index.mjs +0 -31
- package/index.cjs +0 -7
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<script
|
|
2
|
+
setup
|
|
3
|
+
lang="ts"
|
|
4
|
+
generic="
|
|
5
|
+
ListingFilter extends {
|
|
6
|
+
code: string;
|
|
7
|
+
label: string;
|
|
8
|
+
name: string;
|
|
9
|
+
entities?: Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
translated?: { name?: string };
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
"
|
|
16
|
+
>
|
|
17
|
+
import { useCmsTranslations } from "@shopware/composables";
|
|
18
|
+
import { excludeRootCategory, getTranslatedProperty } from "@shopware/helpers";
|
|
19
|
+
import { defu } from "defu";
|
|
20
|
+
import { computed, ref } from "vue";
|
|
21
|
+
|
|
22
|
+
import { useSessionContext } from "#imports";
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
filter,
|
|
26
|
+
selectedFilters,
|
|
27
|
+
displayMode = "accordion",
|
|
28
|
+
} = defineProps<{
|
|
29
|
+
filter: ListingFilter;
|
|
30
|
+
selectedFilters: {
|
|
31
|
+
categories?: string[];
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
};
|
|
34
|
+
displayMode?: "accordion" | "dropdown";
|
|
35
|
+
}>();
|
|
36
|
+
|
|
37
|
+
const emits =
|
|
38
|
+
defineEmits<
|
|
39
|
+
(e: "select-value", value: { code: string; value: unknown }) => void
|
|
40
|
+
>();
|
|
41
|
+
|
|
42
|
+
type Translations = {
|
|
43
|
+
listing: {
|
|
44
|
+
categories: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
let translations: Translations = {
|
|
49
|
+
listing: {
|
|
50
|
+
categories: "Categories",
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
translations = defu(useCmsTranslations(), translations) as Translations;
|
|
55
|
+
|
|
56
|
+
const { sessionContext } = useSessionContext();
|
|
57
|
+
|
|
58
|
+
const categoryOptions = computed(() =>
|
|
59
|
+
excludeRootCategory(
|
|
60
|
+
filter.entities,
|
|
61
|
+
sessionContext.value?.salesChannel?.navigationCategoryId,
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const isFilterVisible = ref<boolean>(false);
|
|
66
|
+
const toggle = () => {
|
|
67
|
+
isFilterVisible.value = !isFilterVisible.value;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const selectedIds = computed(() => selectedFilters?.categories || []);
|
|
71
|
+
|
|
72
|
+
const isChecked = (id: string) => selectedIds.value.includes(id);
|
|
73
|
+
|
|
74
|
+
const selectValue = (id: string) => {
|
|
75
|
+
emits("select-value", {
|
|
76
|
+
code: filter.code,
|
|
77
|
+
value: id,
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<template>
|
|
83
|
+
<div
|
|
84
|
+
v-if="categoryOptions.length"
|
|
85
|
+
class="self-stretch flex flex-col justify-start items-start gap-4"
|
|
86
|
+
>
|
|
87
|
+
<!-- Accordion header (only in accordion mode) -->
|
|
88
|
+
<template v-if="displayMode === 'accordion'">
|
|
89
|
+
<div class="self-stretch flex flex-col justify-center items-center">
|
|
90
|
+
<div
|
|
91
|
+
class="self-stretch py-3 border-b border-outline-outline-variant inline-flex justify-between items-center gap-1 cursor-pointer"
|
|
92
|
+
@click="toggle"
|
|
93
|
+
role="button"
|
|
94
|
+
tabindex="0"
|
|
95
|
+
:aria-expanded="isFilterVisible"
|
|
96
|
+
:aria-controls="filter.code"
|
|
97
|
+
:aria-label="translations.listing.categories"
|
|
98
|
+
@keydown.enter="toggle"
|
|
99
|
+
@keydown.space.prevent="toggle"
|
|
100
|
+
>
|
|
101
|
+
<div class="flex-1 flex items-center gap-2.5">
|
|
102
|
+
<div
|
|
103
|
+
class="flex-1 text-surface-on-surface text-base font-bold leading-normal text-left"
|
|
104
|
+
>
|
|
105
|
+
{{ translations.listing.categories }}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
<span class="flex items-center justify-center" aria-hidden="true">
|
|
109
|
+
<SwChevronIcon
|
|
110
|
+
:direction="isFilterVisible ? 'up' : 'down'"
|
|
111
|
+
:size="24"
|
|
112
|
+
/>
|
|
113
|
+
</span>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</template>
|
|
117
|
+
|
|
118
|
+
<!-- Filter content -->
|
|
119
|
+
<transition name="filter-collapse">
|
|
120
|
+
<div
|
|
121
|
+
v-if="isFilterVisible || displayMode === 'dropdown'"
|
|
122
|
+
:id="filter.code"
|
|
123
|
+
class="self-stretch flex flex-col justify-start items-start gap-4"
|
|
124
|
+
>
|
|
125
|
+
<fieldset
|
|
126
|
+
class="self-stretch flex flex-col justify-start items-start gap-4"
|
|
127
|
+
>
|
|
128
|
+
<legend class="sr-only">{{ translations.listing.categories }}</legend>
|
|
129
|
+
<label
|
|
130
|
+
v-for="option in categoryOptions"
|
|
131
|
+
:key="option.id"
|
|
132
|
+
class="self-stretch inline-flex justify-start items-start gap-2 cursor-pointer"
|
|
133
|
+
>
|
|
134
|
+
<div
|
|
135
|
+
class="w-4 self-stretch pt-[3px] flex justify-start items-start gap-2.5"
|
|
136
|
+
>
|
|
137
|
+
<SwCheckbox
|
|
138
|
+
:model-value="isChecked(option.id)"
|
|
139
|
+
@update:model-value="() => selectValue(option.id)"
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
<div
|
|
143
|
+
class="flex-1 inline-flex flex-col justify-start items-start gap-0.5"
|
|
144
|
+
>
|
|
145
|
+
<div class="inline-flex justify-start items-center gap-1">
|
|
146
|
+
<div
|
|
147
|
+
class="flex-1 text-surface-on-surface text-base font-normal leading-normal"
|
|
148
|
+
>
|
|
149
|
+
{{ getTranslatedProperty(option, "name") }}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</label>
|
|
154
|
+
</fieldset>
|
|
155
|
+
</div>
|
|
156
|
+
</transition>
|
|
157
|
+
</div>
|
|
158
|
+
</template>
|
|
@@ -12,13 +12,13 @@ const props = defineProps<{
|
|
|
12
12
|
content: Schemas["CmsBlock"];
|
|
13
13
|
}>();
|
|
14
14
|
|
|
15
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
16
|
-
const slotContent = getSlotContent("default");
|
|
15
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
16
|
+
const slotContent = computed(() => getSlotContent("default"));
|
|
17
17
|
|
|
18
18
|
function getConfigValue(key: string): unknown {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const slot = slotContent.value;
|
|
20
|
+
if (!slot?.config) return null;
|
|
21
|
+
const configEntry = slot.config[key as keyof typeof slot.config];
|
|
22
22
|
if (
|
|
23
23
|
configEntry &&
|
|
24
24
|
typeof configEntry === "object" &&
|
|
@@ -31,8 +31,8 @@ function getConfigValue(key: string): unknown {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const modelUrl = computed(() => {
|
|
34
|
-
if (slotContent?.data) {
|
|
35
|
-
const data = slotContent.data as unknown as Schemas["Media"];
|
|
34
|
+
if (slotContent.value?.data) {
|
|
35
|
+
const data = slotContent.value.data as unknown as Schemas["Media"];
|
|
36
36
|
if (data?.url && typeof data.url === "string") {
|
|
37
37
|
return data.url;
|
|
38
38
|
}
|
|
@@ -17,22 +17,17 @@ const props = defineProps<{
|
|
|
17
17
|
|
|
18
18
|
const appConfig = useTypedAppConfig();
|
|
19
19
|
|
|
20
|
+
// Plain values, not refs: custom elements inject these as a `number` and a
|
|
21
|
+
// `string` (see the README), so providing a ref here would break them.
|
|
20
22
|
const slotCount = props.content.slots?.length || 1;
|
|
21
23
|
provide("cms-block-slot-count", slotCount);
|
|
22
24
|
provide("cms-image-sizes", getImageSizes(slotCount, appConfig.imageSizes));
|
|
23
25
|
|
|
24
26
|
const DynamicRender = () => {
|
|
25
|
-
const {
|
|
26
|
-
|
|
27
|
-
componentName,
|
|
28
|
-
isResolved,
|
|
29
|
-
componentNameToResolve,
|
|
30
|
-
} = resolveCmsComponent(props.content);
|
|
27
|
+
const { resolvedComponent, componentName, componentNameToResolve } =
|
|
28
|
+
resolveCmsComponent(props.content);
|
|
31
29
|
|
|
32
30
|
if (resolvedComponent) {
|
|
33
|
-
if (!isResolved)
|
|
34
|
-
return h("div", {}, `Problem resolving component: ${componentName}`);
|
|
35
|
-
|
|
36
31
|
const { cssClasses, layoutStyles } = getCmsLayoutConfiguration(
|
|
37
32
|
props.content,
|
|
38
33
|
);
|
|
@@ -75,7 +70,8 @@ const DynamicRender = () => {
|
|
|
75
70
|
);
|
|
76
71
|
return h(resolveComponent("CmsNoComponent"), { content: props.content });
|
|
77
72
|
}
|
|
78
|
-
|
|
73
|
+
// Production: a block type with no component renders nothing.
|
|
74
|
+
return null;
|
|
79
75
|
};
|
|
80
76
|
</script>
|
|
81
77
|
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
Renders an Element type structure
|
|
1
|
+
Renders an Element type structure.
|
|
2
|
+
|
|
3
|
+
`content` is optional: `getSlotContent()` returns `undefined` for a slot the block
|
|
4
|
+
does not carry, and this component renders nothing in that case.
|
|
2
5
|
|
|
3
6
|
Example usage:
|
|
4
7
|
|
|
5
|
-
```vue{
|
|
8
|
+
```vue{22,25}
|
|
6
9
|
<script setup lang="ts">
|
|
7
10
|
import type { CmsBlockGalleryBuybox } from "@shopware/composables";
|
|
11
|
+
import { computed } from "vue";
|
|
8
12
|
import { useCmsBlock } from "#imports";
|
|
9
13
|
|
|
10
14
|
const props = defineProps<{
|
|
11
15
|
content: CmsBlockGalleryBuybox;
|
|
12
16
|
}>();
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
18
|
+
// Pass a getter so the lookups follow a replaced block, and read them through
|
|
19
|
+
// computeds so each one re-runs when it does.
|
|
20
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
21
|
+
const rightContent = computed(() => getSlotContent("right"));
|
|
22
|
+
const leftContent = computed(() => getSlotContent("left"));
|
|
17
23
|
</script>
|
|
18
24
|
|
|
19
25
|
<template>
|
|
@@ -6,20 +6,24 @@ import { h, resolveComponent } from "vue";
|
|
|
6
6
|
import type { Schemas } from "#shopware";
|
|
7
7
|
|
|
8
8
|
const props = defineProps<{
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* A block asks for its slots by name, and a block does not have to carry
|
|
11
|
+
* every slot its layout allows, so this can be undefined.
|
|
12
|
+
*/
|
|
13
|
+
content?: Schemas["CmsSlot"];
|
|
10
14
|
}>();
|
|
11
15
|
|
|
12
16
|
const DynamicRender = () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (resolvedComponent) {
|
|
20
|
-
if (!isResolved)
|
|
21
|
-
return h("div", {}, `Problem resolving component: ${componentName}`);
|
|
17
|
+
// Nothing to render for a slot the block does not carry. An element would
|
|
18
|
+
// take the class the call site passes and occupy a cell of its own wherever a
|
|
19
|
+
// block renders this straight into a grid, as CmsBlockCenterText does. A
|
|
20
|
+
// wrapper the block puts around the call site renders either way — sizing
|
|
21
|
+
// that wrapper for an absent slot is the block's business, not this one's.
|
|
22
|
+
if (!props.content) return null;
|
|
22
23
|
|
|
24
|
+
const { resolvedComponent, componentName, componentNameToResolve } =
|
|
25
|
+
resolveCmsComponent(props.content);
|
|
26
|
+
if (resolvedComponent) {
|
|
23
27
|
const { cssClasses, layoutStyles } = getCmsLayoutConfiguration(
|
|
24
28
|
props.content,
|
|
25
29
|
);
|
|
@@ -35,7 +39,9 @@ const DynamicRender = () => {
|
|
|
35
39
|
);
|
|
36
40
|
return h(resolveComponent("CmsNoComponent"), { content: props.content });
|
|
37
41
|
}
|
|
38
|
-
|
|
42
|
+
// Production: an element type with no component renders nothing, for the same
|
|
43
|
+
// reason the missing slot above does.
|
|
44
|
+
return null;
|
|
39
45
|
};
|
|
40
46
|
</script>
|
|
41
47
|
|
|
@@ -38,7 +38,7 @@ useSeoMeta({
|
|
|
38
38
|
>
|
|
39
39
|
<div
|
|
40
40
|
v-if="registrationResponse?.translated.registrationIntroduction"
|
|
41
|
-
class="px-6 sm:px-4 mb-6"
|
|
41
|
+
class="cms-element-text px-6 sm:px-4 mb-6"
|
|
42
42
|
v-html="registrationResponse.translated.registrationIntroduction"
|
|
43
43
|
/>
|
|
44
44
|
<AccountRegistrationForm
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { CmsBlockAppRenderer } from "@shopware/composables";
|
|
3
|
+
import { computed, getCurrentInstance } from "vue";
|
|
4
|
+
import type { Component, CSSProperties } from "vue";
|
|
5
|
+
|
|
6
|
+
import { getAppRendererLayout } from "../../../../helpers/cms/getAppRendererLayout";
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{
|
|
9
|
+
content: CmsBlockAppRenderer;
|
|
10
|
+
}>();
|
|
11
|
+
|
|
12
|
+
const appContext = getCurrentInstance()?.appContext;
|
|
13
|
+
|
|
14
|
+
const layout = computed(() => getAppRendererLayout(props.content));
|
|
15
|
+
|
|
16
|
+
const appBlockComponent = computed<Component | undefined>(() => {
|
|
17
|
+
const name = layout.value.componentName;
|
|
18
|
+
|
|
19
|
+
return name ? appContext?.components[name] : undefined;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const gridStyle = computed<CSSProperties>(() => ({
|
|
23
|
+
display: "grid",
|
|
24
|
+
grid: layout.value.grid,
|
|
25
|
+
}));
|
|
26
|
+
</script>
|
|
27
|
+
<template>
|
|
28
|
+
<component
|
|
29
|
+
:is="appBlockComponent"
|
|
30
|
+
v-if="appBlockComponent"
|
|
31
|
+
:content="content"
|
|
32
|
+
/>
|
|
33
|
+
<div
|
|
34
|
+
v-else
|
|
35
|
+
class="cms-block-app-renderer"
|
|
36
|
+
:class="`cms-block-app-renderer-fallback-block-${layout.appBlockName}`"
|
|
37
|
+
:style="gridStyle"
|
|
38
|
+
>
|
|
39
|
+
<div
|
|
40
|
+
v-for="slot in layout.slots"
|
|
41
|
+
:key="slot.id"
|
|
42
|
+
class="min-w-0"
|
|
43
|
+
:class="`cms-block-app-renderer-fallback-slot-${slot.type}`"
|
|
44
|
+
>
|
|
45
|
+
<CmsGenericElement :content="slot" />
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { CmsBlockCategoryHeading } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
4
|
+
|
|
5
|
+
import { useCmsBlock } from "#imports";
|
|
6
|
+
|
|
7
|
+
const props = defineProps<{
|
|
8
|
+
content: CmsBlockCategoryHeading;
|
|
9
|
+
}>();
|
|
10
|
+
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
12
|
+
|
|
13
|
+
const slotContent = computed(() => getSlotContent("content"));
|
|
14
|
+
</script>
|
|
15
|
+
<template>
|
|
16
|
+
<div class="cms-block-category-heading">
|
|
17
|
+
<CmsGenericElement :content="slotContent" />
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockCategoryNavigation } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
6
7
|
const props = defineProps<{
|
|
7
8
|
content: CmsBlockCategoryNavigation;
|
|
8
9
|
}>();
|
|
9
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
10
|
-
const slotContent = getSlotContent("content");
|
|
10
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
|
+
const slotContent = computed(() => getSlotContent("content"));
|
|
11
12
|
</script>
|
|
12
13
|
|
|
13
14
|
<template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockCenterText } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,10 +8,10 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockCenterText;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
-
const slotLeftContent = getSlotContent("left");
|
|
12
|
-
const slotRightContent = getSlotContent("right");
|
|
13
|
-
const slotCenterContent = getSlotContent("center");
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
12
|
+
const slotLeftContent = computed(() => getSlotContent("left"));
|
|
13
|
+
const slotRightContent = computed(() => getSlotContent("right"));
|
|
14
|
+
const slotCenterContent = computed(() => getSlotContent("center"));
|
|
14
15
|
</script>
|
|
15
16
|
<template>
|
|
16
17
|
<div class="cms-block-center-text grid md:grid-cols-3 gap-10 content-center">
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockCrossSelling } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
6
7
|
const props = defineProps<{
|
|
7
8
|
content: CmsBlockCrossSelling;
|
|
8
9
|
}>();
|
|
9
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
10
|
-
const slotContent = getSlotContent("content");
|
|
10
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
|
+
const slotContent = computed(() => getSlotContent("content"));
|
|
11
12
|
</script>
|
|
12
13
|
<template>
|
|
13
14
|
<div class="cms-block-cross-selling">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockForm } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,9 +8,9 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockForm;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
12
|
|
|
12
|
-
const cmsContent = getSlotContent("content");
|
|
13
|
+
const cmsContent = computed(() => getSlotContent("content"));
|
|
13
14
|
</script>
|
|
14
15
|
<template>
|
|
15
16
|
<div class="cms-block-form">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockForm } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,9 +8,9 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockForm;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
12
|
|
|
12
|
-
const cmsContent = getSlotContent("content");
|
|
13
|
+
const cmsContent = computed(() => getSlotContent("content"));
|
|
13
14
|
</script>
|
|
14
15
|
<template>
|
|
15
16
|
<div class="cms-block-form">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockGalleryBuybox } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,9 +8,9 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockGalleryBuybox;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
-
const rightContent = getSlotContent("right");
|
|
12
|
-
const leftContent = getSlotContent("left");
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
12
|
+
const rightContent = computed(() => getSlotContent("right"));
|
|
13
|
+
const leftContent = computed(() => getSlotContent("left"));
|
|
13
14
|
</script>
|
|
14
15
|
|
|
15
16
|
<template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockImage } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,8 +8,8 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockImage;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
-
const imageContent = getSlotContent("image");
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
12
|
+
const imageContent = computed(() => getSlotContent("image"));
|
|
12
13
|
</script>
|
|
13
14
|
<template>
|
|
14
15
|
<div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockImageBubbleRow } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,11 +8,11 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockImageBubbleRow;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
12
|
|
|
12
|
-
const leftContent = getSlotContent("left");
|
|
13
|
-
const rightContent = getSlotContent("right");
|
|
14
|
-
const centerContent = getSlotContent("center");
|
|
13
|
+
const leftContent = computed(() => getSlotContent("left"));
|
|
14
|
+
const rightContent = computed(() => getSlotContent("right"));
|
|
15
|
+
const centerContent = computed(() => getSlotContent("center"));
|
|
15
16
|
</script>
|
|
16
17
|
<template>
|
|
17
18
|
<div
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockImageCover } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,9 +8,9 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockImageCover;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
12
|
|
|
12
|
-
const cmsContent = getSlotContent("image");
|
|
13
|
+
const cmsContent = computed(() => getSlotContent("image"));
|
|
13
14
|
</script>
|
|
14
15
|
<template>
|
|
15
16
|
<div class="cms-block-image-cover">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockImageFourColumn } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,12 +8,12 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockImageFourColumn;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
12
|
|
|
12
|
-
const leftContent = getSlotContent("left");
|
|
13
|
-
const rightContent = getSlotContent("right");
|
|
14
|
-
const centerLeftContent = getSlotContent("center-left");
|
|
15
|
-
const centerRightContent = getSlotContent("center-right");
|
|
13
|
+
const leftContent = computed(() => getSlotContent("left"));
|
|
14
|
+
const rightContent = computed(() => getSlotContent("right"));
|
|
15
|
+
const centerLeftContent = computed(() => getSlotContent("center-left"));
|
|
16
|
+
const centerRightContent = computed(() => getSlotContent("center-right"));
|
|
16
17
|
</script>
|
|
17
18
|
<template>
|
|
18
19
|
<div
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockImageGallery } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,9 +8,9 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockImageGallery;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
12
|
|
|
12
|
-
const cmsContent = getSlotContent("imageGallery");
|
|
13
|
+
const cmsContent = computed(() => getSlotContent("imageGallery"));
|
|
13
14
|
</script>
|
|
14
15
|
|
|
15
16
|
<template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockImageGalleryBig } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,9 +8,9 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockImageGalleryBig;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
12
|
|
|
12
|
-
const cmsContent = getSlotContent("imageGallery");
|
|
13
|
+
const cmsContent = computed(() => getSlotContent("imageGallery"));
|
|
13
14
|
</script>
|
|
14
15
|
|
|
15
16
|
<template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { CmsBlockImageHighlightRow } from "@shopware/composables";
|
|
3
|
+
import { computed } from "vue";
|
|
3
4
|
|
|
4
5
|
import { useCmsBlock } from "#imports";
|
|
5
6
|
|
|
@@ -7,11 +8,11 @@ const props = defineProps<{
|
|
|
7
8
|
content: CmsBlockImageHighlightRow;
|
|
8
9
|
}>();
|
|
9
10
|
|
|
10
|
-
const { getSlotContent } = useCmsBlock(props.content);
|
|
11
|
+
const { getSlotContent } = useCmsBlock(() => props.content);
|
|
11
12
|
|
|
12
|
-
const leftContent = getSlotContent("left");
|
|
13
|
-
const rightContent = getSlotContent("right");
|
|
14
|
-
const centerContent = getSlotContent("center");
|
|
13
|
+
const leftContent = computed(() => getSlotContent("left"));
|
|
14
|
+
const rightContent = computed(() => getSlotContent("right"));
|
|
15
|
+
const centerContent = computed(() => getSlotContent("center"));
|
|
15
16
|
</script>
|
|
16
17
|
<template>
|
|
17
18
|
<div
|