@dcodegroup-au/page-builder 0.2.8 → 0.2.9
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 +2 -2
- package/dist/page-builder.es.js +33372 -11858
- package/dist/page-builder.umd.js +59 -59
- package/example/src/App.vue +40 -679
- package/example/src/main.js +2 -1
- package/example/src/pages/BestLife.js +351 -0
- package/example/src/pages/Home.js +677 -0
- package/package.json +1 -1
- package/src/assets/icons.json +3569 -0
- package/src/components/ItemEdit.vue +147 -71
- package/src/components/LinkCardEdit.vue +139 -0
- package/src/components/PageBuilder.vue +43 -10
- package/src/components/PageRender.vue +12 -0
- package/src/components/builders/CollectionCarousel.vue +62 -0
- package/src/components/builders/Header.vue +82 -0
- package/src/components/builders/ImageBlock.vue +56 -0
- package/src/components/builders/Items.vue +112 -0
- package/src/components/builders/Links.vue +123 -0
- package/src/components/builders/Logos.vue +122 -0
- package/src/components/builders/NewsGrid.vue +59 -0
- package/src/components/builders/Paragraph.vue +93 -0
- package/src/components/builders/VideoGrid.vue +101 -0
- package/src/components/common/Button.vue +53 -0
- package/src/components/common/Card.vue +5 -1
- package/src/components/common/FileUpload.vue +1 -1
- package/src/components/common/Icon.vue +41 -0
- package/src/components/common/IconSelector.vue +106 -0
- package/src/components/common/LinkedTo.vue +9 -3
- package/src/components/helpers/bundleIcons.js +1189 -0
- package/src/components/index.js +2 -1
- package/src/components/presenters/components/{VCollectionGridPresenter.vue → CollectionGridPresenter.vue} +0 -5
- package/src/components/presenters/modules/CollectionCarousel.vue +2 -2
- package/src/components/presenters/modules/CollectionGrid.vue +2 -2
- package/src/components/presenters/modules/HeroHeader.vue +2 -2
- package/src/components/presenters/modules/LinkCard.vue +55 -0
- package/src/components/presenters/modules/LinkList.vue +51 -0
- package/src/components/presenters/modules/Paragraph.vue +26 -0
- package/src/components/presenters/modules/QuickLinks.vue +2 -2
- package/src/components/presenters/modules/StandardHeader.vue +32 -0
- package/src/components/presenters/modules/Timeline.vue +55 -0
- package/src/components/presenters/modules/TwoColumnsImageContent.vue +36 -0
- package/src/components/presenters/modules/VTabs.vue +2 -2
- package/src/utils/generateIconBundle.js +33 -0
- package/src/utils/generateIconJson.js +30 -0
- package/tailwind.config.js +5 -0
- package/src/components/builders/BaseModuleForm.vue +0 -86
- package/src/components/builders/LogoBuilder.vue +0 -167
- package/src/components/builders/PageBuilderCarousel.vue +0 -18
- package/src/components/builders/PageBuilderGrid.vue +0 -18
- package/src/components/builders/PageBuilderSectionHeader.vue +0 -30
- package/src/components/builders/PageModal.vue +0 -92
- package/src/components/builders/VCollectionCarousel.vue +0 -58
- package/src/components/builders/VHeader.vue +0 -55
- package/src/components/builders/VItems.vue +0 -110
- package/src/components/builders/VLinks.vue +0 -121
- package/src/components/builders/VLogos.vue +0 -120
- package/src/components/builders/VNewsGrid.vue +0 -55
- package/src/components/builders/VVideoGrid.vue +0 -99
- package/src/components/common/forms/LogosForm.vue +0 -39
- package/src/components/common/forms/PageBuilderLinksForm.vue +0 -39
- package/src/components/common/forms/SectionHeaderForm.vue +0 -45
- package/src/components/common/forms/TabForm.vue +0 -90
- /package/src/components/common/{VModal.vue → Modal.vue} +0 -0
- /package/src/components/common/{VToggle.vue → Toggle.vue} +0 -0
- /package/src/components/presenters/components/{VCarouselPresenter.vue → CarouselPresenter.vue} +0 -0
- /package/src/components/presenters/components/{VHeaderPresenter.vue → HeaderPresenter.vue} +0 -0
- /package/src/components/presenters/components/{VLinkPresenter.vue → LinkPresenter.vue} +0 -0
- /package/src/components/presenters/components/{VSliderPresenter.vue → SliderPresenter.vue} +0 -0
- /package/src/components/presenters/components/{VVerticalTabPresenter.vue → VerticalTabPresenter.vue} +0 -0
|
@@ -1,19 +1,76 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="item-edit">
|
|
3
3
|
<div class="flex items-start gap-4 px-6 pt-4 h-full min-h-[90vh]">
|
|
4
|
-
<div class="flex flex-1 flex-col gap-4">
|
|
5
|
-
<
|
|
4
|
+
<div class="flex flex-1 flex-col gap-4 mb-4">
|
|
5
|
+
<div v-if="hasLines">
|
|
6
|
+
<div class="flex justify-between mb-4">
|
|
7
|
+
<div class="flex flex-col gap-1">
|
|
8
|
+
<p class="text-base font-semibold text-gray-900">Events</p>
|
|
9
|
+
<p class="text-sm">
|
|
10
|
+
This timeline item can contain up to {{ item?.max_items }} events.
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
<button
|
|
14
|
+
:disabled="advancedForm.lines.length >= item?.max_items"
|
|
15
|
+
@click="addLine"
|
|
16
|
+
class="text-sm cursor-pointer flex items-center justify-center gap-1 rounded-[99px] border border-brand-600 bg-brand-500 px-3.5 h-[40px] font-semibold text-white hover:bg-brand-600"
|
|
17
|
+
:class="{ 'border-gray-100 bg-gray-100 !text-gray-400 hover:bg-gray-100': advancedForm.lines.length >= item?.max_items }"
|
|
18
|
+
>
|
|
19
|
+
<PlusIcon class="h-5 w-5"></PlusIcon>
|
|
20
|
+
<span>Event</span>
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="flex flex-col gap-4">
|
|
24
|
+
<div
|
|
25
|
+
v-for="(line, index) in advancedForm.lines"
|
|
26
|
+
:key="index"
|
|
27
|
+
class="flex flex-col gap-2 rounded-lg px-6 pt-4 pb-[40px] bg-gray-50"
|
|
28
|
+
>
|
|
29
|
+
<div class="flex justify-between items-center border-b border-gray-200 pb-3 mb-4">
|
|
30
|
+
<p class="text-lg font-semibold text-gray-900"> Event #{{ index + 1 }}</p>
|
|
31
|
+
<ActionMenu @removeItem="handleDeleteItem(index)"/>
|
|
32
|
+
</div>
|
|
33
|
+
<input-wrapper
|
|
34
|
+
is-vertical
|
|
35
|
+
field="line.title"
|
|
36
|
+
label-text="Heading *"
|
|
37
|
+
class="w-full"
|
|
38
|
+
:value="line.title"
|
|
39
|
+
>
|
|
40
|
+
<input
|
|
41
|
+
v-model="line.title"
|
|
42
|
+
name="line.title"
|
|
43
|
+
type="text"
|
|
44
|
+
placeholder="Heading"
|
|
45
|
+
class="border-1 border-solid border-gray-300 rounded-lg bg-white w-full"
|
|
46
|
+
/>
|
|
47
|
+
</input-wrapper>
|
|
48
|
+
<input-wrapper
|
|
49
|
+
is-vertical
|
|
50
|
+
field="line.description"
|
|
51
|
+
label-text="Description *"
|
|
52
|
+
class="w-full"
|
|
53
|
+
:value="descriptionWordCount(line.description)"
|
|
54
|
+
:show-count="true"
|
|
55
|
+
>
|
|
56
|
+
<QuillEditor v-model="line.description"/>
|
|
57
|
+
</input-wrapper>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<card title="Description" v-else>
|
|
6
63
|
<div class="flex flex-col gap-8">
|
|
7
64
|
<input-wrapper
|
|
8
65
|
is-vertical
|
|
9
66
|
field="title"
|
|
10
67
|
label-text="Title *"
|
|
11
68
|
class="w-full"
|
|
12
|
-
:value="
|
|
69
|
+
:value="basicForm.title"
|
|
13
70
|
:limit="51"
|
|
14
71
|
>
|
|
15
72
|
<input
|
|
16
|
-
v-model="
|
|
73
|
+
v-model="basicForm.title"
|
|
17
74
|
name="title"
|
|
18
75
|
type="text"
|
|
19
76
|
placeholder="Title"
|
|
@@ -26,83 +83,69 @@
|
|
|
26
83
|
field="description"
|
|
27
84
|
label-text="Description *"
|
|
28
85
|
class="w-full mb-4"
|
|
29
|
-
:value="descriptionWordCount"
|
|
86
|
+
:value="descriptionWordCount(basicForm.description)"
|
|
30
87
|
:show-count="true"
|
|
31
88
|
>
|
|
32
|
-
<QuillEditor v-model="
|
|
89
|
+
<QuillEditor v-model="basicForm.description"/>
|
|
33
90
|
</input-wrapper>
|
|
34
91
|
</div>
|
|
35
92
|
</card>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
93
|
+
<VButton v-if="basicForm.hasOwnProperty('primary_button')" :button="basicForm.primary_button" :sites="sites"/>
|
|
94
|
+
<VButton v-if="basicForm.hasOwnProperty('secondary_button')" :button="basicForm.secondary_button" :sites="sites"/>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="flex w-full max-w-[356px] flex-col gap-4">
|
|
97
|
+
<template v-if="!hasLines">
|
|
98
|
+
<card title="Visibility" supporting_text="Toggle off if you'd like to hide this item">
|
|
99
|
+
<VToggle v-model="basicForm.public" name="public" label="Public"/>
|
|
100
|
+
</card>
|
|
101
|
+
<card title="Featured Image" supporting_text="Recommended dimension: 1200*1060px">
|
|
102
|
+
<VFileUpload
|
|
103
|
+
name="image"
|
|
104
|
+
height="h-[200px]"
|
|
105
|
+
v-model="basicForm.featured_image"
|
|
106
|
+
/>
|
|
107
|
+
</card>
|
|
108
|
+
</template>
|
|
109
|
+
<template v-else>
|
|
110
|
+
<card title="Timeline item title" supporting_text="To indicate the time period of the events">
|
|
39
111
|
<input-wrapper
|
|
40
112
|
is-vertical
|
|
41
|
-
field="
|
|
42
|
-
label-text="
|
|
43
|
-
class="w-full
|
|
44
|
-
:value="
|
|
113
|
+
field="title"
|
|
114
|
+
label-text="Timeline item title *"
|
|
115
|
+
class="w-full"
|
|
116
|
+
:value="advancedForm.title"
|
|
45
117
|
:limit="20"
|
|
46
118
|
>
|
|
47
119
|
<input
|
|
48
|
-
v-model="
|
|
49
|
-
name="
|
|
120
|
+
v-model="advancedForm.title"
|
|
121
|
+
name="title"
|
|
50
122
|
type="text"
|
|
51
|
-
placeholder="
|
|
123
|
+
placeholder="Title"
|
|
52
124
|
:maxlength="20"
|
|
53
125
|
class="border-1 border-solid border-gray-300 rounded-lg bg-white w-full"
|
|
54
126
|
/>
|
|
55
127
|
</input-wrapper>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
v-model:type="form.primary_button.type"
|
|
59
|
-
v-model:url="form.primary_button.url"
|
|
60
|
-
v-model:openInNewTab="form.primary_button.open_in_new_tab"
|
|
61
|
-
:sites="sites"
|
|
62
|
-
/>
|
|
63
|
-
</div>
|
|
64
|
-
</card>
|
|
65
|
-
<card v-if="form.hasOwnProperty('secondary_button')" :title="form?.secondary_button?.name ?? 'Secondary button'">
|
|
66
|
-
<div class="flex flex-col gap-4">
|
|
67
|
-
<VToggle name="show" v-model="form.secondary_button.show" title="Show Button" />
|
|
128
|
+
</card>
|
|
129
|
+
<card title="Event group title" supporting_text="The title for the group of events under this time period">
|
|
68
130
|
<input-wrapper
|
|
69
131
|
is-vertical
|
|
70
|
-
field="
|
|
71
|
-
label-text="
|
|
72
|
-
class="w-full
|
|
73
|
-
:value="
|
|
74
|
-
:limit="
|
|
132
|
+
field="title"
|
|
133
|
+
label-text="Event group title *"
|
|
134
|
+
class="w-full"
|
|
135
|
+
:value="advancedForm.group_title"
|
|
136
|
+
:limit="51"
|
|
75
137
|
>
|
|
76
138
|
<input
|
|
77
|
-
v-model="
|
|
78
|
-
name="
|
|
139
|
+
v-model="advancedForm.group_title"
|
|
140
|
+
name="title"
|
|
79
141
|
type="text"
|
|
80
|
-
placeholder="
|
|
81
|
-
:maxlength="
|
|
142
|
+
placeholder="Title"
|
|
143
|
+
:maxlength="51"
|
|
82
144
|
class="border-1 border-solid border-gray-300 rounded-lg bg-white w-full"
|
|
83
145
|
/>
|
|
84
146
|
</input-wrapper>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
v-model:type="form.secondary_button.type"
|
|
88
|
-
v-model:url="form.secondary_button.url"
|
|
89
|
-
v-model:openInNewTab="form.secondary_button.open_in_new_tab"
|
|
90
|
-
:sites="sites"
|
|
91
|
-
/>
|
|
92
|
-
</div>
|
|
93
|
-
</card>
|
|
94
|
-
</div>
|
|
95
|
-
<div class="flex w-full max-w-[356px] flex-col gap-4">
|
|
96
|
-
<card title="Visibility" supporting_text="Toggle off if you'd like to hide this item">
|
|
97
|
-
<VToggle v-model="form.public" name="public" label="Public" />
|
|
98
|
-
</card>
|
|
99
|
-
<card title="Featured Image" supporting_text="Recommended dimension: 1200*1060px">
|
|
100
|
-
<VFileUpload
|
|
101
|
-
name="image"
|
|
102
|
-
height="h-[200px]"
|
|
103
|
-
v-model="form.featured_image"
|
|
104
|
-
/>
|
|
105
|
-
</card>
|
|
147
|
+
</card>
|
|
148
|
+
</template>
|
|
106
149
|
</div>
|
|
107
150
|
</div>
|
|
108
151
|
<slot>
|
|
@@ -116,20 +159,25 @@
|
|
|
116
159
|
>Save changes</a>
|
|
117
160
|
</div>
|
|
118
161
|
</slot>
|
|
162
|
+
<VModal ref="modalRef" :entity="'event'" :callback="removeLine"></VModal>
|
|
119
163
|
</div>
|
|
120
164
|
</template>
|
|
165
|
+
|
|
121
166
|
<script setup>
|
|
122
|
-
import {
|
|
167
|
+
import {ref, computed, inject} from "vue";
|
|
123
168
|
import QuillEditor from "@/components/common/QuillEditor.vue";
|
|
124
169
|
import Card from "@/components/common/Card.vue";
|
|
125
|
-
import LinkedTo from "@/components/common/LinkedTo.vue";
|
|
126
170
|
import InputWrapper from "@/components/common/InputWrapper.vue";
|
|
127
|
-
import VToggle from "@/components/common/
|
|
171
|
+
import VToggle from "@/components/common/Toggle.vue";
|
|
128
172
|
import DefaultFileUpload from "@/components/common/FileUpload.vue";
|
|
129
173
|
|
|
130
174
|
// Inject the FileUpload component or fallback to the default one
|
|
131
175
|
const VFileUpload = inject("VFileUpload", DefaultFileUpload);
|
|
132
176
|
import axios from "axios";
|
|
177
|
+
import VButton from "@/components/common/Button.vue";
|
|
178
|
+
import PlusIcon from "@/assets/img/icons/plus.svg";
|
|
179
|
+
import ActionMenu from "@/components/common/ActionMenu.vue";
|
|
180
|
+
import VModal from "@/components/common/Modal.vue";
|
|
133
181
|
|
|
134
182
|
const emit = defineEmits(["update"]);
|
|
135
183
|
|
|
@@ -147,39 +195,64 @@ const props = defineProps({
|
|
|
147
195
|
required: false,
|
|
148
196
|
},
|
|
149
197
|
sites: {
|
|
150
|
-
type:
|
|
198
|
+
type: Array,
|
|
151
199
|
required: true,
|
|
152
200
|
},
|
|
153
201
|
});
|
|
154
202
|
|
|
155
|
-
const
|
|
203
|
+
const hasLines = computed(() => {
|
|
204
|
+
return props.item.hasOwnProperty('lines');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const modalRef = ref(null);
|
|
208
|
+
|
|
209
|
+
const basicForm = ref({
|
|
156
210
|
public: props.item.public,
|
|
157
211
|
title: props.item.title,
|
|
158
212
|
description: props.item.description,
|
|
159
213
|
featured_image: props.item.featured_image,
|
|
214
|
+
lines: props.item.lines || [],
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const advancedForm = ref({
|
|
218
|
+
title: props.item.title,
|
|
219
|
+
title_group: props.item?.title_group,
|
|
220
|
+
max_items: props.item?.max_items,
|
|
221
|
+
lines: props.item.lines || [],
|
|
160
222
|
});
|
|
161
223
|
|
|
162
224
|
if (props.item.hasOwnProperty('primary_button')) {
|
|
163
|
-
|
|
225
|
+
basicForm.value.primary_button = props.item.primary_button;
|
|
164
226
|
}
|
|
165
227
|
|
|
166
228
|
if (props.item.hasOwnProperty('secondary_button')) {
|
|
167
|
-
|
|
229
|
+
basicForm.value.secondary_button = props.item.secondary_button;
|
|
168
230
|
}
|
|
169
231
|
|
|
232
|
+
const addLine = () => {
|
|
233
|
+
advancedForm.value.lines.push({title: "", description: ""});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const removeLine = (index) => {
|
|
237
|
+
advancedForm.value.lines.splice(index, 1);
|
|
238
|
+
emit("update", false);
|
|
239
|
+
};
|
|
240
|
+
|
|
170
241
|
const save = () => {
|
|
242
|
+
let form = hasLines.value ? advancedForm : basicForm;
|
|
171
243
|
emit("update", form.value);
|
|
172
244
|
|
|
173
245
|
if (props.saveEndpoint) {
|
|
174
|
-
axios.post(props.saveEndpoint, form.value).then
|
|
246
|
+
axios.post(props.saveEndpoint, form.value).then(() => {
|
|
175
247
|
if (props.cancelEndpoint) {
|
|
176
248
|
window.location.href = props.cancelEndpoint;
|
|
177
249
|
}
|
|
178
|
-
})
|
|
250
|
+
});
|
|
179
251
|
}
|
|
180
252
|
};
|
|
181
253
|
|
|
182
254
|
const close = () => {
|
|
255
|
+
let form = hasLines.value ? advancedForm : basicForm;
|
|
183
256
|
emit("close", form.value);
|
|
184
257
|
|
|
185
258
|
if (props.cancelEndpoint) {
|
|
@@ -187,10 +260,13 @@ const close = () => {
|
|
|
187
260
|
}
|
|
188
261
|
};
|
|
189
262
|
|
|
190
|
-
const descriptionWordCount =
|
|
191
|
-
const plainText =
|
|
263
|
+
const descriptionWordCount = (description) => {
|
|
264
|
+
const plainText = description?.replace(/<[^>]*>/g, ' ').trim();
|
|
192
265
|
const words = plainText.split(/\s+/).filter(word => word.length > 0);
|
|
193
266
|
return words.length;
|
|
194
|
-
}
|
|
267
|
+
};
|
|
195
268
|
|
|
196
|
-
|
|
269
|
+
const handleDeleteItem = (index) => {
|
|
270
|
+
modalRef?.value?.open(index);
|
|
271
|
+
};
|
|
272
|
+
</script>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="item-edit">
|
|
3
|
+
<div class="flex items-start gap-4 px-6 pt-4 h-full min-h-[90vh]">
|
|
4
|
+
<div class="flex flex-1 flex-col gap-4">
|
|
5
|
+
<card title="Description">
|
|
6
|
+
<div class="flex flex-col gap-8">
|
|
7
|
+
<div class="flex gap-4">
|
|
8
|
+
<input-wrapper
|
|
9
|
+
is-vertical
|
|
10
|
+
field="icon"
|
|
11
|
+
label-text="Icon *"
|
|
12
|
+
:value="form.icon"
|
|
13
|
+
>
|
|
14
|
+
<IconSelector id="icon" v-model="form.icon" />
|
|
15
|
+
</input-wrapper>
|
|
16
|
+
<input-wrapper
|
|
17
|
+
is-vertical
|
|
18
|
+
field="title"
|
|
19
|
+
label-text="Title *"
|
|
20
|
+
class="w-full"
|
|
21
|
+
:value="form.title"
|
|
22
|
+
:limit="51"
|
|
23
|
+
>
|
|
24
|
+
<input
|
|
25
|
+
v-model="form.title"
|
|
26
|
+
name="title"
|
|
27
|
+
type="text"
|
|
28
|
+
placeholder="Title"
|
|
29
|
+
:maxlength="51"
|
|
30
|
+
class="border-1 border-solid border-gray-300 rounded-lg bg-white w-full"
|
|
31
|
+
/>
|
|
32
|
+
</input-wrapper>
|
|
33
|
+
</div>
|
|
34
|
+
<input-wrapper
|
|
35
|
+
is-vertical
|
|
36
|
+
field="description"
|
|
37
|
+
label-text="Supporting Text *"
|
|
38
|
+
class="w-full mb-4"
|
|
39
|
+
:value="descriptionWordCount"
|
|
40
|
+
:show-count="true"
|
|
41
|
+
>
|
|
42
|
+
<QuillEditor v-model="form.description"/>
|
|
43
|
+
</input-wrapper>
|
|
44
|
+
</div>
|
|
45
|
+
</card>
|
|
46
|
+
<VButton v-if="form.hasOwnProperty('primary_button')" :button="form.primary_button" :sites="sites"/>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="flex w-full max-w-[356px] flex-col gap-4">
|
|
49
|
+
<card title="Visibility" supporting_text="Toggle off if you'd like to hide this item">
|
|
50
|
+
<VToggle v-model="form.public" name="public" label="Public" />
|
|
51
|
+
</card>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<slot>
|
|
55
|
+
<div class="flex justify-between space-x-xsSpace pt-xsSpace gap-4 sticky bottom-0 w-full bg-gray-200 py-2 px-6">
|
|
56
|
+
<a @click="close"
|
|
57
|
+
class="w-[200px] py-[9px] bg-white rounded-full border border-gray-300 shadow-xs text-md font-semibold hover:bg-gray-100 text-gray-700 text-center cursor-pointer">
|
|
58
|
+
Cancel
|
|
59
|
+
</a>
|
|
60
|
+
<a @click.prevent="save"
|
|
61
|
+
class="w-full py-[9px] rounded-full shadow-xs text-md font-semibold text-white bg-brand-600 hover:bg-brand-700 border border-brand-600 text-center cursor-pointer"
|
|
62
|
+
>Save changes</a>
|
|
63
|
+
</div>
|
|
64
|
+
</slot>
|
|
65
|
+
</div>
|
|
66
|
+
</template>
|
|
67
|
+
<script setup>
|
|
68
|
+
import { ref, computed } from "vue";
|
|
69
|
+
import QuillEditor from "@/components/common/QuillEditor.vue";
|
|
70
|
+
import IconSelector from "@/components/common/IconSelector.vue";
|
|
71
|
+
import Card from "@/components/common/Card.vue";
|
|
72
|
+
import InputWrapper from "@/components/common/InputWrapper.vue";
|
|
73
|
+
import VToggle from "@/components/common/Toggle.vue";
|
|
74
|
+
import axios from "axios";
|
|
75
|
+
import VButton from "@/components/common/Button.vue";
|
|
76
|
+
|
|
77
|
+
const emit = defineEmits(["update"]);
|
|
78
|
+
|
|
79
|
+
const props = defineProps({
|
|
80
|
+
item: {
|
|
81
|
+
type: Object,
|
|
82
|
+
required: true,
|
|
83
|
+
},
|
|
84
|
+
saveEndpoint: {
|
|
85
|
+
type: String,
|
|
86
|
+
required: false,
|
|
87
|
+
},
|
|
88
|
+
cancelEndpoint: {
|
|
89
|
+
type: String,
|
|
90
|
+
required: false,
|
|
91
|
+
},
|
|
92
|
+
sites: {
|
|
93
|
+
type: Array,
|
|
94
|
+
required: true,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const form = ref({
|
|
99
|
+
icon: props.item.icon,
|
|
100
|
+
public: props.item.public,
|
|
101
|
+
title: props.item.title,
|
|
102
|
+
description: props.item.description,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (props.item.hasOwnProperty('primary_button')) {
|
|
106
|
+
form.value.primary_button = props.item.primary_button;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (props.item.hasOwnProperty('secondary_button')) {
|
|
110
|
+
form.value.secondary_button = props.item.secondary_button;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const save = () => {
|
|
114
|
+
emit("update", form.value);
|
|
115
|
+
|
|
116
|
+
if (props.saveEndpoint) {
|
|
117
|
+
axios.post(props.saveEndpoint, form.value).then (() => {
|
|
118
|
+
if (props.cancelEndpoint) {
|
|
119
|
+
window.location.href = props.cancelEndpoint;
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const close = () => {
|
|
126
|
+
emit("close", form.value);
|
|
127
|
+
|
|
128
|
+
if (props.cancelEndpoint) {
|
|
129
|
+
window.location.href = props.cancelEndpoint;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const descriptionWordCount = computed(() => {
|
|
134
|
+
const plainText = form.value.description?.replace(/<[^>]*>/g, ' ').trim();
|
|
135
|
+
const words = plainText.split(/\s+/).filter(word => word.length > 0);
|
|
136
|
+
return words.length;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
</script>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="page-builder overflow-auto">
|
|
3
3
|
<div class="flex gap-4 px-6 overflow-auto">
|
|
4
|
-
<div class="flex w-[356px] flex-col gap-2 pb-10">
|
|
4
|
+
<div class="flex w-[356px] flex-col gap-2 pb-10 overflow-y-auto h-[100vh]">
|
|
5
5
|
<div v-for="(parent, sectionIndex) in modelValue.sections" class="border-b border-gray-200 pb-2">
|
|
6
6
|
<div
|
|
7
7
|
@click="openStates[sectionIndex] = !openStates[sectionIndex]"
|
|
@@ -38,8 +38,28 @@
|
|
|
38
38
|
</div>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
41
|
-
<div class="flex h-full flex-1 flex-col
|
|
42
|
-
<
|
|
41
|
+
<div class="flex h-full flex-1 flex-col mb-20">
|
|
42
|
+
<div class="mb-4 px-6 py-5 rounded-xl bg-gray-50" v-if="!selected">
|
|
43
|
+
<div class="text-lg pb-6 font-semibold text-gray-900 border-b border-gray-200">Settings</div>
|
|
44
|
+
<div class="flex flex-col gap-1 pt-6 text-gray-600">
|
|
45
|
+
<input-wrapper
|
|
46
|
+
field="breadcrumb"
|
|
47
|
+
label-text="Breadcrumb section"
|
|
48
|
+
class="w-full">
|
|
49
|
+
<select id="breadcrumb" v-model="modelValue.breadcrumb" name="url">
|
|
50
|
+
<option
|
|
51
|
+
:value="b"
|
|
52
|
+
v-for="(b) in breadcrumbs">
|
|
53
|
+
{{ b }}
|
|
54
|
+
</option>
|
|
55
|
+
</select>
|
|
56
|
+
</input-wrapper>
|
|
57
|
+
<p>This sets the section name shown in the breadcrumb (e.g. Home / Who We Are / Page). Leave blank to skip the section.</p>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="rounded-xl bg-gray-50 px-6 py-5" v-if="!selected">
|
|
61
|
+
<Instructions />
|
|
62
|
+
</div>
|
|
43
63
|
<component
|
|
44
64
|
class="min-h-[90vh]"
|
|
45
65
|
:is="currentComponent"
|
|
@@ -68,13 +88,16 @@ import { ref, markRaw, computed } from "vue";
|
|
|
68
88
|
import ChevronRight from "@/assets/img/icons/chevron-right.svg";
|
|
69
89
|
import ChevronDown from "@/assets/img/icons/chevron-down.svg";
|
|
70
90
|
import Instructions from "@/components/builders/Instructions.vue";
|
|
71
|
-
import VItems from "@/components/builders/
|
|
72
|
-
import VLinks from "@/components/builders/
|
|
73
|
-
import VHeader from "@/components/builders/
|
|
74
|
-
import VLogos from "@/components/builders/
|
|
75
|
-
import VCollectionCarousel from "@/components/builders/
|
|
76
|
-
import VNewsGrid from "@/components/builders/
|
|
77
|
-
import VVideoGrid from "@/components/builders/
|
|
91
|
+
import VItems from "@/components/builders/Items.vue";
|
|
92
|
+
import VLinks from "@/components/builders/Links.vue";
|
|
93
|
+
import VHeader from "@/components/builders/Header.vue";
|
|
94
|
+
import VLogos from "@/components/builders/Logos.vue";
|
|
95
|
+
import VCollectionCarousel from "@/components/builders/CollectionCarousel.vue";
|
|
96
|
+
import VNewsGrid from "@/components/builders/NewsGrid.vue";
|
|
97
|
+
import VVideoGrid from "@/components/builders/VideoGrid.vue";
|
|
98
|
+
import VParagraph from "@/components/builders/Paragraph.vue";
|
|
99
|
+
import VImageBlock from "@/components/builders/ImageBlock.vue";
|
|
100
|
+
import InputWrapper from "@/components/common/InputWrapper.vue";
|
|
78
101
|
|
|
79
102
|
const emit = defineEmits(["save", "close"]);
|
|
80
103
|
const props = defineProps({
|
|
@@ -88,6 +111,12 @@ const props = defineProps({
|
|
|
88
111
|
return []
|
|
89
112
|
}
|
|
90
113
|
},
|
|
114
|
+
breadcrumbs: {
|
|
115
|
+
type: Array,
|
|
116
|
+
default: () => {
|
|
117
|
+
return []
|
|
118
|
+
}
|
|
119
|
+
},
|
|
91
120
|
});
|
|
92
121
|
|
|
93
122
|
const openStates = ref(JSON.parse(window.localStorage.getItem("pageBuilderOpenStates")));
|
|
@@ -103,6 +132,10 @@ const componentMaps = ref({
|
|
|
103
132
|
carousel: markRaw(VCollectionCarousel),
|
|
104
133
|
news_grid: markRaw(VNewsGrid),
|
|
105
134
|
video_grid: markRaw(VVideoGrid),
|
|
135
|
+
paragraph: markRaw(VParagraph),
|
|
136
|
+
image_block: markRaw(VImageBlock),
|
|
137
|
+
timeline_items: markRaw(VItems),
|
|
138
|
+
link_cards: markRaw(VItems),
|
|
106
139
|
});
|
|
107
140
|
|
|
108
141
|
if (!openStates.value) {
|
|
@@ -17,6 +17,12 @@ 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
19
|
import CollectionGrid from "@/components/presenters/modules/CollectionGrid.vue";
|
|
20
|
+
import StandardHeader from "@/components/presenters/modules/StandardHeader.vue";
|
|
21
|
+
import Paragraph from "@/components/presenters/modules/Paragraph.vue";
|
|
22
|
+
import TwoColumnsImageContent from "@/components/presenters/modules/TwoColumnsImageContent.vue";
|
|
23
|
+
import Timeline from "@/components/presenters/modules/Timeline.vue";
|
|
24
|
+
import LinkCard from "@/components/presenters/modules/LinkCard.vue";
|
|
25
|
+
import LinkList from "@/components/presenters/modules/LinkList.vue";
|
|
20
26
|
|
|
21
27
|
const props = defineProps({
|
|
22
28
|
page: {
|
|
@@ -26,12 +32,18 @@ const props = defineProps({
|
|
|
26
32
|
});
|
|
27
33
|
|
|
28
34
|
const componentMaps = ref({
|
|
35
|
+
paragraph: markRaw(Paragraph),
|
|
36
|
+
standard_header: markRaw(StandardHeader),
|
|
29
37
|
header: markRaw(HeroHeader),
|
|
30
38
|
quick_links: markRaw(QuickLinks),
|
|
31
39
|
tabs: markRaw(VTabs),
|
|
32
40
|
logo: markRaw(LogoCloud),
|
|
33
41
|
collection_carousel: markRaw(CollectionCarousel),
|
|
34
42
|
collection_grid: markRaw(CollectionGrid),
|
|
43
|
+
two_column_image_content: markRaw(TwoColumnsImageContent),
|
|
44
|
+
timeline: markRaw(Timeline),
|
|
45
|
+
link_card: markRaw(LinkCard),
|
|
46
|
+
link_list: markRaw(LinkList),
|
|
35
47
|
});
|
|
36
48
|
|
|
37
49
|
const currentComponent = (section) => {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="rounded-xl bg-gray-50 px-6 py-5">
|
|
3
|
+
<div class="flex flex-col">
|
|
4
|
+
<div class="flex flex-col gap-4 mb-4 border-b border-gray-200 pb-4">
|
|
5
|
+
<p class="text-lg font-semibold text-gray-900 border-b border-gray-200 pb-4">
|
|
6
|
+
Carousel
|
|
7
|
+
</p>
|
|
8
|
+
<div class="flex flex-col gap-6">
|
|
9
|
+
<div class="flex flex-col gap-1.5">
|
|
10
|
+
<input-wrapper
|
|
11
|
+
v-if="componentData.button"
|
|
12
|
+
is-vertical
|
|
13
|
+
field="title"
|
|
14
|
+
label-text="Button label *"
|
|
15
|
+
class="w-full mb-4"
|
|
16
|
+
:value="componentData.button.title"
|
|
17
|
+
:limit="51"
|
|
18
|
+
>
|
|
19
|
+
<input
|
|
20
|
+
v-model="componentData.button.title"
|
|
21
|
+
name="title"
|
|
22
|
+
type="text"
|
|
23
|
+
placeholder="Title"
|
|
24
|
+
:maxlength="51"
|
|
25
|
+
class="border-1 border-solid border-gray-300 rounded-lg bg-white w-full"
|
|
26
|
+
/>
|
|
27
|
+
</input-wrapper>
|
|
28
|
+
<linked-to
|
|
29
|
+
v-if="componentData?.button"
|
|
30
|
+
label="Link to"
|
|
31
|
+
name="button"
|
|
32
|
+
v-model:type="componentData.button.type"
|
|
33
|
+
v-model:url="componentData.button.url"
|
|
34
|
+
v-model:openInNewTab="componentData.button.open_in_new_tab"
|
|
35
|
+
:sites="sites"
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
<div>
|
|
41
|
+
<h3 v-if="componentData.content?.label" class="text-base text-gray-900 font-semibold">
|
|
42
|
+
{{ componentData.content.label }}</h3>
|
|
43
|
+
<p v-if="componentData.content?.supportive_text" class="text-gray-600 text-base font-normal mt-2">
|
|
44
|
+
{{ componentData.content.supportive_text }}</p>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
<script setup>
|
|
50
|
+
import {ref} from "vue";
|
|
51
|
+
import {defaultProps} from "@/components/helpers/defaultProps";
|
|
52
|
+
import LinkedTo from "@/components/common/LinkedTo.vue";
|
|
53
|
+
import InputWrapper from "@/components/common/InputWrapper.vue";
|
|
54
|
+
|
|
55
|
+
const emit = defineEmits(["update"]);
|
|
56
|
+
|
|
57
|
+
const props = defineProps({
|
|
58
|
+
...defaultProps,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const componentData = ref(props.data.component);
|
|
62
|
+
</script>
|