@dcodegroup-au/page-builder 0.2.5 → 0.2.6
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/dist/page-builder.css +1 -1
- package/dist/page-builder.es.js +5789 -5500
- package/dist/page-builder.umd.js +49 -49
- package/example/src/App.vue +65 -4
- package/package.json +1 -1
- package/src/assets/svg/play.svg +3 -0
- package/src/components/PageBuilder.vue +5 -0
- package/src/components/PageRender.vue +2 -0
- package/src/components/builders/VNewsGrid.vue +55 -0
- package/src/components/builders/VVideoGrid.vue +99 -0
- package/src/components/presenters/components/VCarouselPresenter.vue +7 -7
- package/src/components/presenters/components/VCollectionGridPresenter.vue +121 -0
- package/src/components/presenters/components/VHeaderPresenter.vue +2 -2
- package/src/components/presenters/modules/CollectionGrid.vue +41 -0
- package/tailwind.config.js +5 -0
package/example/src/App.vue
CHANGED
|
@@ -269,12 +269,73 @@ const page = {
|
|
|
269
269
|
{
|
|
270
270
|
name: "Section header",
|
|
271
271
|
type: "header",
|
|
272
|
-
title: '
|
|
273
|
-
|
|
272
|
+
title: 'News',
|
|
273
|
+
dark: true,
|
|
274
|
+
center: true,
|
|
275
|
+
supporting_text: 'Stay Informed with the Latest Developments in Early Education',
|
|
274
276
|
},
|
|
275
277
|
{
|
|
276
278
|
name: "Grid",
|
|
277
|
-
type: "
|
|
279
|
+
type: "news_grid",
|
|
280
|
+
button: {
|
|
281
|
+
title: 'View all news',
|
|
282
|
+
url: 'google.com', // external could be an url
|
|
283
|
+
type: 'external-page',
|
|
284
|
+
is_new_tab: true,
|
|
285
|
+
show: true,
|
|
286
|
+
},
|
|
287
|
+
content: {
|
|
288
|
+
label: 'Content',
|
|
289
|
+
supportive_text: 'This grid will be automatically populated with the latest three news items.',
|
|
290
|
+
items: [
|
|
291
|
+
{
|
|
292
|
+
image: "https://beta-frontend.elaa.org.au/img/news/news_1.jpg",
|
|
293
|
+
categories: [{
|
|
294
|
+
name: 'Wages, IR and Governance',
|
|
295
|
+
style: 'navy',
|
|
296
|
+
}],
|
|
297
|
+
link: "https://example.com",
|
|
298
|
+
title: "Fair Work Laws changes for casual employment",
|
|
299
|
+
dateTime: "10 Sep 2024",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
image: "https://beta-frontend.elaa.org.au/img/news/news_2.jpg",
|
|
303
|
+
categories: [{
|
|
304
|
+
name: 'Member Benefits',
|
|
305
|
+
style: 'brand',
|
|
306
|
+
}],
|
|
307
|
+
link: "https://example.com",
|
|
308
|
+
title: "ELAA renews its partnership with Bunnings for another year: make the most of your Bunnings PowerPass membership benefits",
|
|
309
|
+
dateTime: "09 Sep 2024",
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
image: "https://beta-frontend.elaa.org.au/img/news/news_3.jpg",
|
|
313
|
+
categories: [{
|
|
314
|
+
name: 'Advocacy',
|
|
315
|
+
style: 'orange',
|
|
316
|
+
}],
|
|
317
|
+
link: "https://example.com",
|
|
318
|
+
title: "Meet our Best Start Best Life (BSBL) Reform and Engagement Advisor Leanne Rodriguez",
|
|
319
|
+
dateTime: "08 Sep 2024",
|
|
320
|
+
},
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: "Grid",
|
|
326
|
+
type: "video_grid",
|
|
327
|
+
max_items: 3,
|
|
328
|
+
data: [
|
|
329
|
+
{
|
|
330
|
+
video: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4",
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
video: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4",
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
video: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4",
|
|
337
|
+
}
|
|
338
|
+
]
|
|
278
339
|
}
|
|
279
340
|
]
|
|
280
341
|
},
|
|
@@ -432,7 +493,7 @@ const page = {
|
|
|
432
493
|
},
|
|
433
494
|
]
|
|
434
495
|
}
|
|
435
|
-
}
|
|
496
|
+
},
|
|
436
497
|
]
|
|
437
498
|
},
|
|
438
499
|
{
|
package/package.json
CHANGED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M28 56C43.464 56 56 43.464 56 28C56 12.536 43.464 0 28 0C12.536 0 0 12.536 0 28C0 43.464 12.536 56 28 56ZM23.625 38.2705L39.375 29.4672C40.5417 28.8151 40.5417 27.1849 39.375 26.5328L23.625 17.7295C22.4583 17.0774 21 17.8925 21 19.1967V36.8033C21 38.1075 22.4583 38.9226 23.625 38.2705Z" fill="white"/>
|
|
3
|
+
</svg>
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
<div class="flex h-full flex-1 flex-col rounded-xl bg-gray-50 px-6 py-5 mb-20">
|
|
42
42
|
<Instructions v-if="!selected" />
|
|
43
43
|
<component
|
|
44
|
+
class="min-h-[90vh]"
|
|
44
45
|
:is="currentComponent"
|
|
45
46
|
:key="selected?.sectionIndex + selected?.componentIndex"
|
|
46
47
|
:data="selected"
|
|
@@ -72,6 +73,8 @@ import VLinks from "@/components/builders/VLinks.vue";
|
|
|
72
73
|
import VHeader from "@/components/builders/VHeader.vue";
|
|
73
74
|
import VLogos from "@/components/builders/VLogos.vue";
|
|
74
75
|
import VCollectionCarousel from "@/components/builders/VCollectionCarousel.vue";
|
|
76
|
+
import VNewsGrid from "@/components/builders/VNewsGrid.vue";
|
|
77
|
+
import VVideoGrid from "@/components/builders/VVideoGrid.vue";
|
|
75
78
|
|
|
76
79
|
const emit = defineEmits(["save", "close"]);
|
|
77
80
|
const props = defineProps({
|
|
@@ -98,6 +101,8 @@ const componentMaps = ref({
|
|
|
98
101
|
link_grid: markRaw(VLinks),
|
|
99
102
|
logos: markRaw(VLogos),
|
|
100
103
|
carousel: markRaw(VCollectionCarousel),
|
|
104
|
+
news_grid: markRaw(VNewsGrid),
|
|
105
|
+
video_grid: markRaw(VVideoGrid),
|
|
101
106
|
});
|
|
102
107
|
|
|
103
108
|
if (!openStates.value) {
|
|
@@ -16,6 +16,7 @@ import QuickLinks from "@/components/presenters/modules/QuickLinks.vue";
|
|
|
16
16
|
import VTabs from "@/components/presenters/modules/VTabs.vue";
|
|
17
17
|
import LogoCloud from "@/components/presenters/modules/LogoCloud.vue";
|
|
18
18
|
import CollectionCarousel from "@/components/presenters/modules/CollectionCarousel.vue";
|
|
19
|
+
import CollectionGrid from "@/components/presenters/modules/CollectionGrid.vue";
|
|
19
20
|
|
|
20
21
|
const props = defineProps({
|
|
21
22
|
page: {
|
|
@@ -30,6 +31,7 @@ const componentMaps = ref({
|
|
|
30
31
|
tabs: markRaw(VTabs),
|
|
31
32
|
logo: markRaw(LogoCloud),
|
|
32
33
|
collection_carousel: markRaw(CollectionCarousel),
|
|
34
|
+
collection_grid: markRaw(CollectionGrid),
|
|
33
35
|
});
|
|
34
36
|
|
|
35
37
|
const currentComponent = (section) => {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col">
|
|
3
|
+
<div class="flex flex-col gap-4 mb-4 border-b border-gray-200 pb-4">
|
|
4
|
+
<p class="text-lg font-semibold text-gray-900 border-b border-gray-200 pb-4">
|
|
5
|
+
Grid
|
|
6
|
+
</p>
|
|
7
|
+
<VToggle name="show" v-model="componentData.button.show" title="Show Button" />
|
|
8
|
+
<input-wrapper
|
|
9
|
+
is-vertical
|
|
10
|
+
field="title"
|
|
11
|
+
label-text="Title *"
|
|
12
|
+
class="w-full mb-4"
|
|
13
|
+
:value="componentData.button.title"
|
|
14
|
+
:limit="20"
|
|
15
|
+
>
|
|
16
|
+
<input
|
|
17
|
+
v-model="componentData.button.title"
|
|
18
|
+
name="title"
|
|
19
|
+
type="text"
|
|
20
|
+
placeholder="Title"
|
|
21
|
+
:maxlength="20"
|
|
22
|
+
class="border-1 border-solid border-gray-300 rounded-lg bg-white w-full"
|
|
23
|
+
/>
|
|
24
|
+
</input-wrapper>
|
|
25
|
+
<linked-to
|
|
26
|
+
v-if="componentData?.button"
|
|
27
|
+
label="Link to"
|
|
28
|
+
name="button"
|
|
29
|
+
v-model:type="componentData.button.type"
|
|
30
|
+
v-model:url="componentData.button.url"
|
|
31
|
+
v-model:openInNewTab="componentData.button.open_in_new_tab"
|
|
32
|
+
:sites="sites"
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
<div>
|
|
36
|
+
<h3 v-if="componentData.content?.label" class="text-base text-gray-900 font-semibold">{{ componentData.content.label }}</h3>
|
|
37
|
+
<p v-if="componentData.content?.supportive_text" class="text-gray-600 text-base font-normal mt-2">{{ componentData.content.supportive_text }}</p>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
41
|
+
<script setup>
|
|
42
|
+
import {ref} from "vue";
|
|
43
|
+
import {defaultProps} from "@/components/helpers/defaultProps";
|
|
44
|
+
import LinkedTo from "@/components/common/LinkedTo.vue";
|
|
45
|
+
import InputWrapper from "@/components/common/InputWrapper.vue";
|
|
46
|
+
import VToggle from "@/components/common/VToggle.vue";
|
|
47
|
+
|
|
48
|
+
const emit = defineEmits(["update"]);
|
|
49
|
+
|
|
50
|
+
const props = defineProps({
|
|
51
|
+
...defaultProps,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const componentData = ref(props.data.component);
|
|
55
|
+
</script>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex justify-between pb-2">
|
|
3
|
+
<div class="flex justify-between w-full py-1">
|
|
4
|
+
<div>
|
|
5
|
+
<p class="text-lg font-semibold text-gray-900">
|
|
6
|
+
{{ componentData.name }}
|
|
7
|
+
</p>
|
|
8
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
9
|
+
This section can contain up to {{ componentData.max_items }} videos
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
<div>
|
|
13
|
+
<a
|
|
14
|
+
@click="addItem"
|
|
15
|
+
class="text-sm cursor-pointer flex items-center justify-center gap-1 rounded-[99px] border border-brand-600 bg-brand-500 px-3.5 py-2 font-semibold text-white hover:bg-brand-600"
|
|
16
|
+
:class="{ 'border-gray-100 bg-gray-100 !text-gray-400 hover:bg-gray-100': componentData.data?.length >= componentData.max_items }"
|
|
17
|
+
>
|
|
18
|
+
<PlusIcon class="h-5 w-5"></PlusIcon>
|
|
19
|
+
<span>Add</span>
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="flex flex-col gap-3">
|
|
25
|
+
<div
|
|
26
|
+
v-for="(item, index) in componentData.data"
|
|
27
|
+
class="flex flex-col gap-4 rounded-xl px-6 py-4 bg-gray-200"
|
|
28
|
+
:key="index"
|
|
29
|
+
:ref="index === componentData.data.length - 1 ? (el) => (lastItemRef = el) : null"
|
|
30
|
+
>
|
|
31
|
+
<div class="flex items-center justify-between">
|
|
32
|
+
<div class="text-lg font-semibold text-gray-900">
|
|
33
|
+
Video #{{index + 1}}
|
|
34
|
+
</div>
|
|
35
|
+
<div class="relative flex items-end">
|
|
36
|
+
<ActionMenu @removeItem="handleDeleteItem(index)"/>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="flex flex-col gap-6">
|
|
40
|
+
<div class="flex flex-col gap-1.5">
|
|
41
|
+
<VFileUpload
|
|
42
|
+
name="image"
|
|
43
|
+
type="video"
|
|
44
|
+
height="h-[250px]"
|
|
45
|
+
v-model="item.video"
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<VModal ref="modalRef" entity="logo" :callback="deleteCallback"></VModal>
|
|
52
|
+
</template>
|
|
53
|
+
<script setup>
|
|
54
|
+
import {ref, inject} from "vue";
|
|
55
|
+
import PlusIcon from "@/assets/img/icons/plus.svg";
|
|
56
|
+
import {defaultProps} from "@/components/helpers/defaultProps";
|
|
57
|
+
import ActionMenu from "@/components/common/ActionMenu.vue";
|
|
58
|
+
import VModal from "@/components/common/VModal.vue";
|
|
59
|
+
import DefaultFileUpload from "@/components/common/FileUpload.vue";
|
|
60
|
+
|
|
61
|
+
// Inject the FileUpload component or fallback to the default one
|
|
62
|
+
const VFileUpload = inject("VFileUpload", DefaultFileUpload);
|
|
63
|
+
|
|
64
|
+
const emit = defineEmits(["update"]);
|
|
65
|
+
|
|
66
|
+
const props = defineProps({
|
|
67
|
+
...defaultProps,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const componentData = ref(props.data.component);
|
|
71
|
+
const modalRef = ref(null);
|
|
72
|
+
const lastItemRef = ref(null);
|
|
73
|
+
const deleteItemIndex = ref(null);
|
|
74
|
+
|
|
75
|
+
function addItem() {
|
|
76
|
+
if (!componentData.value.hasOwnProperty('data')) {
|
|
77
|
+
componentData.value.data = [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (componentData.value.data?.length >= componentData.value.max_items) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
componentData.value.data?.push({
|
|
84
|
+
video: null,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
emit("update", false);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const handleDeleteItem = (index) => {
|
|
91
|
+
deleteItemIndex.value = index;
|
|
92
|
+
modalRef?.value?.open(index);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const deleteCallback = (index) => {
|
|
96
|
+
componentData.value.data?.splice(index, 1);
|
|
97
|
+
emit("update", false);
|
|
98
|
+
};
|
|
99
|
+
</script>
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
/>
|
|
23
23
|
</div>
|
|
24
24
|
<div v-if="slide.tags && slide.tags.length" class="flex gap-2">
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
<span
|
|
26
|
+
v-for="tag in slide.tags"
|
|
27
|
+
:key="tag"
|
|
28
|
+
class="bg-navy-50 border border-navy-200 text-navy-700 px-2 py-0.5 rounded-[6px] text-sm"
|
|
29
|
+
>
|
|
30
|
+
{{ tag }}
|
|
31
|
+
</span>
|
|
32
32
|
</div>
|
|
33
33
|
<h3 class="text-[24px] font-semibold text-gray-900 mb-3 leading-[23px] mt-3">
|
|
34
34
|
{{ slide?.title }}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col items-center gap-6 pb-[40px] pt-[24px]">
|
|
3
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 w-full">
|
|
4
|
+
<div v-for="(card, index) in items" :key="index" class="card bg-white sm:p-0 px-4">
|
|
5
|
+
<div v-if="component.type === 'video_grid'" class="relative group">
|
|
6
|
+
<video
|
|
7
|
+
@click="togglePlayPause(index)"
|
|
8
|
+
:ref="(el) => setVideoRef(el, index)"
|
|
9
|
+
class="w-full h-[281px] object-cover rounded-[16px]"
|
|
10
|
+
width="640"
|
|
11
|
+
height="360"
|
|
12
|
+
preload="metadata"
|
|
13
|
+
>
|
|
14
|
+
<source :src="`${card.video}#t=2`" />
|
|
15
|
+
</video>
|
|
16
|
+
<PlayButton
|
|
17
|
+
v-if="!isPlaying[index]"
|
|
18
|
+
class="cursor-pointer absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 group-hover:opacity-100 transition-opacity"
|
|
19
|
+
@click="togglePlayPause(index)">
|
|
20
|
+
|
|
21
|
+
</PlayButton>
|
|
22
|
+
</div>
|
|
23
|
+
<img
|
|
24
|
+
v-else
|
|
25
|
+
:src="card.image"
|
|
26
|
+
alt="Card Image"
|
|
27
|
+
class="w-full h-[281px] object-cover rounded-[16px] mb-4"
|
|
28
|
+
/>
|
|
29
|
+
<div class="flex flex-col gap-2 items-start text-left w-full">
|
|
30
|
+
<div v-if="card.categories && card.categories.length" class="flex gap-2">
|
|
31
|
+
<span
|
|
32
|
+
v-for="tag in card.categories"
|
|
33
|
+
:key="tag"
|
|
34
|
+
class="border px-2 py-0.5 rounded-[6px] text-sm"
|
|
35
|
+
:class="{
|
|
36
|
+
'bg-navy-50 border-navy-200 text-navy-700': tag.style === 'navy',
|
|
37
|
+
'bg-brand-50 border-brand-200 text-brand-700': tag.style === 'brand',
|
|
38
|
+
'bg-orange-50 border-orange-200 text-orange-700': tag.style === 'orange',
|
|
39
|
+
}"
|
|
40
|
+
>
|
|
41
|
+
{{ tag.name }}
|
|
42
|
+
</span>
|
|
43
|
+
</div>
|
|
44
|
+
<a
|
|
45
|
+
v-if="card?.link"
|
|
46
|
+
:href="card.link"
|
|
47
|
+
class="text-gray-900 text-lg font-semibold hover:underline block mb-2 w-full"
|
|
48
|
+
target="_blank"
|
|
49
|
+
rel="noopener noreferrer"
|
|
50
|
+
>
|
|
51
|
+
<div class="flex justify-between">
|
|
52
|
+
<span class="w-5/6 leading-[28px]">{{ card.title }}</span>
|
|
53
|
+
<ArrowUpRight class="w-5 h-5 mt-1"></ArrowUpRight>
|
|
54
|
+
</div>
|
|
55
|
+
</a>
|
|
56
|
+
<p class="text-gray-500 text-sm mb-4">{{ card.dateTime }}</p>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div v-if="Object.keys(button).length && button?.show" class="flex justify-center">
|
|
61
|
+
<a
|
|
62
|
+
class="border-brand-300 hover:border-brand-700 border text-brand-700 h-[44px] rounded-full px-[14px] py-[10px] inline-flex gap-1.5 items-center font-semibold text-base"
|
|
63
|
+
:href="button.url"
|
|
64
|
+
:target="button.is_new_tab ? '_blank' : ''"
|
|
65
|
+
>
|
|
66
|
+
{{ button.title }}
|
|
67
|
+
<ArrowUpRight class="w-5 h-5"></ArrowUpRight>
|
|
68
|
+
</a>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<script setup>
|
|
74
|
+
import { ref } from "vue";
|
|
75
|
+
import ArrowUpRight from "@/assets/img/icons/arrow-up-right.svg";
|
|
76
|
+
import PlayButton from "@/assets/svg/play.svg";
|
|
77
|
+
|
|
78
|
+
const props = defineProps({
|
|
79
|
+
component: {
|
|
80
|
+
required: true,
|
|
81
|
+
type: Object,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
let items = [];
|
|
86
|
+
if (props.component.type === 'news_grid') {
|
|
87
|
+
items = [...props.component.content?.items || []];
|
|
88
|
+
} else {
|
|
89
|
+
items = [...props.component?.data || []];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const button = props.component.button || {};
|
|
93
|
+
|
|
94
|
+
const isPlaying = ref(items.map(() => false));
|
|
95
|
+
const videoRefs = ref(items.map(() => null));
|
|
96
|
+
|
|
97
|
+
const togglePlayPause = (index) => {
|
|
98
|
+
const video = videoRefs.value[index];
|
|
99
|
+
if (!video) return;
|
|
100
|
+
|
|
101
|
+
if (video.paused) {
|
|
102
|
+
video.play();
|
|
103
|
+
isPlaying.value[index] = true;
|
|
104
|
+
} else {
|
|
105
|
+
video.pause();
|
|
106
|
+
isPlaying.value[index] = false;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const setVideoRef = (el, index) => {
|
|
111
|
+
if (el) {
|
|
112
|
+
videoRefs.value[index] = el;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<style scoped>
|
|
118
|
+
.card {
|
|
119
|
+
@apply flex flex-col items-center text-center;
|
|
120
|
+
}
|
|
121
|
+
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="flex flex-col" :class="{'items-center': component?.center}">
|
|
2
|
+
<div class="flex flex-col sm:px-0 px-4" :class="{'items-center': component?.center}">
|
|
3
3
|
<p
|
|
4
4
|
v-if="component?.title"
|
|
5
5
|
class="pb-4 text-4xl font-semibold text-white"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<p
|
|
9
9
|
v-if="component?.supporting_text"
|
|
10
10
|
class="text-navy-25 text-xl font-normal leading-[30px]"
|
|
11
|
-
:class="{'!text-
|
|
11
|
+
:class="{'!text-gray-600': component?.dark}"
|
|
12
12
|
>
|
|
13
13
|
{{ component?.supporting_text }}</p>
|
|
14
14
|
</div>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="overflow-hidden">
|
|
3
|
+
<div class="max-w-[1400px] mx-auto w-full pt-[40px]">
|
|
4
|
+
<template v-for="(component, index) in section.components" :key="index">
|
|
5
|
+
<component
|
|
6
|
+
:is="currentComponent(component)"
|
|
7
|
+
:component="component"
|
|
8
|
+
></component>
|
|
9
|
+
</template>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
import { ref, markRaw } from "vue";
|
|
16
|
+
import VHeaderPresenter from "@/components/presenters/components/VHeaderPresenter.vue";
|
|
17
|
+
import VCollectionGridPresenter from "@/components/presenters/components/VCollectionGridPresenter.vue";
|
|
18
|
+
|
|
19
|
+
const props = defineProps({
|
|
20
|
+
section: {
|
|
21
|
+
required: true,
|
|
22
|
+
type: Object,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const section = ref(props.section);
|
|
27
|
+
const componentMaps = ref({
|
|
28
|
+
header: markRaw(VHeaderPresenter),
|
|
29
|
+
news_grid: markRaw(VCollectionGridPresenter),
|
|
30
|
+
video_grid: markRaw(VCollectionGridPresenter),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const currentComponent = (component) => {
|
|
34
|
+
if (!component?.type) {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return componentMaps.value[component?.type];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
</script>
|
package/tailwind.config.js
CHANGED