@dcodegroup-au/page-builder 0.2.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/page-builder.css +2 -2
  2. package/dist/page-builder.es.js +33363 -11858
  3. package/dist/page-builder.umd.js +59 -59
  4. package/example/src/App.vue +40 -679
  5. package/example/src/main.js +2 -1
  6. package/example/src/pages/BestLife.js +351 -0
  7. package/example/src/pages/Home.js +677 -0
  8. package/package.json +1 -1
  9. package/src/assets/icons.json +3569 -0
  10. package/src/components/ItemEdit.vue +147 -71
  11. package/src/components/LinkCardEdit.vue +139 -0
  12. package/src/components/PageBuilder.vue +43 -10
  13. package/src/components/PageRender.vue +12 -0
  14. package/src/components/builders/CollectionCarousel.vue +62 -0
  15. package/src/components/builders/Header.vue +82 -0
  16. package/src/components/builders/ImageBlock.vue +56 -0
  17. package/src/components/builders/Items.vue +112 -0
  18. package/src/components/builders/Links.vue +123 -0
  19. package/src/components/builders/Logos.vue +122 -0
  20. package/src/components/builders/NewsGrid.vue +59 -0
  21. package/src/components/builders/Paragraph.vue +93 -0
  22. package/src/components/builders/VideoGrid.vue +101 -0
  23. package/src/components/common/Button.vue +53 -0
  24. package/src/components/common/Card.vue +5 -1
  25. package/src/components/common/FileUpload.vue +1 -1
  26. package/src/components/common/Icon.vue +41 -0
  27. package/src/components/common/IconSelector.vue +106 -0
  28. package/src/components/common/LinkedTo.vue +9 -3
  29. package/src/components/helpers/bundleIcons.js +1189 -0
  30. package/src/components/index.js +2 -1
  31. package/src/components/presenters/components/{VCollectionGridPresenter.vue → CollectionGridPresenter.vue} +0 -5
  32. package/src/components/presenters/modules/CollectionCarousel.vue +2 -2
  33. package/src/components/presenters/modules/CollectionGrid.vue +2 -2
  34. package/src/components/presenters/modules/HeroHeader.vue +2 -2
  35. package/src/components/presenters/modules/LinkCard.vue +55 -0
  36. package/src/components/presenters/modules/LinkList.vue +51 -0
  37. package/src/components/presenters/modules/Paragraph.vue +26 -0
  38. package/src/components/presenters/modules/QuickLinks.vue +2 -2
  39. package/src/components/presenters/modules/StandardHeader.vue +32 -0
  40. package/src/components/presenters/modules/Timeline.vue +53 -0
  41. package/src/components/presenters/modules/TwoColumnsImageContent.vue +36 -0
  42. package/src/components/presenters/modules/VTabs.vue +2 -2
  43. package/src/utils/generateIconBundle.js +33 -0
  44. package/src/utils/generateIconJson.js +30 -0
  45. package/tailwind.config.js +5 -0
  46. package/src/components/builders/BaseModuleForm.vue +0 -86
  47. package/src/components/builders/LogoBuilder.vue +0 -167
  48. package/src/components/builders/PageBuilderCarousel.vue +0 -18
  49. package/src/components/builders/PageBuilderGrid.vue +0 -18
  50. package/src/components/builders/PageBuilderSectionHeader.vue +0 -30
  51. package/src/components/builders/PageModal.vue +0 -92
  52. package/src/components/builders/VCollectionCarousel.vue +0 -58
  53. package/src/components/builders/VHeader.vue +0 -55
  54. package/src/components/builders/VItems.vue +0 -110
  55. package/src/components/builders/VLinks.vue +0 -121
  56. package/src/components/builders/VLogos.vue +0 -120
  57. package/src/components/builders/VNewsGrid.vue +0 -55
  58. package/src/components/builders/VVideoGrid.vue +0 -99
  59. package/src/components/common/forms/LogosForm.vue +0 -39
  60. package/src/components/common/forms/PageBuilderLinksForm.vue +0 -39
  61. package/src/components/common/forms/SectionHeaderForm.vue +0 -45
  62. package/src/components/common/forms/TabForm.vue +0 -90
  63. /package/src/components/common/{VModal.vue → Modal.vue} +0 -0
  64. /package/src/components/common/{VToggle.vue → Toggle.vue} +0 -0
  65. /package/src/components/presenters/components/{VCarouselPresenter.vue → CarouselPresenter.vue} +0 -0
  66. /package/src/components/presenters/components/{VHeaderPresenter.vue → HeaderPresenter.vue} +0 -0
  67. /package/src/components/presenters/components/{VLinkPresenter.vue → LinkPresenter.vue} +0 -0
  68. /package/src/components/presenters/components/{VSliderPresenter.vue → SliderPresenter.vue} +0 -0
  69. /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 bg-gray-50 px-6 py-5">
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>
@@ -138,7 +138,7 @@ onMounted(() => {
138
138
  }
139
139
 
140
140
  .dz-preview {
141
- @apply bg-gray-50 flex gap-1 h-[198px] p-4 items-center rounded-lg;
141
+ @apply bg-gray-200 flex gap-1 h-[198px] p-4 items-center rounded-lg;
142
142
 
143
143
  .dz-image {
144
144
  @apply w-[100px] object-cover;
@@ -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 VToggle from "@/components/common/VToggle.vue";
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
  };