@dcodegroup-au/page-builder 0.2.7 → 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 +33346 -11815
- 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/{VCarouselPresenter.vue → CarouselPresenter.vue} +8 -18
- 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 +3 -3
- package/src/components/presenters/modules/LinkCard.vue +55 -0
- package/src/components/presenters/modules/LinkList.vue +51 -0
- package/src/components/presenters/modules/LogoCloud.vue +1 -1
- 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/components/presenters/overridables/VCarouselPrimaryButton.vue +28 -0
- 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 -121
- 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/{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
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="rounded-xl bg-gray-50 px-6 py-5">
|
|
3
|
+
<div class="flex justify-between pb-2">
|
|
4
|
+
<div class="flex justify-between w-full py-1">
|
|
5
|
+
<div>
|
|
6
|
+
<p class="text-lg font-semibold text-gray-900">
|
|
7
|
+
{{ componentData.name }}
|
|
8
|
+
</p>
|
|
9
|
+
<p class="text-sm text-gray-600 mt-1">
|
|
10
|
+
This section can contain up to {{ componentData.max_items }} videos
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
<div>
|
|
14
|
+
<a
|
|
15
|
+
@click="addItem"
|
|
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 py-2 font-semibold text-white hover:bg-brand-600"
|
|
17
|
+
:class="{ 'border-gray-100 bg-gray-100 !text-gray-400 hover:bg-gray-100': componentData.data?.length >= componentData.max_items }"
|
|
18
|
+
>
|
|
19
|
+
<PlusIcon class="h-5 w-5"></PlusIcon>
|
|
20
|
+
<span>Add</span>
|
|
21
|
+
</a>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="flex flex-col gap-3">
|
|
26
|
+
<div
|
|
27
|
+
v-for="(item, index) in componentData.data"
|
|
28
|
+
class="flex flex-col gap-4 rounded-xl px-6 py-4 bg-gray-200"
|
|
29
|
+
:key="index"
|
|
30
|
+
:ref="index === componentData.data.length - 1 ? (el) => (lastItemRef = el) : null"
|
|
31
|
+
>
|
|
32
|
+
<div class="flex items-center justify-between">
|
|
33
|
+
<div class="text-lg font-semibold text-gray-900">
|
|
34
|
+
Video #{{ index + 1 }}
|
|
35
|
+
</div>
|
|
36
|
+
<div class="relative flex items-end">
|
|
37
|
+
<ActionMenu @removeItem="handleDeleteItem(index)"/>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="flex flex-col gap-6">
|
|
41
|
+
<div class="flex flex-col gap-1.5">
|
|
42
|
+
<VFileUpload
|
|
43
|
+
name="image"
|
|
44
|
+
type="video"
|
|
45
|
+
height="h-[250px]"
|
|
46
|
+
v-model="item.video"
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<VModal ref="modalRef" entity="logo" :callback="deleteCallback"></VModal>
|
|
54
|
+
</template>
|
|
55
|
+
<script setup>
|
|
56
|
+
import {ref, inject} from "vue";
|
|
57
|
+
import PlusIcon from "@/assets/img/icons/plus.svg";
|
|
58
|
+
import {defaultProps} from "@/components/helpers/defaultProps";
|
|
59
|
+
import ActionMenu from "@/components/common/ActionMenu.vue";
|
|
60
|
+
import VModal from "@/components/common/Modal.vue";
|
|
61
|
+
import DefaultFileUpload from "@/components/common/FileUpload.vue";
|
|
62
|
+
|
|
63
|
+
// Inject the FileUpload component or fallback to the default one
|
|
64
|
+
const VFileUpload = inject("VFileUpload", DefaultFileUpload);
|
|
65
|
+
|
|
66
|
+
const emit = defineEmits(["update"]);
|
|
67
|
+
|
|
68
|
+
const props = defineProps({
|
|
69
|
+
...defaultProps,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const componentData = ref(props.data.component);
|
|
73
|
+
const modalRef = ref(null);
|
|
74
|
+
const lastItemRef = ref(null);
|
|
75
|
+
const deleteItemIndex = ref(null);
|
|
76
|
+
|
|
77
|
+
function addItem() {
|
|
78
|
+
if (!componentData.value.hasOwnProperty('data')) {
|
|
79
|
+
componentData.value.data = [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (componentData.value.data?.length >= componentData.value.max_items) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
componentData.value.data?.push({
|
|
86
|
+
video: null,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
emit("update", false);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const handleDeleteItem = (index) => {
|
|
93
|
+
deleteItemIndex.value = index;
|
|
94
|
+
modalRef?.value?.open(index);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const deleteCallback = (index) => {
|
|
98
|
+
componentData.value.data?.splice(index, 1);
|
|
99
|
+
emit("update", false);
|
|
100
|
+
};
|
|
101
|
+
</script>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<card v-if="button" :title="button?.name ?? 'Primary button'" class="flex flex-col gap-4 rounded-xl px-6 py-5 mt-4" :classes="classes" :class="classes">
|
|
3
|
+
<div class="flex flex-col gap-8">
|
|
4
|
+
<VToggle name="show" v-model="button.show" title="Show Button" />
|
|
5
|
+
<input-wrapper
|
|
6
|
+
is-vertical
|
|
7
|
+
field="button.label"
|
|
8
|
+
label-text="Button Label *"
|
|
9
|
+
class="w-full mb-4"
|
|
10
|
+
:value="button.label"
|
|
11
|
+
:limit="20"
|
|
12
|
+
>
|
|
13
|
+
<input
|
|
14
|
+
v-model="button.label"
|
|
15
|
+
name="button.label"
|
|
16
|
+
type="text"
|
|
17
|
+
placeholder="Label"
|
|
18
|
+
:maxlength="20"
|
|
19
|
+
class="border-1 border-solid border-gray-300 rounded-lg bg-white w-full"
|
|
20
|
+
/>
|
|
21
|
+
</input-wrapper>
|
|
22
|
+
<linked-to
|
|
23
|
+
name="primary"
|
|
24
|
+
v-model:type="button.type"
|
|
25
|
+
v-model:url="button.url"
|
|
26
|
+
v-model:openInNewTab="button.open_in_new_tab"
|
|
27
|
+
:sites="sites"
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
</card>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup>
|
|
34
|
+
import Card from "@/components/common/Card.vue";
|
|
35
|
+
import InputWrapper from "@/components/common/InputWrapper.vue";
|
|
36
|
+
import VToggle from "@/components/common/Toggle.vue";
|
|
37
|
+
import LinkedTo from "@/components/common/LinkedTo.vue";
|
|
38
|
+
|
|
39
|
+
const props = defineProps({
|
|
40
|
+
button: {
|
|
41
|
+
type: Object,
|
|
42
|
+
required: true,
|
|
43
|
+
},
|
|
44
|
+
sites: {
|
|
45
|
+
type: Array,
|
|
46
|
+
required: true,
|
|
47
|
+
},
|
|
48
|
+
classes: {
|
|
49
|
+
type: String,
|
|
50
|
+
default: 'bg-gray-50'
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="rounded-xl
|
|
2
|
+
<div class="rounded-xl px-6 py-5" :class="classes">
|
|
3
3
|
<div class="pb-5" v-if="title">
|
|
4
4
|
<div class="text-lg font-semibold text-gray-900">
|
|
5
5
|
{{ title }}
|
|
@@ -23,5 +23,9 @@ const props = defineProps({
|
|
|
23
23
|
type: String,
|
|
24
24
|
default: "",
|
|
25
25
|
},
|
|
26
|
+
classes: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: "bg-gray-50 p",
|
|
29
|
+
}
|
|
26
30
|
});
|
|
27
31
|
</script>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="IconComponent" @click="handleClick">
|
|
4
|
+
<component :class="iconClasses" :is="IconComponent" />
|
|
5
|
+
</div>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
<script setup>
|
|
9
|
+
import { defineEmits, onMounted, shallowRef } from "vue";
|
|
10
|
+
import * as Icons from "@/components/helpers/bundleIcons.js";
|
|
11
|
+
|
|
12
|
+
const props = defineProps({
|
|
13
|
+
icon: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: true,
|
|
16
|
+
},
|
|
17
|
+
iconClasses: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: "h-8 w-8",
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const IconComponent = shallowRef(null);
|
|
24
|
+
const emit = defineEmits(["selectIcon"]);
|
|
25
|
+
|
|
26
|
+
const loadIcon = (name) => {
|
|
27
|
+
try {
|
|
28
|
+
IconComponent.value = Icons[name];
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(`Failed to load icon: ${name}`, error);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleClick = () => {
|
|
35
|
+
emit("selectIcon", props.icon);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
onMounted(() => {
|
|
39
|
+
loadIcon(props.icon);
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="relative block" v-click-outside="handleClickOutside">
|
|
3
|
+
<div
|
|
4
|
+
class="flex h-[40px] w-[40px] cursor-pointer items-center justify-center rounded-full border border-gray-300 bg-white hover:border-brand-300 active:border-brand-300 active:ring-4 active:ring-brand-700/[.24]"
|
|
5
|
+
@click="toggleDropdown"
|
|
6
|
+
>
|
|
7
|
+
<IconComponent v-if="model" :icon="model" icon-classes="w-5 h-5" :key="model"></IconComponent>
|
|
8
|
+
<PlusIcon class="h-5 w-5" v-else/>
|
|
9
|
+
</div>
|
|
10
|
+
<div
|
|
11
|
+
v-show="isDropdownOpen"
|
|
12
|
+
class="absolute left-[70%] top-[100%] z-10 flex w-[504px] flex-col rounded-xl border border-gray-300 bg-white"
|
|
13
|
+
>
|
|
14
|
+
<div class="flex flex-col gap-4 p-4">
|
|
15
|
+
<div class="relative">
|
|
16
|
+
<input
|
|
17
|
+
v-model="search"
|
|
18
|
+
type="text"
|
|
19
|
+
placeholder="Search all 1000+ icons"
|
|
20
|
+
class="w-full border border-gray-300 rounded px-3 py-2"
|
|
21
|
+
/>
|
|
22
|
+
<button
|
|
23
|
+
v-if="search.length > 0"
|
|
24
|
+
@click="search = ''"
|
|
25
|
+
class="absolute right-2 top-1/2 h-3 w-3 -translate-y-1/2 cursor-pointer"
|
|
26
|
+
>
|
|
27
|
+
✕
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="flex flex-col gap-2">
|
|
31
|
+
<p class="text-sm font-medium text-gray-900">
|
|
32
|
+
<span v-if="icons.length > 0 && search.length > 0"> Search result </span>
|
|
33
|
+
<span v-else-if="icons.length < 1 && search.length > 0"> No search result </span>
|
|
34
|
+
<span v-else-if="search.length < 1 && icons.length > 0"> Frequently used icons </span>
|
|
35
|
+
</p>
|
|
36
|
+
<div class="flex max-h-[196px] flex-wrap gap-1.5 overflow-y-auto">
|
|
37
|
+
<IconComponent
|
|
38
|
+
class="cursor-pointer rounded p-1 hover:bg-gray-100"
|
|
39
|
+
icon-classes="w-8 h-8"
|
|
40
|
+
:icon="icon.name"
|
|
41
|
+
:key="icon.name"
|
|
42
|
+
v-for="icon in icons"
|
|
43
|
+
@selectIcon="update"
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="flex items-center justify-between rounded-b-xl bg-gray-100 px-4 py-3">
|
|
49
|
+
<p class="text-sm font-medium text-gray-900">Not sure about the icon names?</p>
|
|
50
|
+
<a
|
|
51
|
+
href="https://www.untitledui.com/free-icons"
|
|
52
|
+
target="_blank"
|
|
53
|
+
class="text-blue-500 underline"
|
|
54
|
+
>
|
|
55
|
+
View all icons
|
|
56
|
+
</a>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<script>
|
|
63
|
+
import { useClickOutside } from "@/components/helpers/clickOutSide.js";
|
|
64
|
+
export default {
|
|
65
|
+
directives: {
|
|
66
|
+
clickOutside: useClickOutside,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<script setup>
|
|
72
|
+
import {ref, watch, onMounted} from "vue";
|
|
73
|
+
import PlusIcon from "@/assets/img/icons/plus.svg";
|
|
74
|
+
import IconComponent from "./Icon.vue";
|
|
75
|
+
import iconsData from "@/assets/icons.json";
|
|
76
|
+
|
|
77
|
+
const model = ref(null);
|
|
78
|
+
const search = ref("");
|
|
79
|
+
const icons = ref([]);
|
|
80
|
+
const isDropdownOpen = ref(false);
|
|
81
|
+
|
|
82
|
+
function toggleDropdown() {
|
|
83
|
+
isDropdownOpen.value = !isDropdownOpen.value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
watch(search, (value) => {
|
|
87
|
+
icons.value = iconsData.filter((icon) =>
|
|
88
|
+
icon.name.toLowerCase().includes(value.toLowerCase())
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
function update(event) {
|
|
93
|
+
model.value = event;
|
|
94
|
+
isDropdownOpen.value = false; // Close dropdown after selection
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function handleClickOutside() {
|
|
98
|
+
if (isDropdownOpen.value) {
|
|
99
|
+
toggleDropdown();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
onMounted(() => {
|
|
104
|
+
icons.value = iconsData.slice(0, 30); // Load the first 30 icons by default
|
|
105
|
+
});
|
|
106
|
+
</script>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
<div class="flex flex-col gap-2">
|
|
20
|
-
<component :is="getComponentByType(type)" v-model:url="url" :sites="sites"/>
|
|
20
|
+
<component :is="getComponentByType(type)" v-model:url="url" v-model="url" :sites="sites"/>
|
|
21
21
|
<input type="hidden" name="url" :value="url" />
|
|
22
22
|
</div>
|
|
23
23
|
<div class="flex items-center gap-2">
|
|
@@ -27,9 +27,14 @@
|
|
|
27
27
|
</template>
|
|
28
28
|
|
|
29
29
|
<script setup>
|
|
30
|
-
import
|
|
30
|
+
import {inject} from "vue";
|
|
31
|
+
import VToggle from "@/components/common/Toggle.vue";
|
|
31
32
|
import SiteContent from "@/components/common/SiteContent.vue";
|
|
32
33
|
import ExternalPage from "@/components/common/ExternalPage.vue";
|
|
34
|
+
import DefaultFileUpload from "@/components/common/FileUpload.vue";
|
|
35
|
+
|
|
36
|
+
// Inject the FileUpload component or fallback to the default one
|
|
37
|
+
const VFileUpload = inject("VFileUpload", DefaultFileUpload);
|
|
33
38
|
|
|
34
39
|
const type = defineModel("type", { default: "site-content" });
|
|
35
40
|
const url = defineModel("url");
|
|
@@ -39,7 +44,7 @@ defineProps({
|
|
|
39
44
|
label: { type: String, required: false },
|
|
40
45
|
sites: { type: Object, required: true },
|
|
41
46
|
name: { type: String, required: true },
|
|
42
|
-
options: { type: Array, required: false, default: ["site-content", "external-page"] },
|
|
47
|
+
options: { type: Array, required: false, default: ["site-content", "external-page", 'download'] },
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
const toReadableWords = (input) => {
|
|
@@ -54,6 +59,7 @@ const getComponentByType = (type) => {
|
|
|
54
59
|
const components = {
|
|
55
60
|
"site-content": SiteContent,
|
|
56
61
|
"external-page": ExternalPage,
|
|
62
|
+
"download": VFileUpload,
|
|
57
63
|
};
|
|
58
64
|
return components[type] || null;
|
|
59
65
|
};
|