@myissue/vue-website-page-builder 3.0.1

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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +470 -0
  3. package/dist/android-chrome-192x192.png +0 -0
  4. package/dist/android-chrome-512x512.png +0 -0
  5. package/dist/apple-touch-icon.png +0 -0
  6. package/dist/browserconfig.xml +9 -0
  7. package/dist/components.json +36 -0
  8. package/dist/favicon-16x16.png +0 -0
  9. package/dist/favicon-32x32.png +0 -0
  10. package/dist/favicon.ico +0 -0
  11. package/dist/home/features.jpg +0 -0
  12. package/dist/home/page-builder-example.jpg +0 -0
  13. package/dist/logo/logo.svg +49 -0
  14. package/dist/mstile-310x150.png +0 -0
  15. package/dist/mstile-310x310.png +0 -0
  16. package/dist/page-builder/2-images-text-top.png +0 -0
  17. package/dist/page-builder/2-images.png +0 -0
  18. package/dist/page-builder/3-images.png +0 -0
  19. package/dist/page-builder/6-images.png +0 -0
  20. package/dist/page-builder/image.png +0 -0
  21. package/dist/page-builder/placeholder.jpg +0 -0
  22. package/dist/page-builder/two-vertical-images.png +0 -0
  23. package/dist/placeholder_image.jpg +0 -0
  24. package/dist/robots.txt +2 -0
  25. package/dist/vue-website-page-builder.css +1 -0
  26. package/dist/vue-website-page-builder.js +24794 -0
  27. package/dist/vue-website-page-builder.umd.cjs +195 -0
  28. package/package.json +99 -0
  29. package/src/App.vue +122 -0
  30. package/src/Components/Homepage/Footer.vue +42 -0
  31. package/src/Components/Homepage/HomeSection.vue +540 -0
  32. package/src/Components/Homepage/Navbar.vue +30 -0
  33. package/src/Components/Layouts/FullWidthElement.vue +34 -0
  34. package/src/Components/Loaders/FullScreenSpinner.vue +13 -0
  35. package/src/Components/Loaders/SmallUniversalSpinner.vue +26 -0
  36. package/src/Components/MediaLibrary/SidebarUnsplash.vue +40 -0
  37. package/src/Components/MediaLibrary/Unsplash.vue +306 -0
  38. package/src/Components/Modals/DynamicModal.vue +476 -0
  39. package/src/Components/Modals/MediaLibraryModal.vue +418 -0
  40. package/src/Components/Modals/Modal.vue +102 -0
  41. package/src/Components/Modals/PageBuilderModal.vue +233 -0
  42. package/src/Components/Modals/PageBuilderPreviewModal.vue +123 -0
  43. package/src/Components/PageBuilder/DropdownsPlusToggles/OptionsDropdown.vue +202 -0
  44. package/src/Components/PageBuilder/DropdownsPlusToggles/SaveDesign.vue +7 -0
  45. package/src/Components/PageBuilder/EditorMenu/Editables/BackgroundColorEditor.vue +91 -0
  46. package/src/Components/PageBuilder/EditorMenu/Editables/BorderRadius.vue +163 -0
  47. package/src/Components/PageBuilder/EditorMenu/Editables/Borders.vue +164 -0
  48. package/src/Components/PageBuilder/EditorMenu/Editables/ClassEditor.vue +80 -0
  49. package/src/Components/PageBuilder/EditorMenu/Editables/ComponentTopMenu.vue +93 -0
  50. package/src/Components/PageBuilder/EditorMenu/Editables/DeleteElement.vue +42 -0
  51. package/src/Components/PageBuilder/EditorMenu/Editables/EditGetElement.vue +338 -0
  52. package/src/Components/PageBuilder/EditorMenu/Editables/ElementEditor.vue +58 -0
  53. package/src/Components/PageBuilder/EditorMenu/Editables/ImageEditor.vue +82 -0
  54. package/src/Components/PageBuilder/EditorMenu/Editables/LinkEditor.vue +301 -0
  55. package/src/Components/PageBuilder/EditorMenu/Editables/ManageBackgroundOpacity.vue +109 -0
  56. package/src/Components/PageBuilder/EditorMenu/Editables/ManageOpacity.vue +107 -0
  57. package/src/Components/PageBuilder/EditorMenu/Editables/OpacityEditor.vue +16 -0
  58. package/src/Components/PageBuilder/EditorMenu/Editables/PaddingPlusMargin.vue +134 -0
  59. package/src/Components/PageBuilder/EditorMenu/Editables/TextColorEditor.vue +91 -0
  60. package/src/Components/PageBuilder/EditorMenu/Editables/Typography.vue +199 -0
  61. package/src/Components/PageBuilder/EditorMenu/EditorAccordion.vue +42 -0
  62. package/src/Components/PageBuilder/EditorMenu/RightSidebarEditor.vue +113 -0
  63. package/src/Components/PageBuilder/Settings/AdvancedPageBuilderSettings.vue +229 -0
  64. package/src/Components/PageBuilder/Settings/PageBuilderSettings.vue +101 -0
  65. package/src/Components/PageBuilder/Slidebars/SlideOverRight.vue +91 -0
  66. package/src/Components/PageBuilder/Slidebars/SlideOverRightParent.vue +91 -0
  67. package/src/Components/Search/SearchComponents.vue +259 -0
  68. package/src/Components/TipTap/TipTap.vue +32 -0
  69. package/src/Components/TipTap/TipTapInput.vue +325 -0
  70. package/src/PageBuilder/PageBuilder.vue +347 -0
  71. package/src/PageBuilder/Preview.vue +24 -0
  72. package/src/composables/PageBuilder.ts +1483 -0
  73. package/src/composables/delay.ts +5 -0
  74. package/src/composables/extract-text-content-html.ts +34 -0
  75. package/src/composables/isObject.ts +6 -0
  76. package/src/composables/usePromise.ts +10 -0
  77. package/src/composables/vueFetch.ts +278 -0
  78. package/src/css/app.css +614 -0
  79. package/src/index.ts +16 -0
  80. package/src/main.ts +11 -0
  81. package/src/stores/media-library.ts +34 -0
  82. package/src/stores/page-builder-state.ts +461 -0
  83. package/src/stores/unsplash.ts +107 -0
  84. package/src/stores/user.ts +30 -0
  85. package/src/types/global.d.ts +49 -0
  86. package/src/utils/builder/compiledCSS.ts +2205 -0
  87. package/src/utils/builder/html-doc-declaration-with-components.ts +7 -0
  88. package/src/utils/builder/html-elements/componentHelpers.ts +101 -0
  89. package/src/utils/builder/tailwaind-colors.ts +503 -0
  90. package/src/utils/builder/tailwind-border-radius.ts +67 -0
  91. package/src/utils/builder/tailwind-border-style-width-color.ts +272 -0
  92. package/src/utils/builder/tailwind-font-sizes.ts +76 -0
  93. package/src/utils/builder/tailwind-font-styles.ts +24 -0
  94. package/src/utils/builder/tailwind-opacities.ts +45 -0
  95. package/src/utils/builder/tailwind-padding-margin.ts +159 -0
@@ -0,0 +1,325 @@
1
+ <script setup>
2
+ import { Editor, useEditor, EditorContent } from '@tiptap/vue-3'
3
+ import StarterKit from '@tiptap/starter-kit'
4
+ import { computed, onBeforeMount, onMounted, ref, watch } from 'vue'
5
+ import PageBuilder from '@/composables/PageBuilder.ts'
6
+ import Link from '@tiptap/extension-link'
7
+ import DynamicModal from '@/Components/Modals/DynamicModal.vue'
8
+ import { usePageBuilderStateStore } from '@/stores/page-builder-state'
9
+ import { useMediaLibraryStore } from '@/stores/media-library'
10
+
11
+ const mediaLibraryStore = useMediaLibraryStore()
12
+ const pageBuilderStateStore = usePageBuilderStateStore()
13
+ const showModalUrl = ref(false)
14
+
15
+ // use dynamic model
16
+ const typeModal = ref('')
17
+ const gridColumnModal = ref(Number(1))
18
+ const titleModal = ref('')
19
+ const descriptionModal = ref('')
20
+ const firstButtonModal = ref('')
21
+ const secondButtonModal = ref(null)
22
+ const thirdButtonModal = ref(null)
23
+ // set dynamic modal handle functions
24
+ const firstModalButtonFunction = ref(null)
25
+ const secondModalButtonFunction = ref(null)
26
+ const thirdModalButtonFunction = ref(null)
27
+
28
+ const pageBuilder = new PageBuilder(pageBuilderStateStore, mediaLibraryStore)
29
+
30
+ const getElement = computed(() => {
31
+ return pageBuilderStateStore.getElement
32
+ })
33
+ const textContentVueModel = ref('')
34
+
35
+ const textContent = computed(() => {
36
+ if (editor.value) {
37
+ return editor.value.getHTML()
38
+ }
39
+ })
40
+
41
+ const getElementtextContentLength = ref(0)
42
+
43
+ watch(getElement, (newVal) => {
44
+ const tempContainer = document.createElement('div')
45
+
46
+ if (newVal) {
47
+ tempContainer.innerHTML = newVal
48
+ const textContent = tempContainer.textContent
49
+ getElementtextContentLength.value = textContent.length
50
+ }
51
+ })
52
+
53
+ // Check if any of the child elements have the tagName "IMG"
54
+
55
+ const editor = useEditor({
56
+ content: '',
57
+ extensions: [
58
+ StarterKit,
59
+ Link.configure({
60
+ openOnClick: false,
61
+ }),
62
+ ],
63
+ editorProps: {
64
+ attributes: {
65
+ class: 'prose-sm sm:prose-sm lg:prose-sm focus:outline-none',
66
+ },
67
+ },
68
+ })
69
+
70
+ // watch for changes in textContent and update store and textContentVueModel
71
+ watch(textContent, (newValue) => {
72
+ if (!pageBuilder.selectedElementIsValidText()) return
73
+
74
+ if (getElement.value) {
75
+ pageBuilderStateStore.setTextAreaVueModel(newValue)
76
+
77
+ if (typeof newValue === 'string' && newValue !== textContentVueModel.value) {
78
+ pageBuilder.handleTextInput(newValue)
79
+ }
80
+ }
81
+ })
82
+
83
+ // pageBuilder.selectedElementIsValidText(newValue);
84
+
85
+ const TipTapSetContent = function () {
86
+ if (!pageBuilder.selectedElementIsValidText()) return
87
+
88
+ if (editor.value) {
89
+ editor.value.commands.setContent(getElement.value.innerHTML)
90
+ }
91
+ }
92
+
93
+ watch(getElement, () => {
94
+ TipTapSetContent()
95
+ })
96
+
97
+ // Manage URL
98
+ const urlEnteret = ref('')
99
+ const newUpdatedExistingURL = ref('')
100
+ const urlError = ref(null)
101
+
102
+ watch(urlEnteret, (newVal) => {
103
+ newUpdatedExistingURL.value = newVal
104
+ })
105
+ const handleURL = function () {
106
+ urlEnteret.value = editor.value.getAttributes('link').href
107
+
108
+ showModalUrl.value = true
109
+ typeModal.value = 'success'
110
+ gridColumnModal.value = 2
111
+ titleModal.value = 'Enter URL'
112
+ descriptionModal.value = null
113
+ firstButtonModal.value = 'Close'
114
+ secondButtonModal.value = urlEnteret.value ? 'Remove url' : null
115
+ thirdButtonModal.value = 'Save'
116
+
117
+ // handle click
118
+ firstModalButtonFunction.value = function () {
119
+ showModalUrl.value = false
120
+ urlError.value = null
121
+ }
122
+
123
+ // handle click
124
+ secondModalButtonFunction.value = function () {
125
+ editor.value.chain().focus().extendMarkRange('link').unsetLink().run()
126
+ showModalUrl.value = false
127
+ }
128
+
129
+ // handle click
130
+ thirdModalButtonFunction.value = function () {
131
+ const isNotValidated = validateURL()
132
+ if (isNotValidated) {
133
+ return
134
+ }
135
+ if (!isNotValidated) {
136
+ setEnteretURL()
137
+ }
138
+ showModalUrl.value = false
139
+ }
140
+ // end modal
141
+ }
142
+
143
+ //
144
+ //
145
+ const validateURL = function () {
146
+ // initial value
147
+ urlError.value = null
148
+
149
+ // url validation
150
+ const urlRegex = /^https?:\/\//
151
+ const isValidURL = ref(true)
152
+ isValidURL.value = urlRegex.test(newUpdatedExistingURL.value)
153
+
154
+ // cancelled
155
+ if (isValidURL.value === false) {
156
+ urlError.value =
157
+ "The provided URL is invalid. Please ensure that it begins with 'https://' for proper formatting and security."
158
+ return true
159
+ }
160
+
161
+ return false
162
+ }
163
+ const setEnteretURL = function () {
164
+ // update link
165
+ editor.value
166
+ .chain()
167
+ .focus()
168
+ .extendMarkRange('link')
169
+ .setLink({ href: newUpdatedExistingURL.value })
170
+ .run()
171
+ }
172
+
173
+ onBeforeMount(() => {
174
+ editor.value?.destroy()
175
+ })
176
+
177
+ onMounted(() => {
178
+ TipTapSetContent()
179
+ })
180
+ </script>
181
+ <template>
182
+ <DynamicModal
183
+ :show="showModalUrl"
184
+ :type="typeModal"
185
+ :gridColumnAmount="gridColumnModal"
186
+ :title="titleModal"
187
+ :description="descriptionModal"
188
+ :firstButtonText="firstButtonModal"
189
+ :secondButtonText="secondButtonModal"
190
+ :thirdButtonText="thirdButtonModal"
191
+ @firstModalButtonFunction="firstModalButtonFunction"
192
+ @secondModalButtonFunction="secondModalButtonFunction"
193
+ @thirdModalButtonFunction="thirdModalButtonFunction"
194
+ >
195
+ <header></header>
196
+ <main>
197
+ <div class="myInputGroup">
198
+ <label class="myPrimaryInputLabel" for="roles"><span>Enter URL</span></label
199
+ ><input v-model="urlEnteret" class="myPrimaryInput mt-1" type="url" placeholder="url" />
200
+ <div v-if="urlError" class="min-h-[2.5rem] flex items-center justify-start">
201
+ <p class="myPrimaryInputError mt-2 mb-0 py-0 self-start">
202
+ {{ urlError }}
203
+ </p>
204
+ </div>
205
+ </div>
206
+ </main>
207
+ </DynamicModal>
208
+
209
+ <div class="blockease-linear duration-200 block ease-linear">
210
+ <div v-if="pageBuilder.selectedElementIsValidText() && editor">
211
+ <div class="relative rounded-lg">
212
+ <div
213
+ class="flex justify-between myPrimaryGap items-center divide-x divide-gray-200 py-4 px-4 overflow-x-auto border-b border-gray-20"
214
+ >
215
+ <div class="flex items-center 0 divide-x divide-gray-200">
216
+ <div class="px-2 flex items-center justify-start gap-2">
217
+ <button
218
+ @click="editor.chain().focus().setHardBreak().run()"
219
+ type="button"
220
+ class="text-[12.5px] gap-2 text-nowrap pl-2 pr-3 w-full h-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
221
+ >
222
+ <span class="material-symbols-outlined"> keyboard_return </span>
223
+ <span>Line break</span>
224
+ </button>
225
+ </div>
226
+
227
+ <div class="px-2 flex items-center justify-start gap-2">
228
+ <button
229
+ @click="editor.chain().focus().toggleBold().run()"
230
+ type="button"
231
+ class="text-[12.5px] gap-2 text-nowrap pl-2 pr-3 w-full h-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
232
+ :class="{
233
+ 'bg-myPrimaryLinkColor text-white': editor.isActive('bold'),
234
+ }"
235
+ >
236
+ <span class="material-symbols-outlined"> format_bold </span>
237
+ <span>Bold</span>
238
+ </button>
239
+ </div>
240
+
241
+ <div class="px-2 flex items-center justify-start gap-2">
242
+ <button
243
+ @click="handleURL"
244
+ type="button"
245
+ class="text-[12.5px] gap-2 text-nowrap pl-2 pr-3 w-full h-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
246
+ :class="{
247
+ 'bg-myPrimaryLinkColor text-white': editor.isActive('link'),
248
+ }"
249
+ >
250
+ <span class="material-symbols-outlined"> link </span>
251
+ <span>Link</span>
252
+ </button>
253
+ </div>
254
+
255
+ <div class="px-2 flex items-center justify-start gap-2">
256
+ <button
257
+ @click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
258
+ type="button"
259
+ class="text-[12.5px] gap-2 text-nowrap pl-2 pr-3 w-full h-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
260
+ :class="{
261
+ 'bg-myPrimaryLinkColor text-white': editor.isActive('heading', {
262
+ level: 2,
263
+ }),
264
+ }"
265
+ >
266
+ <span class="material-symbols-outlined"> titlecase </span>
267
+ <span>Header 2</span>
268
+ </button>
269
+ </div>
270
+
271
+ <div class="px-2 flex items-center justify-start gap-2">
272
+ <button
273
+ @click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
274
+ type="button"
275
+ class="text-[12.5px] gap-2 text-nowrap pl-2 pr-3 w-full h-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
276
+ :class="{
277
+ 'bg-myPrimaryLinkColor text-white': editor.isActive('heading', {
278
+ level: 3,
279
+ }),
280
+ }"
281
+ >
282
+ <span class="material-symbols-outlined"> titlecase </span>
283
+ <span>Header 3</span>
284
+ </button>
285
+ </div>
286
+
287
+ <div class="px-2 flex items-center justify-start gap-2">
288
+ <button
289
+ @click="editor.chain().focus().toggleBulletList().run()"
290
+ type="button"
291
+ class="text-[12.5px] gap-2 text-nowrap pl-2 pr-3 w-full h-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
292
+ :class="{
293
+ 'bg-myPrimaryLinkColor text-white': editor.isActive('bulletList'),
294
+ }"
295
+ >
296
+ <span class="material-symbols-outlined"> list </span>
297
+ <span>List</span>
298
+ </button>
299
+ </div>
300
+ </div>
301
+ <div>
302
+ <div>
303
+ <div class="px-2 flex items-center justify-start gap-2">
304
+ <button
305
+ @click="pageBuilderStateStore.setShowModalTipTap(false)"
306
+ type="button"
307
+ class="text-[12.5px] gap-2 text-nowrap pl-2 pr-3 w-full h-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
308
+ >
309
+ <span class="material-symbols-outlined"> Save </span>
310
+ <span>Save</span>
311
+ </button>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ </div>
316
+
317
+ <editor-content
318
+ id="page-builder-editor"
319
+ :editor="editor"
320
+ class="px-4 pt-6 pb-12 bg-white rounded-lg lg:min-h-[20rem] lg:max-h-[30rem] md:min-h-[20rem] md:max-h-[20rem] min-h-[20rem] max-h-[20rem] overflow-y-auto"
321
+ />
322
+ </div>
323
+ </div>
324
+ </div>
325
+ </template>
@@ -0,0 +1,347 @@
1
+ <script setup>
2
+ import { onMounted, computed, ref, watch, provide } from 'vue'
3
+ import { createPinia } from 'pinia'
4
+ import PageBuilder from '@/composables/PageBuilder.ts'
5
+ import PageBuilderPreviewModal from '@/Components/Modals/PageBuilderPreviewModal.vue'
6
+ import Preview from '@/PageBuilder/Preview.vue'
7
+ import ComponentTopMenu from '@/Components/PageBuilder/EditorMenu/Editables/ComponentTopMenu.vue'
8
+ import EditGetElement from '@/Components/PageBuilder/EditorMenu/Editables/EditGetElement.vue'
9
+ import SearchComponents from '@/Components/Search/SearchComponents.vue'
10
+ import OptionsDropdown from '@/Components/PageBuilder/DropdownsPlusToggles/OptionsDropdown.vue'
11
+ import RightSidebarEditor from '@/Components/PageBuilder/EditorMenu/RightSidebarEditor.vue'
12
+ import { usePageBuilderStateStore } from '@/stores/page-builder-state'
13
+ import { useMediaLibraryStore } from '@/stores/media-library'
14
+
15
+ // Create internal Pinia instance for this component if none exists
16
+ let internalPinia = null
17
+ let mediaLibraryStore = null
18
+ let pageBuilderStateStore = null
19
+ let pageBuilder = null
20
+
21
+ try {
22
+ // Try to use existing Pinia first
23
+ mediaLibraryStore = useMediaLibraryStore()
24
+ pageBuilderStateStore = usePageBuilderStateStore()
25
+ } catch (error) {
26
+ // If no Pinia exists, create our own internal instance
27
+ internalPinia = createPinia()
28
+ mediaLibraryStore = useMediaLibraryStore(internalPinia)
29
+ pageBuilderStateStore = usePageBuilderStateStore(internalPinia)
30
+ }
31
+
32
+ // Initialize PageBuilder with stores
33
+ pageBuilder = new PageBuilder(pageBuilderStateStore, mediaLibraryStore)
34
+
35
+ // Provide the stores for child components if using internal Pinia
36
+ if (internalPinia) {
37
+ provide('pageBuilderStateStore', pageBuilderStateStore)
38
+ provide('mediaLibraryStore', mediaLibraryStore)
39
+ }
40
+
41
+ const getMenuRight = computed(() => {
42
+ return pageBuilderStateStore.getMenuRight
43
+ })
44
+ const previewCurrentDesign = function () {
45
+ pageBuilder.previewCurrentDesign()
46
+ }
47
+ const openPageBuilderPreviewModal = ref(false)
48
+ const firstPageBuilderPreviewModalButton = ref(null)
49
+
50
+ const handlePageBuilderPreview = function () {
51
+ previewCurrentDesign()
52
+
53
+ openPageBuilderPreviewModal.value = true
54
+ // handle click
55
+ firstPageBuilderPreviewModalButton.value = function () {
56
+ openPageBuilderPreviewModal.value = false
57
+ }
58
+ // end modal
59
+ }
60
+
61
+ const showModalAddComponent = ref(false)
62
+ const titleModalAddComponent = ref('')
63
+ const firstButtonTextSearchComponents = ref('')
64
+ const firstModalButtonSearchComponentsFunction = ref(null)
65
+
66
+ const handleAddComponent = function () {
67
+ pageBuilderStateStore.setComponent(null)
68
+
69
+ //
70
+ titleModalAddComponent.value = 'Add Components to Page'
71
+ firstButtonTextSearchComponents.value = 'Close'
72
+ showModalAddComponent.value = true
73
+
74
+ firstModalButtonSearchComponentsFunction.value = function () {
75
+ // handle show modal for unique content
76
+ showModalAddComponent.value = false
77
+ }
78
+
79
+ // end modal
80
+ }
81
+
82
+ const getComponents = computed(() => {
83
+ return pageBuilderStateStore.getComponents
84
+ })
85
+ const getComponent = computed(() => {
86
+ return pageBuilderStateStore.getComponent
87
+ })
88
+
89
+ const getElement = computed(() => {
90
+ return pageBuilderStateStore.getElement
91
+ })
92
+
93
+ const getElementAttributes = computed(() => {
94
+ if (!getElement.value || !(getElement.value instanceof HTMLElement)) {
95
+ return new Object()
96
+ }
97
+
98
+ // Extract the attributes you want to watch
99
+ const attributesToWatch = {
100
+ src: getElement.value.getAttribute('src'),
101
+ href: getElement.value.getAttribute('href'),
102
+ style: getElement.value.getAttribute('style'),
103
+ class: getElement.value.getAttribute('class'),
104
+ dataImage: getElement.value.getAttribute('data-image'),
105
+ }
106
+
107
+ return attributesToWatch
108
+ })
109
+
110
+ watch(getElementAttributes, (newAttributes, oldAttributes) => {
111
+ // Check if any of the specified attributes have changed
112
+ if (
113
+ newAttributes?.src !== oldAttributes?.src ||
114
+ newAttributes?.href !== oldAttributes?.href ||
115
+ newAttributes?.style !== oldAttributes?.style ||
116
+ newAttributes?.class !== oldAttributes?.class ||
117
+ newAttributes?.dataImage !== oldAttributes?.dataImage
118
+ ) {
119
+ // Trigger your method when any of the specified attributes change
120
+ pageBuilder.handlePageBuilderMethods()
121
+ pageBuilder.setEventListenersForElements()
122
+ }
123
+ })
124
+
125
+ const handleSelectComponent = function (componentObject) {
126
+ pageBuilderStateStore.setComponent(componentObject)
127
+ }
128
+
129
+ const draggableZone = ref(null)
130
+
131
+ onMounted(async () => {
132
+ pageBuilder.setEventListenersForElements()
133
+ })
134
+ </script>
135
+
136
+ <template>
137
+ <div>
138
+ <SearchComponents
139
+ v-if="showModalAddComponent"
140
+ :show="showModalAddComponent"
141
+ :firstButtonText="firstButtonTextSearchComponents"
142
+ :title="titleModalAddComponent"
143
+ @firstModalButtonSearchComponentsFunction="firstModalButtonSearchComponentsFunction"
144
+ ></SearchComponents>
145
+ <PageBuilderPreviewModal
146
+ :show="openPageBuilderPreviewModal"
147
+ @firstPageBuilderPreviewModalButton="firstPageBuilderPreviewModalButton"
148
+ >
149
+ <Preview></Preview>
150
+ </PageBuilderPreviewModal>
151
+
152
+ <div class="w-full inset-x-0 h-[90vh] z-10 bg-white overflow-x-scroll lg:pt-2 pt-2">
153
+ <div class="relative h-full flex">
154
+ <div
155
+ @click.self="pageBuilderStateStore.setComponent(null)"
156
+ class="min-w-[3.5rem] pt-6 pb-2 my-2 mx-2 bg-myPrimaryLightGrayColor rounded-full shadow"
157
+ >
158
+ <div class="mx-2 flex flex-col myPrimaryGap">
159
+ <div class="flex gap-2 items-center justify-center">
160
+ <button
161
+ type="button"
162
+ @click="
163
+ () => {
164
+ pageBuilderStateStore.setComponentArrayAddMethod('unshift')
165
+ handleAddComponent()
166
+ }
167
+ "
168
+ class="h-10 w-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 aspect-square hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
169
+ >
170
+ <span class="myMediumIcon material-symbols-outlined"> add </span>
171
+ </button>
172
+ </div>
173
+ <div @click.self="pageBuilderStateStore.setComponent(null)">
174
+ <ComponentTopMenu v-if="getElement"></ComponentTopMenu>
175
+ </div>
176
+ </div>
177
+ </div>
178
+ <main class="flex flex-col h-full grow rounded-2xl duration-300 shadow-2xl">
179
+ <div
180
+ class="flex items-center justify-between primary-gap rounded-t-2xl bg-myPrimaryLightGrayColor"
181
+ >
182
+ <div
183
+ @click.self="pageBuilderStateStore.setComponent(null)"
184
+ class="w-4/12 flex justify-start items-center py-2 pl-2 h-full"
185
+ >
186
+ <div class="flex gap-2">
187
+ <span class="w-2 h-2 rounded-full bg-red-400"></span>
188
+ <span class="w-2 h-2 rounded-full bg-yellow-400"></span>
189
+ <span class="w-2 h-2 rounded-full bg-green-400"></span>
190
+ </div>
191
+ </div>
192
+
193
+ <div
194
+ @click.self="pageBuilderStateStore.setComponent(null)"
195
+ class="w-4/12 flex justify-center py-2"
196
+ >
197
+ <OptionsDropdown @previewCurrentDesign="previewCurrentDesign"></OptionsDropdown>
198
+ </div>
199
+
200
+ <div
201
+ @click.self="pageBuilderStateStore.setComponent(null)"
202
+ class="w-4/12 flex justify-end py-2 pr-2"
203
+ >
204
+ <div class="flex items-center justify-center gap-4">
205
+ <button
206
+ type="button"
207
+ @click="
208
+ () => {
209
+ pageBuilderStateStore.setComponentArrayAddMethod('unshift')
210
+ handleAddComponent()
211
+ }
212
+ "
213
+ >
214
+ <div class="flex items-center justify-center gap-2">
215
+ <span class="lg:block hidden"> Add new Component - v.1.0.6 </span>
216
+
217
+ <span
218
+ class="h-10 w-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 aspect-square hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
219
+ >
220
+ <span class="myMediumIcon material-symbols-outlined"> add </span>
221
+ </span>
222
+ </div>
223
+ </button>
224
+ <button
225
+ type="button"
226
+ @click="
227
+ () => {
228
+ pageBuilderStateStore.setMenuRight(false)
229
+ pageBuilderStateStore.setElement(null)
230
+ pageBuilderStateStore.setComponent(null)
231
+ handlePageBuilderPreview()
232
+ }
233
+ "
234
+ class="h-10 w-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 aspect-square hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
235
+ >
236
+ <span class="material-symbols-outlined"> visibility </span>
237
+ </button>
238
+
239
+ <button
240
+ type="button"
241
+ v-if="getMenuRight === false"
242
+ @click="pageBuilderStateStore.setMenuRight(true)"
243
+ class="h-10 w-10 cursor-pointer rounded-full flex items-center border-none justify-center bg-gray-50 aspect-square hover:bg-myPrimaryLinkColor hover:text-white focus-visible:ring-0"
244
+ >
245
+ <span class="material-symbols-outlined"> gesture </span>
246
+ </button>
247
+ </div>
248
+ </div>
249
+ </div>
250
+
251
+ <EditGetElement></EditGetElement>
252
+ <div
253
+ @click="pageBuilderStateStore.setComponent(null)"
254
+ id="contains-pagebuilder"
255
+ class="pl-4 pr-4 pb-4 overflow-y-auto h-screen pt-1"
256
+ >
257
+ <div id="pagebuilder">
258
+ <div ref="draggableZone">
259
+ <!-- Added Components to DOM # start -->
260
+ <div
261
+ v-for="component in Array.isArray(getComponents) && getComponents"
262
+ :key="component"
263
+ id="page-builder-editor-editable-area"
264
+ class="bg-white grow"
265
+ >
266
+ <div @mouseup="handleSelectComponent(component)" class="relative group">
267
+ <div v-html="component.html_code"></div>
268
+ </div>
269
+ </div>
270
+ </div>
271
+ <!-- Added Components to DOM # end -->
272
+
273
+ <!-- Add Component # start -->
274
+ <div
275
+ class="rounded-lg border-2 border-dashed border-gray-300 p-12 text-center hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 my-12 mx-8"
276
+ >
277
+ <h3 class="mt-2 text-sm font-medium text-gray-900">Add Components</h3>
278
+ <p class="mt-1 text-sm text-gray-500">
279
+ Get started by adding components using the drag & drop Page Builder.
280
+ </p>
281
+ <div class="mt-6 flex items-center gap-2 justify-center">
282
+ <button
283
+ @click="
284
+ () => {
285
+ pageBuilderStateStore.setComponentArrayAddMethod('push')
286
+ handleAddComponent()
287
+ }
288
+ "
289
+ type="button"
290
+ class="myPrimaryButton flex items-center gap-2 justify-center"
291
+ >
292
+ <span class="myMediumIcon material-symbols-outlined"> add </span>
293
+ Add component
294
+ </button>
295
+ </div>
296
+ </div>
297
+ <!-- Add Component # end -->
298
+ </div>
299
+ </div>
300
+ <!-- Add Component # end -->
301
+ </main>
302
+
303
+ <aside
304
+ aria-label="Menu"
305
+ :class="{ 'w-0': !getMenuRight, 'w-80 ml-4': getMenuRight }"
306
+ class="h-full duration-300 z-20 flex-shrink-0 overflow-hidden shadow-2xl rounded-l-2xl bg-white"
307
+ >
308
+ <RightSidebarEditor @closeEditor="pageBuilderStateStore.setMenuRight(false)">
309
+ </RightSidebarEditor>
310
+ </aside>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ </template>
315
+
316
+ <style>
317
+ #pagebuilder a {
318
+ cursor: default;
319
+ }
320
+
321
+ #pagebuilder [element] {
322
+ outline: rgba(255, 255, 255, 0) dashed 3px !important;
323
+ outline-offset: -3px !important;
324
+ }
325
+ #pagebuilder [hovered] {
326
+ outline: rgb(0, 140, 14, 1) dashed 3px !important;
327
+ outline-offset: -3px !important;
328
+ }
329
+
330
+ #pagebuilder [selected] {
331
+ position: relative;
332
+
333
+ outline: rgb(185, 16, 16) dashed 3px !important;
334
+ outline-offset: -3px !important;
335
+ }
336
+
337
+ /* sortable */
338
+
339
+ .sortable-ghost {
340
+ display: flex;
341
+ justify-content: center;
342
+ }
343
+
344
+ .sortable-ghost > * {
345
+ width: 100%;
346
+ }
347
+ </style>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div
3
+ class="w-full inset-x-0 h-[90vh] z-10 bg-white overflow-x-scroll lg:pt-2 pt-2"
4
+ >
5
+ <div id="page-builder-editor">
6
+ <div class="" v-html="htmlPage"></div>
7
+ </div>
8
+ </div>
9
+ </template>
10
+
11
+ <script setup>
12
+ import { ref, watch } from "vue";
13
+
14
+ // router
15
+ // const router = useRouter();
16
+ // preview content
17
+ const htmlPage = ref("");
18
+ // get preview item from local storage
19
+ htmlPage.value = localStorage.getItem("preview");
20
+ // parse
21
+ htmlPage.value = JSON.parse(htmlPage.value);
22
+ // join
23
+ htmlPage.value = htmlPage.value.join("");
24
+ </script>