@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,1483 @@
1
+ // Type definitions
2
+ interface ComponentObject {
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
+ [key: string]: any
5
+ }
6
+
7
+ interface ImageObject {
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ [key: string]: any
10
+ }
11
+
12
+ interface PageBuilderStateStore {
13
+ getTextAreaVueModel: string | null
14
+ getLocalStorageItemName: string | null
15
+ getLocalStorageItemNameUpdate: string | null
16
+ getHyberlinkEnable: boolean
17
+ getComponents: ComponentObject[] | null
18
+ getComponent: ComponentObject | null
19
+ getElement: HTMLElement | null
20
+ getNextSibling: HTMLElement | null
21
+ getParentElement: HTMLElement | null
22
+ getRestoredElement: string | null
23
+ getComponentArrayAddMethod: string | null
24
+ setElement: (element: HTMLElement | null) => void
25
+ setMenuRight: (value: boolean) => void
26
+ setComponent: (component: ComponentObject | null) => void
27
+ setComponents: (components: ComponentObject[] | null) => void
28
+ setComponentArrayAddMethod: (method: string) => void
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ [key: string]: any // For dynamic mutation methods
31
+ }
32
+
33
+ interface MediaLibraryStore {
34
+ getCurrentImage: ImageObject | null
35
+ }
36
+
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ import tailwindColors from '@/utils/builder/tailwaind-colors'
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ import tailwindOpacities from '@/utils/builder/tailwind-opacities'
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ import tailwindFontSizes from '@/utils/builder/tailwind-font-sizes'
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ import tailwindFontStyles from '@/utils/builder/tailwind-font-styles'
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ import tailwindPaddingAndMargin from '@/utils/builder/tailwind-padding-margin'
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ import tailwindBorderRadius from '@/utils/builder/tailwind-border-radius'
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ import tailwindBorderStyleWidthPlusColor from '@/utils/builder/tailwind-border-style-width-color'
51
+ import { computed, ref, nextTick } from 'vue'
52
+ import type { ComputedRef } from 'vue'
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ import { v4 as uuidv4 } from 'uuid'
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ import { delay } from '@/composables/delay'
57
+
58
+ class PageBuilder {
59
+ // Class properties with types
60
+ private elementsWithListeners: WeakSet<Element>
61
+ private nextTick: Promise<void>
62
+ private containsPagebuilder: Element | null
63
+ private timer: number | null
64
+ private pageBuilderStateStore: PageBuilderStateStore
65
+ private mediaLibraryStore: MediaLibraryStore
66
+ private getTextAreaVueModel: ComputedRef<string | null>
67
+ private getLocalStorageItemName: ComputedRef<string | null>
68
+ private getLocalStorageItemNameUpdate: ComputedRef<string | null>
69
+ private getCurrentImage: ComputedRef<ImageObject | null>
70
+ private getHyberlinkEnable: ComputedRef<boolean>
71
+ private getComponents: ComputedRef<ComponentObject[] | null>
72
+ private getComponent: ComputedRef<ComponentObject | null>
73
+ private getElement: ComputedRef<HTMLElement | null>
74
+ private getNextSibling: ComputedRef<HTMLElement | null>
75
+ private getParentElement: ComputedRef<HTMLElement | null>
76
+ private getRestoredElement: ComputedRef<string | null>
77
+ private getComponentArrayAddMethod: ComputedRef<string | null>
78
+ private headerTags: string[]
79
+ private additionalTagsNoneListernes: string[]
80
+ private structuringTags: string[]
81
+ private showRunningMethodLogs: boolean
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
+ private delay: any
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
+ private observer?: any // Add missing observer property
86
+
87
+ constructor(pageBuilderStateStore: PageBuilderStateStore, mediaLibraryStore: MediaLibraryStore) {
88
+ /**
89
+ * Initialize an instance variable 'elementsWithListeners' as a WeakSet.
90
+ *
91
+ * A WeakSet is a special type of Set that holds weak references to its elements,
92
+ * meaning that an element could be garbage collected if there is no other reference to it.
93
+ * This is especially useful in the context of managing DOM elements and their associated events,
94
+ * as it allows for efficient and automated cleanup of references to DOM elements that have been removed.
95
+ *
96
+ * By checking if an element is in this WeakSet before attaching an event listener,
97
+ * we can prevent redundant addition of the same event listener to an element.
98
+ * This helps in managing the memory usage and performance of the application.
99
+ */
100
+ this.elementsWithListeners = new WeakSet()
101
+
102
+ this.nextTick = nextTick()
103
+
104
+ this.containsPagebuilder = document.querySelector('#contains-pagebuilder')
105
+
106
+ this.timer = null
107
+ this.pageBuilderStateStore = pageBuilderStateStore
108
+ this.mediaLibraryStore = mediaLibraryStore
109
+
110
+ this.getTextAreaVueModel = computed(() => this.pageBuilderStateStore.getTextAreaVueModel)
111
+ this.getLocalStorageItemName = computed(
112
+ () => this.pageBuilderStateStore.getLocalStorageItemName,
113
+ )
114
+ this.getLocalStorageItemNameUpdate = computed(
115
+ () => this.pageBuilderStateStore.getLocalStorageItemNameUpdate,
116
+ )
117
+
118
+ this.getCurrentImage = computed(() => this.mediaLibraryStore.getCurrentImage)
119
+ this.getHyberlinkEnable = computed(() => this.pageBuilderStateStore.getHyberlinkEnable)
120
+ this.getComponents = computed(() => this.pageBuilderStateStore.getComponents)
121
+
122
+ this.getComponent = computed(() => this.pageBuilderStateStore.getComponent)
123
+
124
+ this.getElement = computed(() => this.pageBuilderStateStore.getElement)
125
+ this.getNextSibling = computed(() => this.pageBuilderStateStore.getNextSibling)
126
+ this.getParentElement = computed(() => this.pageBuilderStateStore.getParentElement)
127
+ this.getRestoredElement = computed(() => this.pageBuilderStateStore.getRestoredElement)
128
+
129
+ this.getComponentArrayAddMethod = computed(
130
+ () => this.pageBuilderStateStore.getComponentArrayAddMethod,
131
+ )
132
+
133
+ this.headerTags = ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'IFRAME']
134
+
135
+ this.additionalTagsNoneListernes = [
136
+ 'UL',
137
+ 'OL',
138
+ 'LI',
139
+ 'EM',
140
+ 'STRONG',
141
+ 'B',
142
+ 'A',
143
+ 'SPAN',
144
+ 'BLOCKQUOTE',
145
+ 'BR',
146
+ ]
147
+
148
+ this.structuringTags = [
149
+ 'DIV',
150
+ 'IFRAME',
151
+ 'HEADER',
152
+ 'NAV',
153
+ 'MAIN',
154
+ 'ARTICLE',
155
+ 'SECTION',
156
+ 'ASIDE',
157
+ 'FOOTER',
158
+ ]
159
+
160
+ this.showRunningMethodLogs = false
161
+
162
+ this.delay = delay()
163
+ }
164
+
165
+ shouldRunMethods(): boolean {
166
+ if (!this.getComponents.value) {
167
+ return false
168
+ }
169
+
170
+ if (!this.getComponent.value) {
171
+ return false
172
+ }
173
+
174
+ if (!this.getElement.value) {
175
+ return false
176
+ }
177
+
178
+ return true
179
+ }
180
+
181
+ #applyElementClassChanges(
182
+ selectedCSS: string,
183
+ CSSArray: string[],
184
+ mutationName: string,
185
+ ): string | undefined {
186
+ if (this.showRunningMethodLogs) {
187
+ console.log('#applyElementClassChanges')
188
+ }
189
+ if (!this.shouldRunMethods()) return
190
+
191
+ const currentCSS = CSSArray.find((CSS) => {
192
+ return this.getElement.value?.classList.contains(CSS)
193
+ })
194
+
195
+ // set to 'none' if undefined
196
+ let elementClass = currentCSS || 'none'
197
+
198
+ this.pageBuilderStateStore[mutationName](elementClass)
199
+
200
+ if (typeof selectedCSS === 'string' && selectedCSS !== 'none') {
201
+ if (elementClass && this.getElement.value?.classList.contains(elementClass)) {
202
+ this.getElement.value?.classList.remove(elementClass)
203
+ }
204
+
205
+ this.getElement.value?.classList.add(selectedCSS)
206
+ elementClass = selectedCSS
207
+ } else if (typeof selectedCSS === 'string' && selectedCSS === 'none' && elementClass) {
208
+ this.getElement.value?.classList.remove(elementClass)
209
+ elementClass = selectedCSS
210
+ }
211
+
212
+ this.pageBuilderStateStore[mutationName](elementClass)
213
+ this.pageBuilderStateStore.setElement(this.getElement.value)
214
+
215
+ return currentCSS
216
+ }
217
+
218
+ #applyHelperCSSToElements(element: HTMLElement): void {
219
+ this.#wrapElementInDivIfExcluded(element)
220
+
221
+ if (this.showRunningMethodLogs) {
222
+ console.log('#applyHelperCSSToElements')
223
+ }
224
+
225
+ if (element.tagName === 'IMG') {
226
+ element.classList.add('smooth-transition')
227
+ }
228
+
229
+ // Add padding to DIV
230
+ if (element.tagName === 'DIV') {
231
+ if (element.classList.length === 0) {
232
+ // element.classList.add("p-2");
233
+ }
234
+ }
235
+ }
236
+
237
+ #wrapElementInDivIfExcluded(element: HTMLElement): void {
238
+ if (this.showRunningMethodLogs) {
239
+ console.log('#wrapElementInDivIfExcluded')
240
+ }
241
+
242
+ if (
243
+ this.headerTags.includes(element.tagName) &&
244
+ ((element.previousElementSibling && element.previousElementSibling.tagName === 'IMG') ||
245
+ (element.nextElementSibling && element.nextElementSibling.tagName === 'IMG'))
246
+ ) {
247
+ const divWrapper = document.createElement('div')
248
+ element.parentNode?.insertBefore(divWrapper, element)
249
+ divWrapper.appendChild(element)
250
+ }
251
+ }
252
+
253
+ #handleElementClick = (e: Event, element: HTMLElement): void => {
254
+ e.stopPropagation()
255
+
256
+ const pagebuilder = document.querySelector('#pagebuilder')
257
+
258
+ if (!pagebuilder) return
259
+
260
+ this.pageBuilderStateStore.setMenuRight(true)
261
+
262
+ const selectedElement = pagebuilder.querySelector('[selected]')
263
+ if (selectedElement) {
264
+ selectedElement.removeAttribute('selected')
265
+ }
266
+
267
+ element.removeAttribute('hovered')
268
+
269
+ element.setAttribute('selected', '')
270
+
271
+ this.pageBuilderStateStore.setElement(element)
272
+ }
273
+
274
+ #handleMouseOver = (e: Event, element: HTMLElement): void => {
275
+ if (this.showRunningMethodLogs) {
276
+ console.log('#handleMouseOver')
277
+ }
278
+ // console.log("YOU MOUSE OVER ME!");
279
+
280
+ e.preventDefault()
281
+ e.stopPropagation()
282
+
283
+ const pagebuilder = document.querySelector('#pagebuilder')
284
+
285
+ if (!pagebuilder) return
286
+
287
+ const hoveredElement = pagebuilder.querySelector('[hovered]')
288
+ if (hoveredElement) {
289
+ hoveredElement.removeAttribute('hovered')
290
+ }
291
+
292
+ if (!element.hasAttribute('selected')) {
293
+ element.setAttribute('hovered', '')
294
+ }
295
+ }
296
+
297
+ #handleMouseLeave = (e: Event): void => {
298
+ if (this.showRunningMethodLogs) {
299
+ console.log('#handleMouseLeave')
300
+ }
301
+
302
+ e.preventDefault()
303
+ e.stopPropagation()
304
+
305
+ const pagebuilder = document.querySelector('#pagebuilder')
306
+ if (!pagebuilder) return
307
+
308
+ const hoveredElement = pagebuilder.querySelector('[hovered]')
309
+ if (hoveredElement) {
310
+ hoveredElement.removeAttribute('hovered')
311
+ }
312
+ }
313
+
314
+ /**
315
+ * The function is used to
316
+ * attach event listeners to each element within a 'section'
317
+ */
318
+ setEventListenersForElements = () => {
319
+ if (this.showRunningMethodLogs) {
320
+ console.log('setEventListenersForElements')
321
+ }
322
+
323
+ const pagebuilder = document.querySelector('#pagebuilder')
324
+
325
+ if (!pagebuilder) return
326
+
327
+ pagebuilder.querySelectorAll('section *').forEach(async (element) => {
328
+ // exclude headerTags && additional Tags for not listening
329
+ if (
330
+ !this.headerTags.includes(element.tagName) &&
331
+ !this.additionalTagsNoneListernes.includes(element.tagName)
332
+ ) {
333
+ if (this.elementsWithListeners && !this.elementsWithListeners.has(element)) {
334
+ this.elementsWithListeners.add(element)
335
+ // Type assertion to HTMLElement since we know these are DOM elements
336
+ const htmlElement = element as HTMLElement
337
+ // Attach event listeners directly to individual elements
338
+ htmlElement.addEventListener('click', (e) => this.#handleElementClick(e, htmlElement))
339
+ htmlElement.addEventListener('mouseover', (e) => this.#handleMouseOver(e, htmlElement))
340
+ htmlElement.addEventListener('mouseleave', (e) => this.#handleMouseLeave(e))
341
+ }
342
+ }
343
+
344
+ // end for each iterating over elements
345
+ })
346
+ }
347
+
348
+ /**
349
+ * The Intersection Observer API provides a way to asynchronously observe changes in the
350
+ * intersection of a target element with an ancestor element or with a top-level document's viewport.
351
+ */
352
+ synchronizeDOMAndComponents = async () => {
353
+ if (this.showRunningMethodLogs) {
354
+ console.log('synchronizeDOMAndComponents')
355
+ }
356
+ if (!this.getComponents.value) {
357
+ this.pageBuilderStateStore.setComponents([])
358
+ }
359
+
360
+ const hoveredElement = document.querySelector('[hovered]')
361
+ if (hoveredElement) {
362
+ hoveredElement.removeAttribute('hovered')
363
+ }
364
+
365
+ this.getComponents.value?.forEach((component) => {
366
+ const section = document.querySelector(`section[data-componentid="${component.id}"]`)
367
+
368
+ if (section) {
369
+ component.html_code = section.outerHTML
370
+ }
371
+ })
372
+
373
+ // Initialize the MutationObserver
374
+ this.observer = new MutationObserver((mutationsList, observer) => {
375
+ // Once we have seen a mutation, stop observing and resolve the promise
376
+ observer.disconnect()
377
+ })
378
+
379
+ // Start observing the document with the configured parameters
380
+ this.observer.observe(document, {
381
+ attributes: true,
382
+ childList: true,
383
+ subtree: true,
384
+ })
385
+
386
+ // Use the MutationObserver to wait for the next DOM change
387
+ await new Promise<void>((resolve) => {
388
+ resolve()
389
+ })
390
+ }
391
+
392
+ cloneCompObjForDOMInsertion(componentObject: ComponentObject): ComponentObject {
393
+ if (this.showRunningMethodLogs) {
394
+ console.log('cloneCompObjForDOMInsertion')
395
+ }
396
+
397
+ // Deep clone clone component
398
+ const clonedComponent = { ...componentObject }
399
+
400
+ // scoll to top or bottom # end
401
+ if (this.containsPagebuilder) {
402
+ if (
403
+ this.getComponentArrayAddMethod.value === 'unshift' ||
404
+ this.getComponentArrayAddMethod.value === 'push'
405
+ ) {
406
+ // push to top
407
+ if (this.getComponentArrayAddMethod.value === 'unshift') {
408
+ this.containsPagebuilder.scrollTo({
409
+ top: 0,
410
+ behavior: 'smooth',
411
+ })
412
+ }
413
+
414
+ // push to bottom
415
+ if (this.getComponentArrayAddMethod.value === 'push') {
416
+ const maxHeight = this.containsPagebuilder.scrollHeight
417
+ this.containsPagebuilder.scrollTo({
418
+ top: maxHeight,
419
+ behavior: 'smooth',
420
+ })
421
+ }
422
+ }
423
+ }
424
+
425
+ // Create a DOMParser instance
426
+ const parser = new DOMParser()
427
+
428
+ // Parse the HTML content of the clonedComponent using the DOMParser
429
+ const doc = parser.parseFromString(clonedComponent.html_code, 'text/html')
430
+
431
+ // Selects all elements within the HTML document, including elements like:
432
+ const elements = doc.querySelectorAll('*')
433
+
434
+ elements.forEach((element) => {
435
+ this.#applyHelperCSSToElements(element as HTMLElement)
436
+ })
437
+
438
+ // Add the component id to the section element
439
+ const section = doc.querySelector('section')
440
+ if (section) {
441
+ // Generate a unique ID using uuidv4() and assign it to the section
442
+ section.dataset.componentid = uuidv4()
443
+
444
+ // Find all images within elements with "flex" or "grid" classes inside the section
445
+ const images = section.querySelectorAll('img')
446
+
447
+ // Add a unique ID as a data attribute to each image element
448
+ images.forEach((image) => {
449
+ image.setAttribute('data-image', uuidv4())
450
+ })
451
+
452
+ // Update the clonedComponent id with the newly generated unique ID
453
+ clonedComponent.id = section.dataset.componentid
454
+
455
+ // Update the HTML content of the clonedComponent with the modified HTML
456
+ clonedComponent.html_code = section.outerHTML
457
+ }
458
+
459
+ // return to the cloned element to be dropped
460
+ return clonedComponent
461
+ }
462
+
463
+ removeHoveredAndSelected() {
464
+ if (this.showRunningMethodLogs) {
465
+ console.log('removeHoveredAndSelected')
466
+ }
467
+
468
+ const hoveredElement = document.querySelector('[hovered]')
469
+ if (hoveredElement) {
470
+ hoveredElement.removeAttribute('hovered')
471
+ }
472
+
473
+ const selectedElement = document.querySelector('[selected]')
474
+ if (selectedElement) {
475
+ selectedElement.removeAttribute('selected')
476
+ }
477
+ }
478
+
479
+ currentClasses() {
480
+ if (this.showRunningMethodLogs) {
481
+ console.log('handleAddClasses')
482
+ }
483
+
484
+ if (!this.shouldRunMethods()) return
485
+
486
+ // convert classList to array
487
+ const classListArray = Array.from(this.getElement.value?.classList || [])
488
+
489
+ // commit array to store
490
+ this.pageBuilderStateStore.setCurrentClasses(classListArray)
491
+ }
492
+
493
+ handleAddClasses(userSelectedClass: string): void {
494
+ if (this.showRunningMethodLogs) {
495
+ console.log('handleAddClasses')
496
+ }
497
+
498
+ if (!this.shouldRunMethods()) return
499
+
500
+ if (
501
+ typeof userSelectedClass === 'string' &&
502
+ userSelectedClass !== '' &&
503
+ !userSelectedClass.includes(' ') &&
504
+ // Check if class already exists
505
+ !this.getElement.value?.classList.contains(userSelectedClass)
506
+ ) {
507
+ // Remove any leading/trailing spaces
508
+ const cleanedClass = userSelectedClass.trim()
509
+
510
+ this.getElement.value?.classList.add(cleanedClass)
511
+
512
+ this.pageBuilderStateStore.setElement(this.getElement.value)
513
+
514
+ this.pageBuilderStateStore.setClass(userSelectedClass)
515
+ }
516
+ }
517
+ handleRemoveClasses(userSelectedClass: string): void {
518
+ if (this.showRunningMethodLogs) {
519
+ console.log('handleRemoveClasses')
520
+ }
521
+
522
+ if (!this.shouldRunMethods()) return
523
+
524
+ // remove selected class from element
525
+ if (this.getElement.value?.classList.contains(userSelectedClass)) {
526
+ this.getElement.value?.classList.remove(userSelectedClass)
527
+
528
+ this.pageBuilderStateStore.setElement(this.getElement.value)
529
+ this.pageBuilderStateStore.removeClass(userSelectedClass)
530
+ }
531
+ }
532
+
533
+ handleDeleteElement() {
534
+ if (this.showRunningMethodLogs) {
535
+ console.log('handleDeleteElement')
536
+ }
537
+
538
+ // Get the element to be deleted
539
+ const element = this.getElement.value
540
+
541
+ if (!element) return
542
+
543
+ if (!element?.parentNode) {
544
+ this.pageBuilderStateStore.setComponent(null)
545
+ this.pageBuilderStateStore.setElement(null)
546
+ return
547
+ }
548
+
549
+ // Store the parent of the deleted element
550
+ // if parent element tag is section remove the hole component
551
+ if (element.parentElement?.tagName !== 'SECTION') {
552
+ this.pageBuilderStateStore.setParentElement(element.parentNode)
553
+
554
+ // Store the outerHTML of the deleted element
555
+ this.pageBuilderStateStore.setRestoredElement(element.outerHTML)
556
+
557
+ // Store the next sibling of the deleted element
558
+ this.pageBuilderStateStore.setNextSibling(element.nextSibling)
559
+ }
560
+
561
+ // if parent element tag is section remove the hole component
562
+ if (element.parentElement?.tagName === 'SECTION') {
563
+ this.deleteComponent()
564
+ }
565
+
566
+ // Remove the element from the DOM
567
+ element.remove()
568
+ this.pageBuilderStateStore.setComponent(null)
569
+ this.pageBuilderStateStore.setElement(null)
570
+ }
571
+
572
+ handleRestoreElement() {
573
+ if (this.showRunningMethodLogs) {
574
+ console.log('handleRestoreElement')
575
+ }
576
+
577
+ // Get the stored deleted element and its parent
578
+ if (this.getRestoredElement.value && this.getParentElement.value) {
579
+ // Create a new element from the stored outerHTML
580
+ const newElement = document.createElement('div')
581
+ // Fixed type conversion issue
582
+ newElement.innerHTML = this.getRestoredElement.value
583
+
584
+ // Append the restored element to its parent
585
+ // Insert the restored element before its next sibling in its parent
586
+ if (newElement.firstChild) {
587
+ this.getParentElement.value.insertBefore(newElement.firstChild, this.getNextSibling.value)
588
+ }
589
+ }
590
+
591
+ // Clear
592
+
593
+ this.pageBuilderStateStore.setParentElement(null)
594
+ this.pageBuilderStateStore.setRestoredElement(null)
595
+ this.pageBuilderStateStore.setComponent(null)
596
+ this.pageBuilderStateStore.setElement(null)
597
+
598
+ this.setEventListenersForElements()
599
+ }
600
+
601
+ handleFontWeight(userSelectedFontWeight?: string): void {
602
+ if (this.showRunningMethodLogs) {
603
+ console.log('handleFontWeight')
604
+ }
605
+ if (!userSelectedFontWeight) return
606
+
607
+ this.#applyElementClassChanges(
608
+ userSelectedFontWeight,
609
+ tailwindFontStyles.fontWeight,
610
+ 'setFontWeight',
611
+ )
612
+ }
613
+ handleFontFamily(userSelectedFontFamily?: string): void {
614
+ if (this.showRunningMethodLogs) {
615
+ console.log('handleFontFamily')
616
+ }
617
+ if (!userSelectedFontFamily) return
618
+
619
+ this.#applyElementClassChanges(
620
+ userSelectedFontFamily,
621
+ tailwindFontStyles.fontFamily,
622
+ 'setFontFamily',
623
+ )
624
+ }
625
+ handleFontStyle(userSelectedFontStyle?: string): void {
626
+ if (this.showRunningMethodLogs) {
627
+ console.log('handleFontStyle')
628
+ }
629
+ if (!userSelectedFontStyle) return
630
+
631
+ this.#applyElementClassChanges(
632
+ userSelectedFontStyle,
633
+ tailwindFontStyles.fontStyle,
634
+ 'setFontStyle',
635
+ )
636
+ }
637
+ handleVerticalPadding(userSelectedVerticalPadding?: string): void {
638
+ if (this.showRunningMethodLogs) {
639
+ console.log('handleVerticalPadding')
640
+ }
641
+ if (!userSelectedVerticalPadding) return
642
+
643
+ this.#applyElementClassChanges(
644
+ userSelectedVerticalPadding,
645
+ tailwindPaddingAndMargin.verticalPadding,
646
+ 'setFontVerticalPadding',
647
+ )
648
+ }
649
+ handleHorizontalPadding(userSelectedHorizontalPadding?: string): void {
650
+ if (this.showRunningMethodLogs) {
651
+ console.log('handleHorizontalPadding')
652
+ }
653
+ if (!userSelectedHorizontalPadding) return
654
+
655
+ this.#applyElementClassChanges(
656
+ userSelectedHorizontalPadding,
657
+ tailwindPaddingAndMargin.horizontalPadding,
658
+ 'setFontHorizontalPadding',
659
+ )
660
+ }
661
+
662
+ handleVerticalMargin(userSelectedVerticalMargin?: string): void {
663
+ if (this.showRunningMethodLogs) {
664
+ console.log('handleVerticalMargin')
665
+ }
666
+ if (!userSelectedVerticalMargin) return
667
+
668
+ this.#applyElementClassChanges(
669
+ userSelectedVerticalMargin,
670
+ tailwindPaddingAndMargin.verticalMargin,
671
+ 'setFontVerticalMargin',
672
+ )
673
+ }
674
+ handleHorizontalMargin(userSelectedHorizontalMargin?: string): void {
675
+ if (this.showRunningMethodLogs) {
676
+ console.log('handleHorizontalMargin')
677
+ }
678
+ if (!userSelectedHorizontalMargin) return
679
+
680
+ this.#applyElementClassChanges(
681
+ userSelectedHorizontalMargin,
682
+ tailwindPaddingAndMargin.horizontalMargin,
683
+ 'setFontHorizontalMargin',
684
+ )
685
+ }
686
+
687
+ // border color, style & width / start
688
+ handleBorderStyle(borderStyle?: string): void {
689
+ if (this.showRunningMethodLogs) {
690
+ console.log('handleBorderStyle')
691
+ }
692
+ if (!borderStyle) return
693
+
694
+ this.#applyElementClassChanges(
695
+ borderStyle,
696
+ tailwindBorderStyleWidthPlusColor.borderStyle,
697
+ 'setBorderStyle',
698
+ )
699
+ }
700
+ handleBorderWidth(borderWidth?: string): void {
701
+ if (this.showRunningMethodLogs) {
702
+ console.log('handleBorderWidth')
703
+ }
704
+ if (!borderWidth) return
705
+
706
+ this.#applyElementClassChanges(
707
+ borderWidth,
708
+ tailwindBorderStyleWidthPlusColor.borderWidth,
709
+ 'setBorderWidth',
710
+ )
711
+ }
712
+ handleBorderColor(borderColor?: string): void {
713
+ if (this.showRunningMethodLogs) {
714
+ console.log('handleBorderColor')
715
+ }
716
+ if (!borderColor) return
717
+
718
+ this.#applyElementClassChanges(
719
+ borderColor,
720
+ tailwindBorderStyleWidthPlusColor.borderColor,
721
+ 'setBorderColor',
722
+ )
723
+ }
724
+ // border color, style & width / end
725
+
726
+ handleBackgroundColor(color?: string): void {
727
+ if (!color) return
728
+ this.#applyElementClassChanges(
729
+ color,
730
+ tailwindColors.backgroundColorVariables,
731
+ 'setBackgroundColor',
732
+ )
733
+ }
734
+
735
+ handleTextColor(color?: string): void {
736
+ if (!color) return
737
+ this.#applyElementClassChanges(color, tailwindColors.textColorVariables, 'setTextColor')
738
+ }
739
+
740
+ // border radius / start
741
+ handleBorderRadiusGlobal(borderRadiusGlobal?: string): void {
742
+ if (this.showRunningMethodLogs) {
743
+ console.log('handleBorderRadiusGlobal')
744
+ }
745
+ if (!borderRadiusGlobal) return
746
+
747
+ this.#applyElementClassChanges(
748
+ borderRadiusGlobal,
749
+ tailwindBorderRadius.roundedGlobal,
750
+ 'setBorderRadiusGlobal',
751
+ )
752
+ }
753
+ handleBorderRadiusTopLeft(borderRadiusTopLeft?: string): void {
754
+ if (this.showRunningMethodLogs) {
755
+ console.log('handleBorderRadiusTopLeft')
756
+ }
757
+ if (!borderRadiusTopLeft) return
758
+
759
+ this.#applyElementClassChanges(
760
+ borderRadiusTopLeft,
761
+ tailwindBorderRadius.roundedTopLeft,
762
+ 'setBorderRadiusTopLeft',
763
+ )
764
+ }
765
+ handleBorderRadiusTopRight(borderRadiusTopRight?: string): void {
766
+ if (this.showRunningMethodLogs) {
767
+ console.log('handleBorderRadiusTopRight')
768
+ }
769
+ if (!borderRadiusTopRight) return
770
+
771
+ this.#applyElementClassChanges(
772
+ borderRadiusTopRight,
773
+ tailwindBorderRadius.roundedTopRight,
774
+ 'setBorderRadiusTopRight',
775
+ )
776
+ }
777
+ handleBorderRadiusBottomleft(borderRadiusBottomleft?: string): void {
778
+ if (this.showRunningMethodLogs) {
779
+ console.log('handleBorderRadiusBottomleft')
780
+ }
781
+ if (!borderRadiusBottomleft) return
782
+
783
+ this.#applyElementClassChanges(
784
+ borderRadiusBottomleft,
785
+ tailwindBorderRadius.roundedBottomLeft,
786
+ 'setBorderRadiusBottomleft',
787
+ )
788
+ }
789
+ handleBorderRadiusBottomRight(borderRadiusBottomRight?: string): void {
790
+ if (this.showRunningMethodLogs) {
791
+ console.log('handleBorderRadiusBottomRight')
792
+ }
793
+ if (!borderRadiusBottomRight) return
794
+
795
+ this.#applyElementClassChanges(
796
+ borderRadiusBottomRight,
797
+ tailwindBorderRadius.roundedBottomRight,
798
+ 'setBorderRadiusBottomRight',
799
+ )
800
+ }
801
+ // border radius / end
802
+
803
+ handleFontSize(userSelectedFontSize?: string): void {
804
+ if (this.showRunningMethodLogs) {
805
+ console.log('handleFontSize')
806
+ }
807
+
808
+ if (!this.shouldRunMethods()) return
809
+ if (!userSelectedFontSize) return
810
+ if (!this.getElement.value) return
811
+
812
+ let fontBase = tailwindFontSizes.fontBase.find((size: string) => {
813
+ return this.getElement.value?.classList.contains(size)
814
+ })
815
+ if (fontBase === undefined) {
816
+ fontBase = 'base:none'
817
+ }
818
+
819
+ let fontDesktop = tailwindFontSizes.fontDesktop.find((size: string) => {
820
+ return this.getElement.value?.classList.contains(size)
821
+ })
822
+ if (fontDesktop === undefined) {
823
+ fontDesktop = 'lg:none'
824
+ }
825
+
826
+ let fontTablet = tailwindFontSizes.fontTablet.find((size: string) => {
827
+ return this.getElement.value?.classList.contains(size)
828
+ })
829
+ if (fontTablet === undefined) {
830
+ fontTablet = 'md:none'
831
+ }
832
+
833
+ let fontMobile = tailwindFontSizes.fontMobile.find((size: string) => {
834
+ return this.getElement.value?.classList.contains(size)
835
+ })
836
+ if (fontMobile === undefined) {
837
+ fontMobile = 'sm:none'
838
+ }
839
+
840
+ // set fonts
841
+ this.pageBuilderStateStore.setFontBase(fontBase)
842
+ this.pageBuilderStateStore.setFontDesktop(fontDesktop)
843
+ this.pageBuilderStateStore.setFontTablet(fontTablet)
844
+ this.pageBuilderStateStore.setFontMobile(fontMobile)
845
+
846
+ const getFontBase = computed(() => {
847
+ return this.pageBuilderStateStore.getFontBase
848
+ })
849
+ const getFontDesktop = computed(() => {
850
+ return this.pageBuilderStateStore.getFontDesktop
851
+ })
852
+ const getFontTablet = computed(() => {
853
+ return this.pageBuilderStateStore.getFontTablet
854
+ })
855
+ const getFontMobile = computed(() => {
856
+ return this.pageBuilderStateStore.getFontMobile
857
+ })
858
+
859
+ if (userSelectedFontSize !== undefined && this.getElement.value) {
860
+ if (
861
+ !userSelectedFontSize.includes('sm:') &&
862
+ !userSelectedFontSize.includes('md:') &&
863
+ !userSelectedFontSize.includes('lg:')
864
+ ) {
865
+ this.getElement.value.classList.remove(getFontBase.value)
866
+ if (!userSelectedFontSize.includes('base:none')) {
867
+ this.getElement.value.classList.add(userSelectedFontSize)
868
+ }
869
+
870
+ this.pageBuilderStateStore.setFontBase(userSelectedFontSize)
871
+ }
872
+ if (userSelectedFontSize.includes('lg:')) {
873
+ this.getElement.value.classList.remove(getFontDesktop.value)
874
+ if (!userSelectedFontSize.includes('lg:none')) {
875
+ this.getElement.value.classList.add(userSelectedFontSize)
876
+ }
877
+
878
+ this.pageBuilderStateStore.setFontDesktop(userSelectedFontSize)
879
+ }
880
+ if (userSelectedFontSize.includes('md:')) {
881
+ this.getElement.value.classList.remove(getFontTablet.value)
882
+ if (!userSelectedFontSize.includes('md:none')) {
883
+ this.getElement.value.classList.add(userSelectedFontSize)
884
+ }
885
+
886
+ this.pageBuilderStateStore.setFontTablet(userSelectedFontSize)
887
+ }
888
+ if (userSelectedFontSize.includes('sm:')) {
889
+ this.getElement.value.classList.remove(getFontMobile.value)
890
+ if (!userSelectedFontSize.includes('sm:none')) {
891
+ this.getElement.value.classList.add(userSelectedFontSize)
892
+ }
893
+
894
+ this.pageBuilderStateStore.setFontMobile(userSelectedFontSize)
895
+ }
896
+ this.pageBuilderStateStore.setElement(this.getElement.value)
897
+ }
898
+ }
899
+
900
+ handleBackgroundOpacity(opacity?: string): void {
901
+ if (this.showRunningMethodLogs) {
902
+ console.log('handleBackgroundOpacity')
903
+ }
904
+ if (!opacity) return
905
+
906
+ this.#applyElementClassChanges(
907
+ opacity,
908
+ tailwindOpacities.backgroundOpacities,
909
+ 'setBackgroundOpacity',
910
+ )
911
+ }
912
+ handleOpacity(opacity?: string): void {
913
+ if (this.showRunningMethodLogs) {
914
+ console.log('handleOpacity')
915
+ }
916
+ if (!opacity) return
917
+
918
+ this.#applyElementClassChanges(opacity, tailwindOpacities.opacities, 'setOpacity')
919
+ }
920
+
921
+ deleteAllComponents() {
922
+ if (this.showRunningMethodLogs) {
923
+ console.log('deleteAllComponents')
924
+ }
925
+
926
+ this.pageBuilderStateStore.setComponents([])
927
+ }
928
+
929
+ deleteComponent() {
930
+ if (this.showRunningMethodLogs) {
931
+ console.log('deleteComponent')
932
+ }
933
+
934
+ if (!this.shouldRunMethods()) return
935
+ if (!this.getComponents.value || !this.getComponent.value) return
936
+
937
+ // Find the index of the component to delete
938
+ const indexToDelete = this.getComponents.value.findIndex(
939
+ (component) => component.id === this.getComponent.value?.id,
940
+ )
941
+
942
+ if (indexToDelete === -1) {
943
+ // Component not found in the array, handle this case as needed.
944
+ return
945
+ }
946
+
947
+ // Remove the component from the array
948
+ this.getComponents.value.splice(indexToDelete, 1)
949
+ this.pageBuilderStateStore.setComponents(this.getComponents.value)
950
+
951
+ this.pageBuilderStateStore.setComponent(null)
952
+ this.pageBuilderStateStore.setElement(null)
953
+ }
954
+
955
+ // move component
956
+ // runs when html components are rearranged
957
+ moveComponent(direction: number): void {
958
+ if (this.showRunningMethodLogs) {
959
+ console.log('moveComponent')
960
+ }
961
+
962
+ if (!this.shouldRunMethods()) return
963
+ if (!this.getComponents.value || !this.getComponent.value) return
964
+
965
+ if (this.getComponents.value.length <= 1) return
966
+
967
+ // Get the component you want to move (replace this with your actual logic)
968
+ const componentToMove = this.getComponent.value
969
+
970
+ // Determine the new index where you want to move the component
971
+ const currentIndex = this.getComponents.value.findIndex(
972
+ (component) => component.id === componentToMove.id,
973
+ )
974
+
975
+ if (currentIndex === -1) {
976
+ // Component not found in the array, handle this case as needed.
977
+ return
978
+ }
979
+
980
+ const newIndex = currentIndex + direction
981
+
982
+ // Ensure the newIndex is within bounds
983
+ if (newIndex < 0 || newIndex >= this.getComponents.value.length) {
984
+ return
985
+ }
986
+
987
+ // Move the component to the new position
988
+ this.getComponents.value.splice(currentIndex, 1)
989
+ this.getComponents.value.splice(newIndex, 0, componentToMove)
990
+ }
991
+
992
+ ensureTextAreaHasContent = () => {
993
+ if (this.showRunningMethodLogs) {
994
+ console.log('ensureTextAreaHasContent')
995
+ }
996
+
997
+ if (!this.shouldRunMethods()) return
998
+ if (!this.getElement.value) return
999
+
1000
+ // text content
1001
+ if (typeof this.getElement.value.innerHTML !== 'string') {
1002
+ return
1003
+ }
1004
+ const element = this.getElement.value
1005
+ const elementTag = element.tagName
1006
+
1007
+ if (
1008
+ ['DIV'].includes(elementTag) &&
1009
+ element.tagName.toLowerCase() !== 'img' &&
1010
+ element.textContent &&
1011
+ Number(element.textContent.length) === 0
1012
+ ) {
1013
+ element.classList.add('h-6')
1014
+ element.classList.add('bg-red-50')
1015
+ } else {
1016
+ element.classList.remove('h-6')
1017
+ element.classList.remove('bg-red-50')
1018
+ }
1019
+ }
1020
+
1021
+ //
1022
+ handleTextInput = async (textContentVueModel: string): Promise<void> => {
1023
+ if (this.showRunningMethodLogs) {
1024
+ console.log('handleTextInput')
1025
+ }
1026
+
1027
+ if (!this.shouldRunMethods()) return
1028
+
1029
+ // // text content
1030
+ if (typeof this.getElement.value?.innerHTML !== 'string') {
1031
+ return
1032
+ }
1033
+
1034
+ if (typeof this.getElement.value.innerHTML === 'string') {
1035
+ await this.nextTick
1036
+
1037
+ // Update text content
1038
+ this.getElement.value.textContent = textContentVueModel
1039
+
1040
+ this.pageBuilderStateStore.setTextAreaVueModel(this.getElement.value.innerHTML)
1041
+
1042
+ this.getElement.value.innerHTML = textContentVueModel
1043
+ }
1044
+
1045
+ this.ensureTextAreaHasContent()
1046
+ }
1047
+
1048
+ //
1049
+ //
1050
+ ElOrFirstChildIsIframe() {
1051
+ if (
1052
+ this.getElement.value?.tagName === 'IFRAME' ||
1053
+ this.getElement.value?.firstElementChild?.tagName === 'IFRAME'
1054
+ ) {
1055
+ return true
1056
+ } else {
1057
+ return false
1058
+ }
1059
+ }
1060
+ //
1061
+ //
1062
+ //
1063
+ selectedElementIsValidText() {
1064
+ let reachedElseStatement = false
1065
+
1066
+ // Get all child elements of the parentDiv
1067
+ const childElements = this.getElement.value?.children
1068
+ if (
1069
+ this.getElement.value?.tagName === 'IMG' ||
1070
+ this.getElement.value?.firstElementChild?.tagName === 'IFRAME'
1071
+ ) {
1072
+ return
1073
+ }
1074
+ if (!childElements) {
1075
+ return
1076
+ }
1077
+
1078
+ Array.from(childElements).forEach((element) => {
1079
+ if (element?.tagName === 'IMG' || element?.tagName === 'DIV') {
1080
+ reachedElseStatement = false
1081
+ } else {
1082
+ reachedElseStatement = true
1083
+ }
1084
+ })
1085
+
1086
+ return reachedElseStatement
1087
+ }
1088
+
1089
+ previewCurrentDesign() {
1090
+ if (this.showRunningMethodLogs) {
1091
+ console.log('previewCurrentDesign')
1092
+ }
1093
+
1094
+ this.pageBuilderStateStore.setElement(null)
1095
+
1096
+ const addedHtmlComponents = ref<string[]>([])
1097
+ // preview current design in external browser tab
1098
+ // iterate over each top-level section component
1099
+ document.querySelectorAll('section:not(section section)').forEach((section) => {
1100
+ // remove hovered and selected
1101
+
1102
+ // remove hovered
1103
+ const hoveredElement = section.querySelector('[hovered]')
1104
+ if (hoveredElement) {
1105
+ hoveredElement.removeAttribute('hovered')
1106
+ }
1107
+
1108
+ // remove selected
1109
+ const selectedElement = section.querySelector('[selected]')
1110
+ if (selectedElement) {
1111
+ selectedElement.removeAttribute('selected')
1112
+ }
1113
+
1114
+ // push outer html into the array
1115
+ addedHtmlComponents.value.push(section.outerHTML)
1116
+ })
1117
+
1118
+ // stringify added html components
1119
+ const stringifiedComponents = JSON.stringify(addedHtmlComponents.value)
1120
+
1121
+ // commit
1122
+ this.pageBuilderStateStore.setCurrentLayoutPreview(stringifiedComponents)
1123
+
1124
+ // set added html components back to empty array
1125
+ addedHtmlComponents.value = []
1126
+
1127
+ //
1128
+ }
1129
+
1130
+ async saveComponentsLocalStorage() {
1131
+ await this.nextTick
1132
+ this.synchronizeDOMAndComponents()
1133
+
1134
+ if (this.showRunningMethodLogs) {
1135
+ console.log('saveComponentsLocalStorage')
1136
+ }
1137
+
1138
+ await this.nextTick
1139
+ if (this.getLocalStorageItemName.value) {
1140
+ localStorage.setItem(
1141
+ this.getLocalStorageItemName.value,
1142
+ JSON.stringify(this.getComponents.value),
1143
+ )
1144
+ }
1145
+ }
1146
+
1147
+ async saveComponentsLocalStorageUpdate() {
1148
+ if (this.showRunningMethodLogs) {
1149
+ console.log('saveComponentsLocalStorageUpdate')
1150
+ }
1151
+
1152
+ await this.nextTick
1153
+ if (this.getLocalStorageItemNameUpdate.value) {
1154
+ localStorage.setItem(
1155
+ this.getLocalStorageItemNameUpdate.value,
1156
+ JSON.stringify(this.getComponents.value),
1157
+ )
1158
+ }
1159
+ }
1160
+ async removeItemComponentsLocalStorageUpdate() {
1161
+ if (this.showRunningMethodLogs) {
1162
+ console.log('saveComponentsLocalStorageUpdate')
1163
+ }
1164
+
1165
+ await this.nextTick
1166
+ if (this.getLocalStorageItemNameUpdate.value) {
1167
+ localStorage.removeItem(this.getLocalStorageItemNameUpdate.value)
1168
+ }
1169
+ }
1170
+
1171
+ areComponentsStoredInLocalStorage() {
1172
+ if (this.showRunningMethodLogs) {
1173
+ console.log('areComponentsStoredInLocalStorage')
1174
+ }
1175
+
1176
+ if (!this.getLocalStorageItemName.value) return false
1177
+
1178
+ const savedCurrentDesign = localStorage.getItem(this.getLocalStorageItemName.value)
1179
+ if (savedCurrentDesign) {
1180
+ let components = JSON.parse(savedCurrentDesign)
1181
+ if (!components) {
1182
+ components = []
1183
+ }
1184
+
1185
+ this.pageBuilderStateStore.setComponents(components)
1186
+
1187
+ return true
1188
+ }
1189
+
1190
+ return false
1191
+ }
1192
+ //
1193
+ areComponentsStoredInLocalStorageUpdate() {
1194
+ if (this.showRunningMethodLogs) {
1195
+ console.log('areComponentsStoredInLocalStorageUpdate')
1196
+ }
1197
+
1198
+ if (!this.getLocalStorageItemNameUpdate.value) return false
1199
+
1200
+ const savedCurrentDesign = localStorage.getItem(this.getLocalStorageItemNameUpdate.value)
1201
+ if (savedCurrentDesign) {
1202
+ let components = JSON.parse(savedCurrentDesign)
1203
+ if (!components) {
1204
+ components = []
1205
+ }
1206
+
1207
+ this.pageBuilderStateStore.setComponents(components)
1208
+
1209
+ return true
1210
+ }
1211
+
1212
+ return false
1213
+ }
1214
+ //
1215
+ async updateBasePrimaryImage(data: { type: string }): Promise<void> {
1216
+ if (this.showRunningMethodLogs) {
1217
+ console.log('updateBasePrimaryImage')
1218
+ }
1219
+
1220
+ if (!this.getElement.value) return
1221
+
1222
+ if (data.type === 'unsplash' && this.getCurrentImage.value) {
1223
+ if (this.getCurrentImage.value.file) {
1224
+ await this.nextTick
1225
+
1226
+ this.pageBuilderStateStore.setBasePrimaryImage(`${this.getCurrentImage.value.file}`)
1227
+ }
1228
+ }
1229
+ }
1230
+
1231
+ showBasePrimaryImage() {
1232
+ if (this.showRunningMethodLogs) {
1233
+ console.log('showBasePrimaryImage')
1234
+ }
1235
+
1236
+ if (!this.getElement.value) return
1237
+
1238
+ const currentImageContainer = document.createElement('div')
1239
+
1240
+ currentImageContainer.innerHTML = this.getElement.value.outerHTML
1241
+
1242
+ // Get all img and div within the current image container
1243
+ const imgElements = currentImageContainer.getElementsByTagName('img')
1244
+ const divElements = currentImageContainer.getElementsByTagName('div')
1245
+
1246
+ // Check if there is exactly one img and no div
1247
+ if (imgElements.length === 1 && divElements.length === 0) {
1248
+ // Return the source of the only img
1249
+
1250
+ this.pageBuilderStateStore.setBasePrimaryImage(imgElements[0].src)
1251
+
1252
+ return
1253
+ }
1254
+
1255
+ this.pageBuilderStateStore.setBasePrimaryImage(null)
1256
+ }
1257
+
1258
+ #addHyperlinkToElement(
1259
+ hyperlinkEnable: boolean,
1260
+ urlInput: string | null,
1261
+ openHyperlinkInNewTab: boolean,
1262
+ ) {
1263
+ if (this.showRunningMethodLogs) {
1264
+ console.log('#addHyperlinkToElement')
1265
+ }
1266
+
1267
+ if (!this.shouldRunMethods()) return
1268
+ if (!this.getElement.value) return
1269
+
1270
+ const parentHyperlink = this.getElement.value.closest('a')
1271
+ const hyperlink = this.getElement.value.querySelector('a')
1272
+
1273
+ this.pageBuilderStateStore.setHyperlinkError(null)
1274
+
1275
+ // url validation
1276
+ const urlRegex = /^https?:\/\//
1277
+
1278
+ const isValidURL = ref(true)
1279
+
1280
+ if (hyperlinkEnable === true && urlInput !== null) {
1281
+ isValidURL.value = urlRegex.test(urlInput)
1282
+ }
1283
+
1284
+ if (isValidURL.value === false) {
1285
+ this.pageBuilderStateStore.setHyperlinkMessage(null)
1286
+
1287
+ this.pageBuilderStateStore.setHyperlinkError('URL is not valid')
1288
+ return
1289
+ }
1290
+
1291
+ if (hyperlinkEnable === true && typeof urlInput === 'string') {
1292
+ // check if element contains child hyperlink tag
1293
+ // updated existing url
1294
+ if (hyperlink !== null && urlInput.length !== 0) {
1295
+ hyperlink.href = urlInput
1296
+
1297
+ // Conditionally set the target attribute if openHyperlinkInNewTab is true
1298
+ if (openHyperlinkInNewTab === true) {
1299
+ hyperlink.target = '_blank'
1300
+ }
1301
+ if (openHyperlinkInNewTab === false) {
1302
+ hyperlink.removeAttribute('target')
1303
+ }
1304
+
1305
+ hyperlink.textContent = this.getElement.value.textContent
1306
+
1307
+ this.pageBuilderStateStore.setHyperlinkMessage('Succesfully updated element hyperlink')
1308
+
1309
+ this.pageBuilderStateStore.setElementContainsHyperlink(true)
1310
+
1311
+ return
1312
+ }
1313
+
1314
+ // check if element contains child a tag
1315
+ if (hyperlink === null && urlInput.length !== 0) {
1316
+ // add a href
1317
+ if (parentHyperlink === null) {
1318
+ const link = document.createElement('a')
1319
+ link.href = urlInput
1320
+
1321
+ // Conditionally set the target attribute if openHyperlinkInNewTab is true
1322
+ if (openHyperlinkInNewTab === true) {
1323
+ link.target = '_blank'
1324
+ }
1325
+
1326
+ link.textContent = this.getElement.value.textContent
1327
+ this.getElement.value.textContent = ''
1328
+ this.getElement.value.appendChild(link)
1329
+
1330
+ this.pageBuilderStateStore.setHyperlinkMessage('Successfully added hyperlink to element')
1331
+
1332
+ this.pageBuilderStateStore.setElementContainsHyperlink(true)
1333
+
1334
+ return
1335
+ }
1336
+ }
1337
+ //
1338
+ }
1339
+
1340
+ if (hyperlinkEnable === false && urlInput === 'removeHyperlink') {
1341
+ // To remove the added hyperlink tag
1342
+ const originalText = this.getElement.value.textContent || ''
1343
+ const textNode = document.createTextNode(originalText)
1344
+ this.getElement.value.textContent = ''
1345
+ this.getElement.value.appendChild(textNode)
1346
+
1347
+ this.pageBuilderStateStore.setHyberlinkEnable(false)
1348
+ this.pageBuilderStateStore.setElementContainsHyperlink(false)
1349
+ }
1350
+ }
1351
+
1352
+ #checkForHyperlink() {
1353
+ if (this.showRunningMethodLogs) {
1354
+ console.log('#checkForHyperlink')
1355
+ }
1356
+
1357
+ if (!this.shouldRunMethods()) return
1358
+ if (!this.getElement.value) return
1359
+
1360
+ const hyperlink = this.getElement.value.querySelector('a')
1361
+ if (hyperlink !== null) {
1362
+ this.pageBuilderStateStore.setHyberlinkEnable(true)
1363
+ this.pageBuilderStateStore.setElementContainsHyperlink(true)
1364
+ this.pageBuilderStateStore.setHyperlinkInput(hyperlink.href)
1365
+ this.pageBuilderStateStore.setHyperlinkMessage(null)
1366
+ this.pageBuilderStateStore.setHyperlinkError(null)
1367
+
1368
+ if (hyperlink.target === '_blank') {
1369
+ this.pageBuilderStateStore.setOpenHyperlinkInNewTab(true)
1370
+ }
1371
+ if (hyperlink.target !== '_blank') {
1372
+ this.pageBuilderStateStore.setOpenHyperlinkInNewTab(false)
1373
+ }
1374
+
1375
+ return
1376
+ }
1377
+
1378
+ this.pageBuilderStateStore.setElementContainsHyperlink(false)
1379
+ this.pageBuilderStateStore.setHyperlinkInput('')
1380
+ this.pageBuilderStateStore.setHyperlinkError(null)
1381
+ this.pageBuilderStateStore.setHyperlinkMessage(null)
1382
+ this.pageBuilderStateStore.setHyberlinkEnable(false)
1383
+ }
1384
+
1385
+ handleHyperlink(
1386
+ hyperlinkEnable?: boolean,
1387
+ urlInput?: string | null,
1388
+ openHyperlinkInNewTab?: boolean,
1389
+ ): void {
1390
+ if (this.showRunningMethodLogs) {
1391
+ console.log('handleHyperlink')
1392
+ }
1393
+
1394
+ if (!this.shouldRunMethods()) return
1395
+
1396
+ this.pageBuilderStateStore.setHyperlinkAbility(true)
1397
+
1398
+ const parentHyperlink = this.getElement.value?.closest('a')
1399
+
1400
+ // handle case where parent element already has an a href tag
1401
+ // when clicking directly on a hyperlink
1402
+ if (parentHyperlink !== null) {
1403
+ this.pageBuilderStateStore.setHyperlinkAbility(false)
1404
+ }
1405
+ //
1406
+ const elementTag = this.getElement.value?.tagName.toUpperCase()
1407
+
1408
+ if (
1409
+ elementTag !== 'P' &&
1410
+ elementTag !== 'H1' &&
1411
+ elementTag !== 'H2' &&
1412
+ elementTag !== 'H3' &&
1413
+ elementTag !== 'H4' &&
1414
+ elementTag !== 'H5' &&
1415
+ elementTag !== 'H6'
1416
+ ) {
1417
+ this.pageBuilderStateStore.setHyperlinkAbility(false)
1418
+ }
1419
+
1420
+ if (hyperlinkEnable === undefined) {
1421
+ this.#checkForHyperlink()
1422
+ return
1423
+ }
1424
+
1425
+ this.#addHyperlinkToElement(hyperlinkEnable, urlInput || null, openHyperlinkInNewTab || false)
1426
+ }
1427
+
1428
+ handlePageBuilderMethods(): void {
1429
+ if (!this.shouldRunMethods()) return
1430
+
1431
+ this.pageBuilderStateStore.setParentElement(null)
1432
+ this.pageBuilderStateStore.setRestoredElement(null)
1433
+
1434
+ // handle custom URL
1435
+ this.handleHyperlink(undefined, null, false)
1436
+ // handle opacity
1437
+ this.handleOpacity(undefined)
1438
+ // handle BG opacity
1439
+ this.handleBackgroundOpacity(undefined)
1440
+ // displayed image
1441
+ this.showBasePrimaryImage()
1442
+ // border style
1443
+ this.handleBorderStyle(undefined)
1444
+ // border width
1445
+ this.handleBorderWidth(undefined)
1446
+ // border color
1447
+ this.handleBorderColor(undefined)
1448
+ // border radius
1449
+ this.handleBorderRadiusGlobal(undefined)
1450
+ // border radius
1451
+ this.handleBorderRadiusTopLeft(undefined)
1452
+ // border radius
1453
+ this.handleBorderRadiusTopRight(undefined)
1454
+ // border radius
1455
+ this.handleBorderRadiusBottomleft(undefined)
1456
+ // border radius
1457
+ this.handleBorderRadiusBottomRight(undefined)
1458
+ // handle font size
1459
+ this.handleFontSize(undefined)
1460
+ // handle font weight
1461
+ this.handleFontWeight(undefined)
1462
+ // handle font family
1463
+ this.handleFontFamily(undefined)
1464
+ // handle font style
1465
+ this.handleFontStyle(undefined)
1466
+ // handle vertical padding
1467
+ this.handleVerticalPadding(undefined)
1468
+ // handle horizontal padding
1469
+ this.handleHorizontalPadding(undefined)
1470
+ // handle vertical margin
1471
+ this.handleVerticalMargin(undefined)
1472
+ // handle horizontal margin
1473
+ this.handleHorizontalMargin(undefined)
1474
+ // handle color
1475
+ this.handleBackgroundColor(undefined)
1476
+ // handle text color
1477
+ this.handleTextColor(undefined)
1478
+ // handle classes
1479
+ this.currentClasses()
1480
+ }
1481
+ }
1482
+
1483
+ export default PageBuilder