@myissue/vue-website-page-builder 3.3.64 → 3.3.67

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 (48) hide show
  1. package/README.md +216 -129
  2. package/dist/logo/mybuilder_new_lowercase.svg +17558 -0
  3. package/dist/vue-website-page-builder.css +1 -1
  4. package/dist/vue-website-page-builder.js +16345 -13089
  5. package/dist/vue-website-page-builder.umd.cjs +78 -54
  6. package/package.json +3 -2
  7. package/src/Components/DemoUnsplash.vue +1 -4
  8. package/src/Components/PageBuilder/EditorMenu/Editables/BackgroundColorEditor.vue +4 -3
  9. package/src/Components/PageBuilder/EditorMenu/Editables/BorderRadius.vue +32 -13
  10. package/src/Components/PageBuilder/EditorMenu/Editables/Borders.vue +12 -8
  11. package/src/Components/PageBuilder/EditorMenu/Editables/ClassEditor.vue +10 -8
  12. package/src/Components/PageBuilder/EditorMenu/Editables/EditGetElement.vue +5 -5
  13. package/src/Components/PageBuilder/EditorMenu/Editables/ManageBackgroundOpacity.vue +12 -9
  14. package/src/Components/PageBuilder/EditorMenu/Editables/ManageOpacity.vue +6 -4
  15. package/src/Components/PageBuilder/EditorMenu/Editables/Margin.vue +11 -5
  16. package/src/Components/PageBuilder/EditorMenu/Editables/OpacityEditor.vue +1 -1
  17. package/src/Components/PageBuilder/EditorMenu/Editables/Padding.vue +11 -5
  18. package/src/Components/PageBuilder/EditorMenu/Editables/StyleEditor.vue +116 -0
  19. package/src/Components/PageBuilder/EditorMenu/Editables/TextColorEditor.vue +2 -1
  20. package/src/Components/PageBuilder/EditorMenu/Editables/Typography.vue +32 -9
  21. package/src/Components/PageBuilder/EditorMenu/RightSidebarEditor.vue +63 -74
  22. package/src/Components/PageBuilder/Settings/PageBuilderSettings.vue +3 -3
  23. package/src/Components/PageBuilder/ToolbarOption/ToolbarOption.vue +14 -11
  24. package/src/PageBuilder/PageBuilder.vue +169 -65
  25. package/src/PageBuilder/Preview.vue +25 -9
  26. package/src/composables/builderInstance.ts +3 -2
  27. package/src/composables/extractCleanHTMLFromPageBuilder.ts +4 -3
  28. package/src/css/app.css +10 -70
  29. package/src/i18n.ts +28 -0
  30. package/src/locales/ar.json +136 -0
  31. package/src/locales/de.json +136 -0
  32. package/src/locales/en.json +136 -0
  33. package/src/locales/es.json +136 -0
  34. package/src/locales/fr.json +136 -0
  35. package/src/locales/hi.json +136 -0
  36. package/src/locales/ja.json +136 -0
  37. package/src/locales/pt.json +136 -0
  38. package/src/locales/ru.json +136 -0
  39. package/src/locales/zh-Hans.json +136 -0
  40. package/src/main.ts +10 -5
  41. package/src/services/LocalStorageManager.ts +1 -162
  42. package/src/services/PageBuilderService.ts +730 -290
  43. package/src/stores/page-builder-state.ts +8 -0
  44. package/src/tests/PageBuilderTest.vue +41 -70
  45. package/src/tests/componentsArray.test.json +3 -3
  46. package/src/tests/pageBuilderService.test.ts +7 -1
  47. package/src/types/index.ts +17 -3
  48. package/src/utils/html-elements/component.ts +10 -10
@@ -1,14 +1,13 @@
1
- import { LocalStorageManager } from './LocalStorageManager'
2
-
3
- // Type definitions
4
1
  import type {
5
2
  BuilderResourceData,
6
3
  ComponentObject,
7
4
  ImageObject,
8
5
  PageBuilderConfig,
6
+ PageSettings,
9
7
  StartBuilderResult,
10
8
  } from '../types'
11
9
  import type { usePageBuilderStateStore } from '../stores/page-builder-state'
10
+ // import { i18n } from '../i18n' // i18n is now async and must be passed in, not imported
12
11
 
13
12
  import tailwindFontSizes from '../utils/builder/tailwind-font-sizes'
14
13
  import tailwindColors from '../utils/builder/tailwaind-colors'
@@ -24,7 +23,36 @@ import { delay } from '../composables/delay'
24
23
  import { isEmptyObject } from '../helpers/isEmptyObject'
25
24
  import { extractCleanHTMLFromPageBuilder } from '../composables/extractCleanHTMLFromPageBuilder'
26
25
 
26
+ // Define available languages as a type and an array for easy iteration and type safety
27
+ export type AvailableLanguage =
28
+ | 'en'
29
+ | 'zh-Hans'
30
+ | 'fr'
31
+ | 'ja'
32
+ | 'ru'
33
+ | 'es'
34
+ | 'pt'
35
+ | 'de'
36
+ | 'ar'
37
+ | 'hi'
38
+
39
+ export const AVAILABLE_LANGUAGES: AvailableLanguage[] = [
40
+ 'en',
41
+ 'zh-Hans',
42
+ 'fr',
43
+ 'ja',
44
+ 'ru',
45
+ 'es',
46
+ 'pt',
47
+ 'de',
48
+ 'ar',
49
+ 'hi',
50
+ ]
51
+
52
+ import type { I18n } from 'vue-i18n'
53
+
27
54
  export class PageBuilderService {
55
+ private i18n: I18n
28
56
  // Class properties with types
29
57
  private fontSizeRegex =
30
58
  /^(sm:|md:|lg:|xl:|2xl:)?pbx-text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)$/
@@ -44,26 +72,21 @@ export class PageBuilderService {
44
72
  private hasStartedEditing: boolean = false
45
73
  // Hold data from Database or Backend for updated post
46
74
  private originalComponents: BuilderResourceData | undefined = undefined
47
- // Holds data to be mounted when #pagebuilder is not yet present in the DOM
48
- private pendingMountData: BuilderResourceData | null = null
75
+ // Holds data to be mounted when pagebuilder is not yet present in the DOM
76
+ private savedMountComponents: BuilderResourceData | null = null
77
+ private pendingMountComponents: BuilderResourceData | null = null
49
78
  private isPageBuilderMissingOnStart: boolean = false
50
- private localStorageManager: LocalStorageManager
51
79
 
52
- constructor(pageBuilderStateStore: ReturnType<typeof usePageBuilderStateStore>) {
53
- this.localStorageManager = new LocalStorageManager(
54
- pageBuilderStateStore,
55
- this.sanitizeForLocalStorage,
56
- )
80
+ constructor(pageBuilderStateStore: ReturnType<typeof usePageBuilderStateStore>, i18n: I18n) {
81
+ this.i18n = i18n
57
82
  this.hasStartedEditing = false
58
83
  this.pageBuilderStateStore = pageBuilderStateStore
59
-
60
- this.getLocalStorageItemName = computed(
61
- () => this.pageBuilderStateStore.getLocalStorageItemName,
62
- )
63
-
64
84
  this.getApplyImageToSelection = computed(
65
85
  () => this.pageBuilderStateStore.getApplyImageToSelection,
66
86
  )
87
+ this.getLocalStorageItemName = computed(
88
+ () => this.pageBuilderStateStore.getLocalStorageItemName,
89
+ )
67
90
  this.getHyberlinkEnable = computed(() => this.pageBuilderStateStore.getHyberlinkEnable)
68
91
  this.getComponents = computed(() => this.pageBuilderStateStore.getComponents)
69
92
 
@@ -97,17 +120,28 @@ export class PageBuilderService {
97
120
  'SPAN',
98
121
  'BLOCKQUOTE',
99
122
  'BR',
123
+ 'PRE',
124
+ 'CODE',
125
+ 'MARK',
126
+ 'DEL',
127
+ 'INS',
128
+ 'U',
129
+ 'FIGURE',
130
+ 'FIGCAPTION',
100
131
  ]
101
132
  }
102
133
 
134
+ public availableLanguage(): AvailableLanguage[] {
135
+ return AVAILABLE_LANGUAGES
136
+ }
103
137
  // Deselect any selected or hovered elements in the builder UI
104
138
  async clearHtmlSelection(): Promise<void> {
105
139
  this.pageBuilderStateStore.setComponent(null)
106
140
  this.pageBuilderStateStore.setElement(null)
107
- await this.#removeHoveredAndSelected()
141
+ await this.removeHoveredAndSelected()
108
142
  }
109
143
 
110
- #ensureUpdateOrCreateConfig(config: PageBuilderConfig): void {
144
+ private ensureUpdateOrCreateConfig(config: PageBuilderConfig): void {
111
145
  // Case A: updateOrCreate is missing or an empty object
112
146
  if (!config.updateOrCreate || (config.updateOrCreate && isEmptyObject(config.updateOrCreate))) {
113
147
  const updatedConfig = {
@@ -183,7 +217,7 @@ export class PageBuilderService {
183
217
  }
184
218
  }
185
219
 
186
- #validateUserProvidedComponents(components: unknown) {
220
+ private validateUserProvidedComponents(components: unknown) {
187
221
  const formType =
188
222
  this.pageBuilderStateStore.getPageBuilderConfig &&
189
223
  this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
@@ -235,7 +269,56 @@ export class PageBuilderService {
235
269
  return
236
270
  }
237
271
 
238
- #validateConfig(config: PageBuilderConfig): void {
272
+ private ensureLanguage(config: PageBuilderConfig): void {
273
+ // Set default language config if missing, empty, or language missing/empty
274
+ const defaultLang = 'en'
275
+ const defaultEnable = ['en', 'zh-Hans', 'fr', 'ja', 'ru', 'es', 'pt', 'de', 'ar', 'hi'] as const
276
+
277
+ let needsDefault = false
278
+ const userSettings = config.userSettings
279
+ const language = userSettings && userSettings.language
280
+
281
+ if (!userSettings || isEmptyObject(userSettings)) {
282
+ needsDefault = true
283
+ } else if (!language || isEmptyObject(language)) {
284
+ needsDefault = true
285
+ }
286
+
287
+ if (needsDefault) {
288
+ const updatedLanguage = {
289
+ ...config,
290
+ userSettings: {
291
+ ...userSettings,
292
+ language: {
293
+ default: defaultLang,
294
+ enable: defaultEnable as typeof defaultEnable,
295
+ },
296
+ },
297
+ } as const
298
+ this.pageBuilderStateStore.setPageBuilderConfig(updatedLanguage)
299
+ return
300
+ }
301
+
302
+ // Ensure default is in enable array
303
+ if (language && Array.isArray(language.enable) && language.default) {
304
+ if (!language.enable.includes(language.default)) {
305
+ const updatedEnable = [...language.enable, language.default]
306
+ const updatedLanguage = {
307
+ ...config,
308
+ userSettings: {
309
+ ...userSettings,
310
+ language: {
311
+ ...language,
312
+ enable: updatedEnable,
313
+ },
314
+ },
315
+ } as const
316
+ this.pageBuilderStateStore.setPageBuilderConfig(updatedLanguage)
317
+ }
318
+ }
319
+ }
320
+
321
+ private validateConfig(config: PageBuilderConfig): void {
239
322
  const defaultConfigValues = {
240
323
  updateOrCreate: {
241
324
  formType: 'create',
@@ -249,7 +332,53 @@ export class PageBuilderService {
249
332
  }
250
333
 
251
334
  if (config && Object.keys(config).length !== 0 && config.constructor === Object) {
252
- this.#ensureUpdateOrCreateConfig(config)
335
+ this.ensureUpdateOrCreateConfig(config)
336
+ }
337
+
338
+ this.ensureLanguage(config)
339
+ }
340
+
341
+ public saveBuilderConfigToLocalStorage(specificConfig: PageBuilderConfig) {
342
+ // Only save userSettings to localStorage
343
+ if (specificConfig && specificConfig.userSettings) {
344
+ localStorage.setItem(
345
+ 'pageBuilderConfig',
346
+ JSON.stringify({ userSettings: specificConfig.userSettings }),
347
+ )
348
+ }
349
+ }
350
+
351
+ private checkBuilderConfigToLocalStorage(currentConfig: PageBuilderConfig) {
352
+ const savedConfigRaw = localStorage.getItem('pageBuilderConfig')
353
+
354
+ if (savedConfigRaw) {
355
+ try {
356
+ const savedConfig = JSON.parse(savedConfigRaw)
357
+ // Deep merge: prefer all keys from savedConfig, fallback to currentConfig
358
+ const mergedConfig = {
359
+ ...currentConfig,
360
+ ...savedConfig,
361
+ userSettings: {
362
+ ...currentConfig.userSettings,
363
+ ...savedConfig.userSettings,
364
+ },
365
+ }
366
+
367
+ this.pageBuilderStateStore.setPageBuilderConfig(mergedConfig)
368
+
369
+ const saveLang =
370
+ this.pageBuilderStateStore.getPageBuilderConfig &&
371
+ this.pageBuilderStateStore.getPageBuilderConfig.userSettings &&
372
+ this.pageBuilderStateStore.getPageBuilderConfig.userSettings.language &&
373
+ this.pageBuilderStateStore.getPageBuilderConfig.userSettings.language.default
374
+
375
+ if (saveLang) {
376
+ this.i18n.global.locale = saveLang
377
+ }
378
+ return
379
+ } catch (e) {
380
+ console.warn('Failed to parse saved pageBuilderConfig from localStorage:', e)
381
+ }
253
382
  }
254
383
  }
255
384
 
@@ -271,25 +400,42 @@ export class PageBuilderService {
271
400
  // Prevents builder actions to prevent errors caused by missing DOM .
272
401
  this.pageBuilderStateStore.setBuilderStarted(true)
273
402
  const pagebuilder = document.querySelector('#pagebuilder')
274
- let validation
275
403
 
404
+ let validation
276
405
  try {
277
406
  this.originalComponents = passedComponentsArray
278
407
  this.pageBuilderStateStore.setPageBuilderConfig(config)
279
408
  // Validate and normalize the config (ensure required fields are present)
280
- this.#validateConfig(config)
409
+ this.validateConfig(config)
281
410
 
282
- validation = this.#validateUserProvidedComponents(passedComponentsArray)
411
+ if (
412
+ this.pageBuilderStateStore.getPageBuilderConfig &&
413
+ this.pageBuilderStateStore.getPageBuilderConfig.userSettings &&
414
+ this.pageBuilderStateStore.getPageBuilderConfig.userSettings.language &&
415
+ this.pageBuilderStateStore.getPageBuilderConfig.userSettings.language.default
416
+ ) {
417
+ this.i18n.global.locale =
418
+ this.pageBuilderStateStore.getPageBuilderConfig.userSettings.language.default
419
+ }
420
+
421
+ if (this.pageBuilderStateStore.getPageBuilderConfig) {
422
+ this.checkBuilderConfigToLocalStorage(this.pageBuilderStateStore.getPageBuilderConfig)
423
+ }
424
+
425
+ validation = this.validateUserProvidedComponents(passedComponentsArray)
283
426
 
284
427
  // Update the localStorage key name based on the config/resource
285
- this.localStorageManager.updateLocalStorageItemName()
428
+ this.updateLocalStorageItemName()
286
429
 
430
+ if (passedComponentsArray) {
431
+ this.savedMountComponents = passedComponentsArray
432
+ }
287
433
  // Page Builder is not Present in the DOM but Components have been passed to the Builder
288
434
  if (!pagebuilder) {
289
435
  this.isPageBuilderMissingOnStart = true
290
436
  }
291
437
  if (passedComponentsArray && !pagebuilder) {
292
- this.pendingMountData = passedComponentsArray
438
+ this.pendingMountComponents = passedComponentsArray
293
439
  }
294
440
  // Page Builder is Present in the DOM & Components have been passed to the Builder
295
441
  if (pagebuilder) {
@@ -297,9 +443,7 @@ export class PageBuilderService {
297
443
  }
298
444
 
299
445
  // result to end user
300
-
301
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
302
- const result: any = {
446
+ const result: StartBuilderResult = {
303
447
  message: 'Page builder started successfully.',
304
448
  }
305
449
 
@@ -307,7 +451,7 @@ export class PageBuilderService {
307
451
  result.validation = validation
308
452
  }
309
453
 
310
- // passedComponentsArray
454
+ // PassedComponentsArray
311
455
  if (Array.isArray(passedComponentsArray) && passedComponentsArray.length >= 0) {
312
456
  result.passedComponentsArray = passedComponentsArray
313
457
  }
@@ -323,7 +467,10 @@ export class PageBuilderService {
323
467
  }
324
468
  }
325
469
 
326
- async completeBuilderInitialization(passedComponentsArray?: BuilderResourceData): Promise<void> {
470
+ async completeBuilderInitialization(
471
+ passedComponentsArray?: BuilderResourceData,
472
+ internalPageBuilderCall?: boolean,
473
+ ): Promise<void> {
327
474
  this.pageBuilderStateStore.setIsLoadingGlobal(true)
328
475
  await delay(400)
329
476
 
@@ -333,63 +480,78 @@ export class PageBuilderService {
333
480
  const config = this.pageBuilderStateStore.getPageBuilderConfig
334
481
  const formType = config && config.updateOrCreate && config.updateOrCreate.formType
335
482
 
336
- const localStorageData = this.loadStoredComponentsFromStorage()
483
+ const localStorageData = this.getSavedPageHtml()
337
484
 
338
485
  // Deselect any selected or hovered elements in the builder UI
339
486
  await this.clearHtmlSelection()
340
487
 
341
- //
342
488
  if (formType === 'update' || formType === 'create') {
343
- if (!this.pendingMountData) {
344
- //
345
- //
346
- if (!passedComponentsArray && !this.isPageBuilderMissingOnStart && localStorageData) {
347
- await this.#completeMountProcess(localStorageData)
489
+ if (!this.pendingMountComponents) {
490
+ // Page Builder Is initially present in DOM
491
+ if (!passedComponentsArray && this.isPageBuilderMissingOnStart && localStorageData) {
492
+ console.log('1111:', internalPageBuilderCall)
493
+ await this.completeMountProcess(localStorageData)
348
494
  return
349
495
  }
350
496
  if (passedComponentsArray && !localStorageData) {
351
- await this.#completeMountProcess(JSON.stringify(passedComponentsArray), true)
497
+ console.log('2222:', internalPageBuilderCall)
498
+ await this.completeMountProcess(JSON.stringify(passedComponentsArray), true)
499
+ this.saveDomComponentsToLocalStorage()
352
500
  return
353
501
  }
354
502
 
355
503
  if (passedComponentsArray && localStorageData) {
504
+ console.log('3333:', internalPageBuilderCall)
356
505
  this.pageBuilderStateStore.setHasLocalDraftForUpdate(true)
357
-
358
- await this.#completeMountProcess(JSON.stringify(passedComponentsArray), true)
506
+ await this.completeMountProcess(JSON.stringify(passedComponentsArray), true)
359
507
  return
360
508
  }
361
-
362
- if (!passedComponentsArray && localStorageData && this.isPageBuilderMissingOnStart) {
363
- await this.#completeMountProcess(localStorageData)
509
+ if (!passedComponentsArray && localStorageData && !this.savedMountComponents) {
510
+ console.log('4444:', internalPageBuilderCall)
511
+ await this.completeMountProcess(localStorageData)
512
+ return
513
+ }
514
+ if (!passedComponentsArray && this.savedMountComponents && localStorageData) {
515
+ console.log('5555:', internalPageBuilderCall)
516
+ await this.completeMountProcess(JSON.stringify(this.savedMountComponents))
364
517
  return
365
518
  }
366
519
 
367
520
  if (!passedComponentsArray && !localStorageData && this.isPageBuilderMissingOnStart) {
368
- await this.#completeMountProcess(JSON.stringify([]))
521
+ console.log('6666:', internalPageBuilderCall)
522
+ await this.completeMountProcess(JSON.stringify([]))
369
523
  return
370
524
  }
371
525
 
372
526
  if (!this.isPageBuilderMissingOnStart && !localStorageData && !passedComponentsArray) {
373
- await this.#completeMountProcess(JSON.stringify([]))
527
+ console.log('7777:', internalPageBuilderCall)
528
+ await this.completeMountProcess(JSON.stringify([]))
374
529
  return
375
530
  }
376
531
  }
377
532
 
378
- // FOCUS ON: pendingMountData
379
- if (this.pendingMountData) {
533
+ // FOCUS ON: pendingMountComponents
534
+ if (this.pendingMountComponents) {
535
+ // No Page Builder Is present in DOM initially
380
536
  if (localStorageData && this.isPageBuilderMissingOnStart) {
537
+ console.log('8888:', internalPageBuilderCall)
538
+ await this.completeMountProcess(JSON.stringify(this.pendingMountComponents), true)
539
+ await delay(3000)
381
540
  this.pageBuilderStateStore.setHasLocalDraftForUpdate(true)
382
- await this.#completeMountProcess(JSON.stringify(this.pendingMountData), true)
383
- this.pendingMountData = null
541
+ this.pendingMountComponents = null
384
542
  return
385
543
  }
386
544
  if (!localStorageData && passedComponentsArray && this.isPageBuilderMissingOnStart) {
387
- await this.#completeMountProcess(JSON.stringify(this.pendingMountData), true)
545
+ console.log('9999:', internalPageBuilderCall)
546
+ await this.completeMountProcess(JSON.stringify(this.pendingMountComponents), true)
547
+ this.saveDomComponentsToLocalStorage()
388
548
  return
389
549
  }
390
550
 
391
551
  if (!passedComponentsArray && !localStorageData && this.isPageBuilderMissingOnStart) {
392
- await this.#completeMountProcess(JSON.stringify(this.pendingMountData), true)
552
+ console.log('XXXX:', internalPageBuilderCall)
553
+ await this.completeMountProcess(JSON.stringify(this.pendingMountComponents), true)
554
+ this.saveDomComponentsToLocalStorage()
393
555
  return
394
556
  }
395
557
  }
@@ -397,21 +559,22 @@ export class PageBuilderService {
397
559
  //
398
560
  }
399
561
 
400
- async #completeMountProcess(html: string, usePassedPageSettings?: boolean) {
401
- await this.#mountComponentsToDOM(html, usePassedPageSettings)
402
- // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
403
- await nextTick()
404
- // Attach event listeners to all editable elements in the Builder
405
- await this.#addListenersToEditableElements()
562
+ private async completeMountProcess(html: string, usePassedPageSettings?: boolean) {
563
+ await this.mountComponentsToDOM(html, usePassedPageSettings)
406
564
 
407
565
  // Clean up any old localStorage items related to previous builder sessions
408
566
  this.deleteOldPageBuilderLocalStorage()
409
567
 
410
568
  this.pageBuilderStateStore.setIsRestoring(false)
411
569
  this.pageBuilderStateStore.setIsLoadingGlobal(false)
570
+
571
+ // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
572
+ await nextTick()
573
+ // Attach event listeners to all editable elements in the Builder
574
+ await this.addListenersToEditableElements()
412
575
  }
413
576
 
414
- #applyElementClassChanges(
577
+ private applyElementClassChanges(
415
578
  cssUserSelection: string | undefined,
416
579
  CSSArray: string[],
417
580
  mutationName: string,
@@ -461,7 +624,7 @@ export class PageBuilderService {
461
624
  return currentCSS
462
625
  }
463
626
 
464
- async clearClassesFromPage() {
627
+ public async clearClassesFromPage() {
465
628
  const pagebuilder = document.querySelector('#pagebuilder')
466
629
  if (!pagebuilder) return
467
630
 
@@ -470,7 +633,7 @@ export class PageBuilderService {
470
633
  this.initializeElementStyles()
471
634
  await nextTick()
472
635
  }
473
- async clearInlineStylesFromPagee() {
636
+ public async clearInlineStylesFromPagee() {
474
637
  const pagebuilder = document.querySelector('#pagebuilder')
475
638
  if (!pagebuilder) return
476
639
 
@@ -480,7 +643,7 @@ export class PageBuilderService {
480
643
  await nextTick()
481
644
  }
482
645
 
483
- async globalPageStyles() {
646
+ public async globalPageStyles() {
484
647
  const pagebuilder = document.querySelector('#pagebuilder')
485
648
  if (!pagebuilder) return
486
649
 
@@ -496,19 +659,19 @@ export class PageBuilderService {
496
659
  await nextTick()
497
660
  }
498
661
 
499
- handleFontWeight(userSelectedFontWeight?: string): void {
500
- this.#applyElementClassChanges(
662
+ public handleFontWeight(userSelectedFontWeight?: string): void {
663
+ this.applyElementClassChanges(
501
664
  userSelectedFontWeight,
502
665
  tailwindFontStyles.fontWeight,
503
666
  'setFontWeight',
504
667
  )
505
668
  }
506
669
 
507
- handleFontSizeBase(userSelectedFontSize?: string): void {
508
- this.#applyElementClassChanges(userSelectedFontSize, tailwindFontSizes.fontBase, 'setFontBase')
670
+ public handleFontSizeBase(userSelectedFontSize?: string): void {
671
+ this.applyElementClassChanges(userSelectedFontSize, tailwindFontSizes.fontBase, 'setFontBase')
509
672
  }
510
673
 
511
- handleFontSizeDesktop(userSelectedFontSize?: string): void {
674
+ public handleFontSizeDesktop(userSelectedFontSize?: string): void {
512
675
  const currentHTMLElement = this.getElement.value
513
676
  if (!currentHTMLElement) return
514
677
 
@@ -563,12 +726,8 @@ export class PageBuilderService {
563
726
  }
564
727
  }
565
728
 
566
- #applyHelperCSSToElements(element: HTMLElement): void {
567
- this.#wrapElementInDivIfExcluded(element)
568
-
569
- if (element.tagName === 'IMG') {
570
- element.classList.add('smooth-transition')
571
- }
729
+ private applyHelperCSSToElements(element: HTMLElement): void {
730
+ this.wrapElementInDivIfExcluded(element)
572
731
 
573
732
  // If this is a DIV and its only/main child is a heading, apply font size classes to the DIV
574
733
  if (
@@ -594,20 +753,20 @@ export class PageBuilderService {
594
753
  }
595
754
  }
596
755
 
597
- async toggleTipTapModal(status: boolean): Promise<void> {
756
+ public async toggleTipTapModal(status: boolean): Promise<void> {
598
757
  this.pageBuilderStateStore.setShowModalTipTap(status)
599
758
 
600
759
  // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
601
760
  await nextTick()
602
761
  // Attach event listeners to all editable elements in the Builder
603
- await this.#addListenersToEditableElements()
762
+ await this.addListenersToEditableElements()
604
763
 
605
764
  if (!status) {
606
765
  await this.handleAutoSave()
607
766
  }
608
767
  }
609
768
 
610
- #wrapElementInDivIfExcluded(element: HTMLElement): void {
769
+ private wrapElementInDivIfExcluded(element: HTMLElement): void {
611
770
  if (!element) return
612
771
 
613
772
  if (
@@ -621,7 +780,7 @@ export class PageBuilderService {
621
780
  }
622
781
  }
623
782
 
624
- #handleElementClick = async (e: Event, element: HTMLElement): Promise<void> => {
783
+ private handleElementClick = async (e: Event, element: HTMLElement): Promise<void> => {
625
784
  e.preventDefault()
626
785
  e.stopPropagation()
627
786
 
@@ -645,7 +804,7 @@ export class PageBuilderService {
645
804
  this.pageBuilderStateStore.setElement(element)
646
805
  }
647
806
 
648
- #handleMouseOver = (e: Event, element: HTMLElement): void => {
807
+ private handleMouseOver = (e: Event, element: HTMLElement): void => {
649
808
  e.preventDefault()
650
809
  e.stopPropagation()
651
810
 
@@ -663,7 +822,7 @@ export class PageBuilderService {
663
822
  }
664
823
  }
665
824
 
666
- #handleMouseLeave = (e: Event): void => {
825
+ private handleMouseLeave = (e: Event): void => {
667
826
  e.preventDefault()
668
827
  e.stopPropagation()
669
828
 
@@ -676,7 +835,7 @@ export class PageBuilderService {
676
835
  }
677
836
  }
678
837
 
679
- isEditableElement(el: Element | null): boolean {
838
+ public isEditableElement(el: Element | null): boolean {
680
839
  if (!el) return false
681
840
  return !this.NoneListernesTags.includes(el.tagName)
682
841
  }
@@ -685,7 +844,7 @@ export class PageBuilderService {
685
844
  * The function is used to
686
845
  * attach event listeners to each element within a 'section'
687
846
  */
688
- #addListenersToEditableElements = async () => {
847
+ private addListenersToEditableElements = async () => {
689
848
  const elementsWithListeners = new WeakSet<Element>()
690
849
 
691
850
  const pagebuilder = document.querySelector('#pagebuilder')
@@ -702,9 +861,9 @@ export class PageBuilderService {
702
861
  // Type assertion to HTMLElement since we know these are DOM elements
703
862
  const htmlElement = element as HTMLElement
704
863
  // Attach event listeners directly to individual elements
705
- htmlElement.addEventListener('click', (e) => this.#handleElementClick(e, htmlElement))
706
- htmlElement.addEventListener('mouseover', (e) => this.#handleMouseOver(e, htmlElement))
707
- htmlElement.addEventListener('mouseleave', (e) => this.#handleMouseLeave(e))
864
+ htmlElement.addEventListener('click', (e) => this.handleElementClick(e, htmlElement))
865
+ htmlElement.addEventListener('mouseover', (e) => this.handleMouseOver(e, htmlElement))
866
+ htmlElement.addEventListener('mouseleave', (e) => this.handleMouseLeave(e))
708
867
  }
709
868
  }
710
869
 
@@ -712,7 +871,7 @@ export class PageBuilderService {
712
871
  })
713
872
  }
714
873
 
715
- handleAutoSave = async () => {
874
+ public handleAutoSave = async () => {
716
875
  this.startEditing()
717
876
  const passedConfig = this.pageBuilderStateStore.getPageBuilderConfig
718
877
 
@@ -730,7 +889,7 @@ export class PageBuilderService {
730
889
  this.pageBuilderStateStore.setIsSaving(true)
731
890
  // Deselect any selected or hovered elements in the builder UI
732
891
  //
733
- this.#saveDomComponentsToLocalStorage()
892
+ this.saveDomComponentsToLocalStorage()
734
893
  await delay(400)
735
894
  } catch (err) {
736
895
  console.error('Error trying auto save.', err)
@@ -742,7 +901,7 @@ export class PageBuilderService {
742
901
  if (passedConfig && !passedConfig.userSettings) {
743
902
  try {
744
903
  this.pageBuilderStateStore.setIsSaving(true)
745
- this.#saveDomComponentsToLocalStorage()
904
+ this.saveDomComponentsToLocalStorage()
746
905
  await delay(400)
747
906
  } catch (err) {
748
907
  console.error('Error trying saving.', err)
@@ -752,43 +911,21 @@ export class PageBuilderService {
752
911
  }
753
912
  }
754
913
 
755
- handleManualSave = async () => {
914
+ public handleManualSave = async () => {
756
915
  this.startEditing()
757
- const passedConfig = this.pageBuilderStateStore.getPageBuilderConfig
758
-
759
- // Check if config is set
760
- if (passedConfig && passedConfig.userSettings) {
761
- //
762
- //
763
- // Enabled auto save
764
- if (
765
- (typeof passedConfig.userSettings.autoSave === 'boolean' &&
766
- !passedConfig.userSettings.autoSave) ||
767
- (typeof passedConfig.userSettings.autoSave === 'boolean' &&
768
- passedConfig.userSettings.autoSave)
769
- ) {
770
- this.pageBuilderStateStore.setIsSaving(true)
771
- this.#saveDomComponentsToLocalStorage()
772
- await delay(400)
773
916
 
774
- this.pageBuilderStateStore.setIsSaving(false)
775
- }
776
- }
777
- if (passedConfig && !passedConfig.userSettings) {
778
- this.pageBuilderStateStore.setIsSaving(true)
779
- this.#saveDomComponentsToLocalStorage()
780
- await delay(400)
781
-
782
- this.pageBuilderStateStore.setIsSaving(false)
783
- }
917
+ this.pageBuilderStateStore.setIsSaving(true)
918
+ this.saveDomComponentsToLocalStorage()
919
+ await delay(400)
920
+ this.pageBuilderStateStore.setIsSaving(false)
784
921
  }
785
922
 
786
- cloneCompObjForDOMInsertion(componentObject: ComponentObject): ComponentObject {
923
+ public cloneCompObjForDOMInsertion(componentObject: ComponentObject): ComponentObject {
787
924
  // Deep clone clone component
788
925
  const clonedComponent = { ...componentObject }
789
926
 
790
- const pageBuilder = document.querySelector('#contains-pagebuilder')
791
- // scoll to top or bottom # end
927
+ const pageBuilder = document.querySelector('#pagebuilder')
928
+ // scoll to top or bottom
792
929
  if (pageBuilder) {
793
930
  // push to top
794
931
  if (this.getComponentArrayAddMethod.value === 'unshift') {
@@ -809,7 +946,7 @@ export class PageBuilderService {
809
946
  const elements = doc.querySelectorAll('*')
810
947
 
811
948
  elements.forEach((element) => {
812
- this.#applyHelperCSSToElements(element as HTMLElement)
949
+ this.applyHelperCSSToElements(element as HTMLElement)
813
950
  })
814
951
 
815
952
  // Add the component id to the section element
@@ -819,7 +956,7 @@ export class PageBuilderService {
819
956
  section.querySelectorAll('[class]').forEach((el) => {
820
957
  el.setAttribute(
821
958
  'class',
822
- this.#addTailwindPrefixToClasses(el.getAttribute('class') || '', 'pbx-'),
959
+ this.addTailwindPrefixToClasses(el.getAttribute('class') || '', 'pbx-'),
823
960
  )
824
961
  })
825
962
 
@@ -843,7 +980,7 @@ export class PageBuilderService {
843
980
  return clonedComponent
844
981
  }
845
982
 
846
- async #removeHoveredAndSelected() {
983
+ private async removeHoveredAndSelected() {
847
984
  const pagebuilder = document.querySelector('#pagebuilder')
848
985
  if (!pagebuilder) return
849
986
 
@@ -859,7 +996,7 @@ export class PageBuilderService {
859
996
  }
860
997
  }
861
998
 
862
- async #syncCurrentClasses() {
999
+ private async syncCurrentClasses() {
863
1000
  // convert classList to array
864
1001
  const classListArray = Array.from(this.getElement.value?.classList || [])
865
1002
 
@@ -867,7 +1004,17 @@ export class PageBuilderService {
867
1004
  this.pageBuilderStateStore.setCurrentClasses(classListArray)
868
1005
  }
869
1006
 
870
- handleAddClasses(userSelectedClass: string): void {
1007
+ private async syncCurrentStyles() {
1008
+ const style = this.getElement.value?.getAttribute('style')
1009
+ if (style) {
1010
+ const stylesObject = this.parseStyleString(style)
1011
+ this.pageBuilderStateStore.setCurrentStyles(stylesObject)
1012
+ } else {
1013
+ this.pageBuilderStateStore.setCurrentStyles({})
1014
+ }
1015
+ }
1016
+
1017
+ public handleAddClasses(userSelectedClass: string): void {
871
1018
  if (
872
1019
  typeof userSelectedClass === 'string' &&
873
1020
  userSelectedClass.trim() !== '' &&
@@ -886,66 +1033,83 @@ export class PageBuilderService {
886
1033
  this.pageBuilderStateStore.setClass(prefixedClass)
887
1034
  }
888
1035
  }
889
- handleFontFamily(userSelectedFontFamily?: string): void {
890
- this.#applyElementClassChanges(
1036
+
1037
+ public handleAddStyle(property: string, value: string): void {
1038
+ const element = this.getElement.value
1039
+ if (!element || !property || !value) return
1040
+
1041
+ element.style.setProperty(property, value)
1042
+ this.pageBuilderStateStore.setElement(element)
1043
+ }
1044
+
1045
+ public handleRemoveStyle(property: string): void {
1046
+ const element = this.getElement.value
1047
+ if (!element || !property) return
1048
+
1049
+ element.style.removeProperty(property)
1050
+ this.pageBuilderStateStore.setElement(element)
1051
+ }
1052
+
1053
+ public handleFontFamily(userSelectedFontFamily?: string): void {
1054
+ this.applyElementClassChanges(
891
1055
  userSelectedFontFamily,
892
1056
  tailwindFontStyles.fontFamily,
893
1057
  'setFontFamily',
894
1058
  )
895
1059
  }
896
- handleFontStyle(userSelectedFontStyle?: string): void {
897
- this.#applyElementClassChanges(
1060
+ public handleFontStyle(userSelectedFontStyle?: string): void {
1061
+ this.applyElementClassChanges(
898
1062
  userSelectedFontStyle,
899
1063
  tailwindFontStyles.fontStyle,
900
1064
  'setFontStyle',
901
1065
  )
902
1066
  }
903
- handleVerticalPadding(userSelectedVerticalPadding?: string): void {
904
- this.#applyElementClassChanges(
1067
+ public handleVerticalPadding(userSelectedVerticalPadding?: string): void {
1068
+ this.applyElementClassChanges(
905
1069
  userSelectedVerticalPadding,
906
1070
  tailwindPaddingAndMargin.verticalPadding,
907
1071
  'setFontVerticalPadding',
908
1072
  )
909
1073
  }
910
- handleHorizontalPadding(userSelectedHorizontalPadding?: string): void {
911
- this.#applyElementClassChanges(
1074
+ public handleHorizontalPadding(userSelectedHorizontalPadding?: string): void {
1075
+ this.applyElementClassChanges(
912
1076
  userSelectedHorizontalPadding,
913
1077
  tailwindPaddingAndMargin.horizontalPadding,
914
1078
  'setFontHorizontalPadding',
915
1079
  )
916
1080
  }
917
1081
 
918
- handleVerticalMargin(userSelectedVerticalMargin?: string): void {
919
- this.#applyElementClassChanges(
1082
+ public handleVerticalMargin(userSelectedVerticalMargin?: string): void {
1083
+ this.applyElementClassChanges(
920
1084
  userSelectedVerticalMargin,
921
1085
  tailwindPaddingAndMargin.verticalMargin,
922
1086
  'setFontVerticalMargin',
923
1087
  )
924
1088
  }
925
- handleHorizontalMargin(userSelectedHorizontalMargin?: string): void {
926
- this.#applyElementClassChanges(
1089
+ public handleHorizontalMargin(userSelectedHorizontalMargin?: string): void {
1090
+ this.applyElementClassChanges(
927
1091
  userSelectedHorizontalMargin,
928
1092
  tailwindPaddingAndMargin.horizontalMargin,
929
1093
  'setFontHorizontalMargin',
930
1094
  )
931
1095
  }
932
1096
 
933
- handleBorderStyle(borderStyle?: string): void {
934
- this.#applyElementClassChanges(
1097
+ public handleBorderStyle(borderStyle?: string): void {
1098
+ this.applyElementClassChanges(
935
1099
  borderStyle,
936
1100
  tailwindBorderStyleWidthPlusColor.borderStyle,
937
1101
  'setBorderStyle',
938
1102
  )
939
1103
  }
940
- handleBorderWidth(borderWidth?: string): void {
941
- this.#applyElementClassChanges(
1104
+ public handleBorderWidth(borderWidth?: string): void {
1105
+ this.applyElementClassChanges(
942
1106
  borderWidth,
943
1107
  tailwindBorderStyleWidthPlusColor.borderWidth,
944
1108
  'setBorderWidth',
945
1109
  )
946
1110
  }
947
- handleBorderColor(borderColor?: string): void {
948
- this.#applyElementClassChanges(
1111
+ public handleBorderColor(borderColor?: string): void {
1112
+ this.applyElementClassChanges(
949
1113
  borderColor,
950
1114
  tailwindBorderStyleWidthPlusColor.borderColor,
951
1115
  'setBorderColor',
@@ -953,48 +1117,48 @@ export class PageBuilderService {
953
1117
  }
954
1118
  // border color, style & width / end
955
1119
 
956
- handleBackgroundColor(color?: string): void {
957
- this.#applyElementClassChanges(
1120
+ public handleBackgroundColor(color?: string): void {
1121
+ this.applyElementClassChanges(
958
1122
  color,
959
1123
  tailwindColors.backgroundColorVariables,
960
1124
  'setBackgroundColor',
961
1125
  )
962
1126
  }
963
1127
 
964
- handleTextColor(color?: string): void {
965
- this.#applyElementClassChanges(color, tailwindColors.textColorVariables, 'setTextColor')
1128
+ public handleTextColor(color?: string): void {
1129
+ this.applyElementClassChanges(color, tailwindColors.textColorVariables, 'setTextColor')
966
1130
  }
967
1131
 
968
1132
  handleBorderRadiusGlobal(borderRadiusGlobal?: string): void {
969
- this.#applyElementClassChanges(
1133
+ this.applyElementClassChanges(
970
1134
  borderRadiusGlobal,
971
1135
  tailwindBorderRadius.roundedGlobal,
972
1136
  'setBorderRadiusGlobal',
973
1137
  )
974
1138
  }
975
1139
  handleBorderRadiusTopLeft(borderRadiusTopLeft?: string): void {
976
- this.#applyElementClassChanges(
1140
+ this.applyElementClassChanges(
977
1141
  borderRadiusTopLeft,
978
1142
  tailwindBorderRadius.roundedTopLeft,
979
1143
  'setBorderRadiusTopLeft',
980
1144
  )
981
1145
  }
982
1146
  handleBorderRadiusTopRight(borderRadiusTopRight?: string): void {
983
- this.#applyElementClassChanges(
1147
+ this.applyElementClassChanges(
984
1148
  borderRadiusTopRight,
985
1149
  tailwindBorderRadius.roundedTopRight,
986
1150
  'setBorderRadiusTopRight',
987
1151
  )
988
1152
  }
989
1153
  handleBorderRadiusBottomleft(borderRadiusBottomleft?: string): void {
990
- this.#applyElementClassChanges(
1154
+ this.applyElementClassChanges(
991
1155
  borderRadiusBottomleft,
992
1156
  tailwindBorderRadius.roundedBottomLeft,
993
1157
  'setBorderRadiusBottomleft',
994
1158
  )
995
1159
  }
996
1160
  handleBorderRadiusBottomRight(borderRadiusBottomRight?: string): void {
997
- this.#applyElementClassChanges(
1161
+ this.applyElementClassChanges(
998
1162
  borderRadiusBottomRight,
999
1163
  tailwindBorderRadius.roundedBottomRight,
1000
1164
  'setBorderRadiusBottomRight',
@@ -1003,14 +1167,14 @@ export class PageBuilderService {
1003
1167
  // border radius / end
1004
1168
 
1005
1169
  handleFontSizeTablet(userSelectedFontSize?: string): void {
1006
- this.#applyElementClassChanges(
1170
+ this.applyElementClassChanges(
1007
1171
  userSelectedFontSize,
1008
1172
  tailwindFontSizes.fontTablet,
1009
1173
  'setFontTablet',
1010
1174
  )
1011
1175
  }
1012
1176
  handleFontSizeMobile(userSelectedFontSize?: string): void {
1013
- this.#applyElementClassChanges(
1177
+ this.applyElementClassChanges(
1014
1178
  userSelectedFontSize,
1015
1179
  tailwindFontSizes.fontMobile,
1016
1180
  'setFontMobile',
@@ -1018,14 +1182,14 @@ export class PageBuilderService {
1018
1182
  }
1019
1183
 
1020
1184
  handleBackgroundOpacity(opacity?: string): void {
1021
- this.#applyElementClassChanges(
1185
+ this.applyElementClassChanges(
1022
1186
  opacity,
1023
1187
  tailwindOpacities.backgroundOpacities,
1024
1188
  'setBackgroundOpacity',
1025
1189
  )
1026
1190
  }
1027
1191
  handleOpacity(opacity?: string): void {
1028
- this.#applyElementClassChanges(opacity, tailwindOpacities.opacities, 'setOpacity')
1192
+ this.applyElementClassChanges(opacity, tailwindOpacities.opacities, 'setOpacity')
1029
1193
  }
1030
1194
 
1031
1195
  /**
@@ -1038,7 +1202,7 @@ export class PageBuilderService {
1038
1202
  *
1039
1203
  */
1040
1204
 
1041
- deleteAllComponentsFromDOM() {
1205
+ private deleteAllComponentsFromDOM() {
1042
1206
  // Clear the store
1043
1207
  this.pageBuilderStateStore.setComponents([])
1044
1208
 
@@ -1052,8 +1216,8 @@ export class PageBuilderService {
1052
1216
  }
1053
1217
  }
1054
1218
 
1055
- async deleteComponentFromDOM() {
1056
- this.#syncDomToStoreOnly()
1219
+ public async deleteComponentFromDOM() {
1220
+ this.syncDomToStoreOnly()
1057
1221
  await nextTick()
1058
1222
 
1059
1223
  const components = this.getComponents.value
@@ -1089,7 +1253,7 @@ export class PageBuilderService {
1089
1253
 
1090
1254
  // Wait for Vue to finish DOM updates before attaching event listeners.
1091
1255
  await nextTick()
1092
- await this.#addListenersToEditableElements()
1256
+ await this.addListenersToEditableElements()
1093
1257
 
1094
1258
  this.pageBuilderStateStore.setComponent(null)
1095
1259
  this.pageBuilderStateStore.setElement(null)
@@ -1098,7 +1262,7 @@ export class PageBuilderService {
1098
1262
  await this.handleAutoSave()
1099
1263
  }
1100
1264
 
1101
- async deleteElementFromDOM() {
1265
+ public async deleteElementFromDOM() {
1102
1266
  const element = this.getElement.value
1103
1267
  if (!element) return
1104
1268
 
@@ -1129,10 +1293,10 @@ export class PageBuilderService {
1129
1293
  // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
1130
1294
  await nextTick()
1131
1295
  // Attach event listeners to all editable elements in the Builder
1132
- await this.#addListenersToEditableElements()
1296
+ await this.addListenersToEditableElements()
1133
1297
  }
1134
1298
 
1135
- async restoreDeletedElementToDOM() {
1299
+ public async restoreDeletedElementToDOM() {
1136
1300
  // Restore the previously deleted element to the DOM
1137
1301
  const restoredHTML = this.getRestoredElement.value
1138
1302
  const parent = this.getParentElement.value
@@ -1158,10 +1322,10 @@ export class PageBuilderService {
1158
1322
 
1159
1323
  // Wait for Vue to finish DOM updates before attaching event listeners
1160
1324
  await nextTick()
1161
- await this.#addListenersToEditableElements()
1325
+ await this.addListenersToEditableElements()
1162
1326
  }
1163
1327
 
1164
- handleRemoveClasses(userSelectedClass: string): void {
1328
+ public handleRemoveClasses(userSelectedClass: string): void {
1165
1329
  // remove selected class from element
1166
1330
  if (this.getElement.value?.classList.contains(userSelectedClass)) {
1167
1331
  this.getElement.value?.classList.remove(userSelectedClass)
@@ -1173,7 +1337,7 @@ export class PageBuilderService {
1173
1337
 
1174
1338
  // move component
1175
1339
  // runs when html components are rearranged
1176
- reorderComponent(direction: number): void {
1340
+ public reorderComponent(direction: number): void {
1177
1341
  if (!this.getComponents.value || !this.getComponent.value) return
1178
1342
 
1179
1343
  if (this.getComponents.value.length <= 1) return
@@ -1203,7 +1367,7 @@ export class PageBuilderService {
1203
1367
  this.getComponents.value.splice(newIndex, 0, componentToMove)
1204
1368
  }
1205
1369
 
1206
- ensureTextAreaHasContent = () => {
1370
+ public ensureTextAreaHasContent = () => {
1207
1371
  if (!this.getElement.value) return
1208
1372
 
1209
1373
  // text content
@@ -1227,7 +1391,7 @@ export class PageBuilderService {
1227
1391
  }
1228
1392
  }
1229
1393
 
1230
- handleTextInput = async (textContentVueModel: string): Promise<void> => {
1394
+ public handleTextInput = async (textContentVueModel: string): Promise<void> => {
1231
1395
  if (typeof this.getElement.value?.innerHTML !== 'string') {
1232
1396
  return
1233
1397
  }
@@ -1248,7 +1412,7 @@ export class PageBuilderService {
1248
1412
 
1249
1413
  //
1250
1414
  //
1251
- ElOrFirstChildIsIframe() {
1415
+ public ElOrFirstChildIsIframe() {
1252
1416
  if (
1253
1417
  this.getElement.value?.tagName === 'IFRAME' ||
1254
1418
  this.getElement.value?.firstElementChild?.tagName === 'IFRAME'
@@ -1261,7 +1425,7 @@ export class PageBuilderService {
1261
1425
  //
1262
1426
  //
1263
1427
  //
1264
- isSelectedElementValidText() {
1428
+ public isSelectedElementValidText() {
1265
1429
  let reachedElseStatement = false
1266
1430
 
1267
1431
  // Get all child elements of the parentDiv
@@ -1287,7 +1451,7 @@ export class PageBuilderService {
1287
1451
  return reachedElseStatement
1288
1452
  }
1289
1453
 
1290
- previewCurrentDesign() {
1454
+ public previewCurrentDesign() {
1291
1455
  this.pageBuilderStateStore.setElement(null)
1292
1456
 
1293
1457
  const pagebuilder = document.querySelector('#pagebuilder')
@@ -1324,7 +1488,7 @@ export class PageBuilderService {
1324
1488
  * removed from itself and all descendants. Does NOT mutate the live DOM.
1325
1489
  * @param element The HTMLElement to clone and sanitize
1326
1490
  */
1327
- #cloneAndRemoveSelectionAttributes(element: HTMLElement): HTMLElement {
1491
+ private cloneAndRemoveSelectionAttributes(element: HTMLElement): HTMLElement {
1328
1492
  // Deep clone the element
1329
1493
  const clone = element.cloneNode(true) as HTMLElement
1330
1494
 
@@ -1342,14 +1506,14 @@ export class PageBuilderService {
1342
1506
  * Syncs the current DOM state into the in-memory store (getComponents),
1343
1507
  * but does NOT save to localStorage.
1344
1508
  */
1345
- #syncDomToStoreOnly() {
1509
+ private syncDomToStoreOnly() {
1346
1510
  const pagebuilder = document.querySelector('#pagebuilder')
1347
1511
  if (!pagebuilder) return
1348
1512
 
1349
1513
  const componentsToSave: { html_code: string; id: string | null; title: string }[] = []
1350
1514
 
1351
1515
  pagebuilder.querySelectorAll('section[data-componentid]').forEach((section) => {
1352
- const sanitizedSection = this.#cloneAndRemoveSelectionAttributes(section as HTMLElement)
1516
+ const sanitizedSection = this.cloneAndRemoveSelectionAttributes(section as HTMLElement)
1353
1517
  componentsToSave.push({
1354
1518
  html_code: sanitizedSection.outerHTML,
1355
1519
  id: sanitizedSection.getAttribute('data-componentid'),
@@ -1363,8 +1527,8 @@ export class PageBuilderService {
1363
1527
  /**
1364
1528
  * Saves the current DOM state (components) to localStorage.
1365
1529
  */
1366
- #saveDomComponentsToLocalStorage() {
1367
- this.localStorageManager.updateLocalStorageItemName()
1530
+ private saveDomComponentsToLocalStorage() {
1531
+ this.updateLocalStorageItemName()
1368
1532
  const pagebuilder = document.querySelector('#pagebuilder')
1369
1533
  if (!pagebuilder) return
1370
1534
 
@@ -1373,14 +1537,13 @@ export class PageBuilderService {
1373
1537
  hoveredElement.removeAttribute('hovered')
1374
1538
  }
1375
1539
 
1376
- const componentsToSave: { html_code: string; id: string | null; title: string }[] = []
1540
+ const componentsToSave: { html_code: string; title: string }[] = []
1377
1541
 
1378
1542
  pagebuilder.querySelectorAll('section[data-componentid]').forEach((section) => {
1379
- const sanitizedSection = this.#cloneAndRemoveSelectionAttributes(section as HTMLElement)
1543
+ const sanitizedSection = this.cloneAndRemoveSelectionAttributes(section as HTMLElement)
1380
1544
 
1381
1545
  componentsToSave.push({
1382
1546
  html_code: sanitizedSection.outerHTML,
1383
- id: sanitizedSection.getAttribute('data-componentid'),
1384
1547
  title: sanitizedSection.getAttribute('data-component-title') || 'Untitled Component',
1385
1548
  })
1386
1549
  })
@@ -1402,8 +1565,8 @@ export class PageBuilderService {
1402
1565
  localStorage.setItem(keyForSavingFromDomToLocal, JSON.stringify(dataToSave))
1403
1566
  }
1404
1567
  }
1405
- async removeCurrentComponentsFromLocalStorage() {
1406
- this.localStorageManager.updateLocalStorageItemName()
1568
+ private async removeCurrentComponentsFromLocalStorage() {
1569
+ this.updateLocalStorageItemName()
1407
1570
  await nextTick()
1408
1571
 
1409
1572
  const key = this.getLocalStorageItemName.value
@@ -1412,7 +1575,26 @@ export class PageBuilderService {
1412
1575
  }
1413
1576
  }
1414
1577
 
1415
- //
1578
+ public async handleFormSubmission() {
1579
+ await this.removeCurrentComponentsFromLocalStorage()
1580
+ this.deleteAllComponentsFromDOM()
1581
+ }
1582
+
1583
+ private parseStyleString(style: string): Record<string, string> {
1584
+ return style
1585
+ .split(';')
1586
+ .map((s) => s.trim())
1587
+ .filter(Boolean)
1588
+ .reduce(
1589
+ (acc, rule) => {
1590
+ const [key, value] = rule.split(':').map((str) => str.trim())
1591
+ if (key && value) acc[key] = value
1592
+ return acc
1593
+ },
1594
+ {} as Record<string, string>,
1595
+ )
1596
+ }
1597
+
1416
1598
  deleteOldPageBuilderLocalStorage(): void {
1417
1599
  const config = this.pageBuilderStateStore.getPageBuilderConfig
1418
1600
  const formType = config && config.updateOrCreate && config.updateOrCreate.formType
@@ -1466,33 +1648,33 @@ export class PageBuilderService {
1466
1648
  }
1467
1649
 
1468
1650
  // Call this when the user starts editing (e.g., on first change or when resuming a draft)
1469
- startEditing() {
1651
+ public startEditing() {
1470
1652
  this.hasStartedEditing = true
1471
1653
  }
1472
1654
 
1473
1655
  //
1474
- async resumeEditingFromDraft() {
1475
- this.localStorageManager.updateLocalStorageItemName()
1656
+ public async resumeEditingFromDraft() {
1657
+ this.updateLocalStorageItemName()
1476
1658
 
1477
- const localStorageData = this.loadStoredComponentsFromStorage()
1659
+ const localStorageData = this.getSavedPageHtml()
1478
1660
 
1479
1661
  if (localStorageData) {
1480
- this.pageBuilderStateStore.setIsLoadingResumeEditing(true)
1481
1662
  await delay(400)
1482
- await this.#mountComponentsToDOM(localStorageData)
1663
+ this.pageBuilderStateStore.setIsLoadingResumeEditing(true)
1664
+ await this.mountComponentsToDOM(localStorageData)
1483
1665
  this.pageBuilderStateStore.setIsLoadingResumeEditing(false)
1484
1666
  }
1485
1667
 
1486
1668
  // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
1487
1669
  await nextTick()
1488
1670
  // Attach event listeners to all editable elements in the Builder
1489
- await this.#addListenersToEditableElements()
1671
+ await this.addListenersToEditableElements()
1490
1672
  // set loading to false
1491
1673
  this.pageBuilderStateStore.setIsLoadingResumeEditing(false)
1492
1674
  }
1493
1675
 
1494
- async restoreOriginalContent() {
1495
- this.localStorageManager.updateLocalStorageItemName()
1676
+ public async restoreOriginalContent() {
1677
+ this.updateLocalStorageItemName()
1496
1678
 
1497
1679
  this.pageBuilderStateStore.setIsRestoring(true)
1498
1680
  await delay(400)
@@ -1501,56 +1683,52 @@ export class PageBuilderService {
1501
1683
  if (Array.isArray(this.originalComponents)) {
1502
1684
  await this.clearClassesFromPage()
1503
1685
  await this.clearInlineStylesFromPagee()
1504
- await this.#mountComponentsToDOM(JSON.stringify(this.originalComponents), true)
1686
+ await this.mountComponentsToDOM(JSON.stringify(this.originalComponents), true)
1505
1687
  this.removeCurrentComponentsFromLocalStorage()
1506
1688
  }
1507
1689
 
1508
1690
  // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
1509
1691
  await nextTick()
1510
1692
  // Attach event listeners to all editable elements in the Builder
1511
- await this.#addListenersToEditableElements()
1693
+ await this.addListenersToEditableElements()
1512
1694
 
1513
1695
  this.pageBuilderStateStore.setIsRestoring(false)
1514
1696
  }
1515
1697
 
1516
- getStorageItemNameForResource(): string | null {
1698
+ public getStorageItemNameForResource(): string | null {
1517
1699
  return this.getLocalStorageItemName.value
1518
1700
  }
1519
1701
 
1520
- loadStoredComponentsFromStorage() {
1521
- this.localStorageManager.updateLocalStorageItemName()
1702
+ public getSavedPageHtml() {
1522
1703
  if (!this.getLocalStorageItemName.value) return false
1523
1704
 
1524
- if (
1525
- this.getLocalStorageItemName.value &&
1526
- localStorage.getItem(this.getLocalStorageItemName.value)
1527
- ) {
1528
- const savedCurrentDesign = localStorage.getItem(this.getLocalStorageItemName.value)
1705
+ const key = this.getLocalStorageItemName.value
1706
+ if (!key) return false
1529
1707
 
1530
- if (savedCurrentDesign) {
1531
- return savedCurrentDesign
1532
- }
1533
- }
1708
+ const raw = localStorage.getItem(key)
1709
+ if (!raw) return false
1534
1710
 
1535
- return false
1536
- }
1711
+ const parsed = JSON.parse(raw)
1537
1712
 
1538
- /**
1539
- * Sets the image selected from the media library as the "pending" image
1540
- * to be applied to the currently selected element in the builder.
1541
- * This does not update the DOM immediately—call `applyPendingImageToSelectedElement` to commit.
1542
- * @param image - The image object to be staged for application
1543
- */
1544
- stageImageForSelectedElement(image: ImageObject) {
1545
- this.pageBuilderStateStore.setApplyImageToSelection(image)
1713
+ // Object with components and pageSettings
1714
+ if (parsed && Array.isArray(parsed.components)) {
1715
+ const classes = (parsed.pageSettings && parsed.pageSettings.classes) || ''
1716
+ const style = (parsed.pageSettings && parsed.pageSettings.style) || ''
1717
+
1718
+ const sectionsHtml = parsed.components.map((c: ComponentObject) => c.html_code).join('\n')
1719
+ return `<div id="pagebuilder" class="${classes}" style="${style}">\n${sectionsHtml}\n</div>`
1720
+ }
1721
+ return false
1546
1722
  }
1547
1723
 
1548
1724
  /**
1549
- * Applies the staged image (set by `stageImageForSelectedElement`) to the currently selected element.
1725
+ * Applies the staged image to the currently selected element.
1550
1726
  * This updates the builder state and triggers an auto-save.
1551
1727
  * If no element is selected or no image is staged, nothing happens.
1552
1728
  */
1553
- async applyPendingImageToSelectedElement(): Promise<void> {
1729
+ public async applySelectedImage(image: ImageObject): Promise<void> {
1730
+ this.pageBuilderStateStore.setApplyImageToSelection(image)
1731
+
1554
1732
  if (!this.getElement.value) return
1555
1733
 
1556
1734
  // Only apply if an image is staged
@@ -1567,7 +1745,7 @@ export class PageBuilderService {
1567
1745
  * sets that image's src as the base primary image in the builder state.
1568
1746
  * If the element does not meet these criteria, clears the base primary image.
1569
1747
  */
1570
- setBasePrimaryImageFromSelectedElement() {
1748
+ private setBasePrimaryImageFromSelectedElement() {
1571
1749
  if (!this.getElement.value) return
1572
1750
 
1573
1751
  const currentImageContainer = document.createElement('div')
@@ -1587,7 +1765,7 @@ export class PageBuilderService {
1587
1765
  this.pageBuilderStateStore.setBasePrimaryImage(null)
1588
1766
  }
1589
1767
 
1590
- #addHyperlinkToElement(
1768
+ private addHyperlinkToElement(
1591
1769
  hyperlinkEnable: boolean,
1592
1770
  urlInput: string | null,
1593
1771
  openHyperlinkInNewTab: boolean,
@@ -1683,7 +1861,7 @@ export class PageBuilderService {
1683
1861
  }
1684
1862
  }
1685
1863
 
1686
- #checkForHyperlink() {
1864
+ private checkForHyperlink() {
1687
1865
  if (!this.getElement.value) return
1688
1866
 
1689
1867
  const hyperlink = this.getElement.value.querySelector('a')
@@ -1711,7 +1889,7 @@ export class PageBuilderService {
1711
1889
  this.pageBuilderStateStore.setHyberlinkEnable(false)
1712
1890
  }
1713
1891
 
1714
- handleHyperlink(
1892
+ public handleHyperlink(
1715
1893
  hyperlinkEnable?: boolean,
1716
1894
  urlInput?: string | null,
1717
1895
  openHyperlinkInNewTab?: boolean,
@@ -1749,15 +1927,15 @@ export class PageBuilderService {
1749
1927
  }
1750
1928
 
1751
1929
  if (hyperlinkEnable === undefined) {
1752
- this.#checkForHyperlink()
1930
+ this.checkForHyperlink()
1753
1931
  return
1754
1932
  }
1755
1933
 
1756
- this.#addHyperlinkToElement(hyperlinkEnable, urlInput || null, openHyperlinkInNewTab || false)
1934
+ this.addHyperlinkToElement(hyperlinkEnable, urlInput || null, openHyperlinkInNewTab || false)
1757
1935
  }
1758
1936
 
1759
1937
  // Helper method for custom components to easily add components
1760
- async addComponent(componentObject: ComponentObject): Promise<void> {
1938
+ public async addComponent(componentObject: ComponentObject): Promise<void> {
1761
1939
  try {
1762
1940
  const clonedComponent = this.cloneCompObjForDOMInsertion({
1763
1941
  html_code: componentObject.html_code,
@@ -1772,8 +1950,8 @@ export class PageBuilderService {
1772
1950
  : 'push',
1773
1951
  })
1774
1952
 
1775
- const pageBuilder = document.querySelector('#contains-pagebuilder')
1776
- // scoll to top or bottom # end
1953
+ const pageBuilder = document.querySelector('#pagebuilder')
1954
+ // scoll to top or bottom
1777
1955
  if (pageBuilder) {
1778
1956
  // push to bottom
1779
1957
  if (this.getComponentArrayAddMethod.value === 'push') {
@@ -1787,7 +1965,7 @@ export class PageBuilderService {
1787
1965
  // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
1788
1966
  await nextTick()
1789
1967
  // Attach event listeners to all editable elements in the Builder
1790
- await this.#addListenersToEditableElements()
1968
+ await this.addListenersToEditableElements()
1791
1969
 
1792
1970
  await this.handleAutoSave()
1793
1971
  } catch (error) {
@@ -1800,7 +1978,7 @@ export class PageBuilderService {
1800
1978
  * process each element’s class attribute and update the classes accordingly.
1801
1979
  */
1802
1980
 
1803
- #addTailwindPrefixToClasses(classList: string, prefix = 'pbx-'): string {
1981
+ private addTailwindPrefixToClasses(classList: string, prefix = 'pbx-'): string {
1804
1982
  return classList
1805
1983
  .split(/\s+/)
1806
1984
  .map((cls) => {
@@ -1814,7 +1992,7 @@ export class PageBuilderService {
1814
1992
  .join(' ')
1815
1993
  }
1816
1994
 
1817
- #convertStyleObjectToString(
1995
+ private convertStyleObjectToString(
1818
1996
  styleObj: string | Record<string, string> | null | undefined,
1819
1997
  ): string {
1820
1998
  if (!styleObj) return ''
@@ -1828,6 +2006,114 @@ export class PageBuilderService {
1828
2006
  .join(' ')
1829
2007
  }
1830
2008
 
2009
+ /**
2010
+ * Parses a string of HTML and extracts builder components and global page settings.
2011
+ *
2012
+ * ⚠️ **Important:**
2013
+ * - This method expects an **HTML string** containing one or more `<section>...</section>` elements (such as the output from `getSavedPageHtml()` or a previously saved builder HTML string).
2014
+ * - **Do NOT pass a JSON string** (such as the result of `JSON.stringify(componentsArray)`) to this method. Passing a JSON string to `DOMParser.parseFromString(..., 'text/html')` will not produce valid DOM nodes. Instead, it will treat the JSON as plain text, resulting in a `<html><head></head><body>{...json...}</body></html>` structure, not real HTML elements.
2015
+ * - If you pass a JSON string, you will see lots of `\n` and strange HTML, because the parser is just wrapping your JSON in a `<body>` tag as text.
2016
+ *
2017
+ * Why only HTML?
2018
+ * - It enforces a single source of truth for builder state (HTML).
2019
+ * - It prevents misuse (e.g., passing JSON to a DOM parser, which is always a bug).
2020
+ * - It makes your documentation and support much simpler.
2021
+ *
2022
+ * @param htmlString - The HTML string to parse (must contain `<section>...</section>` elements, not JSON).
2023
+ * @returns An object with `components` (array of builder components) and `pageSettings` (global styles for the page).
2024
+ */
2025
+ public parsePageBuilderHTML(htmlString: string): {
2026
+ components: ComponentObject[]
2027
+ pageSettings: PageSettings
2028
+ } {
2029
+ const parser = new DOMParser()
2030
+ const doc = parser.parseFromString(htmlString, 'text/html')
2031
+
2032
+ // Prefix all classes in the document
2033
+ doc.querySelectorAll('[class]').forEach((element) => {
2034
+ const currentClasses = element.getAttribute('class') || ''
2035
+ const prefixedClasses = this.addTailwindPrefixToClasses(currentClasses)
2036
+ element.setAttribute('class', prefixedClasses)
2037
+ })
2038
+
2039
+ const pagebuilderDiv = doc.querySelector('#pagebuilder')
2040
+ let pageSettings: PageSettings = {
2041
+ classes: '',
2042
+ style: {},
2043
+ }
2044
+
2045
+ if (pagebuilderDiv) {
2046
+ const rawStyle = pagebuilderDiv.getAttribute('style') || ''
2047
+ pageSettings = {
2048
+ classes: pagebuilderDiv.className || '',
2049
+ style: this.parseStyleString(rawStyle),
2050
+ }
2051
+ }
2052
+
2053
+ // Always assign sectionNodes before use
2054
+ let sectionNodes: NodeListOf<HTMLElement> = doc.querySelectorAll('section')
2055
+ if (pagebuilderDiv) {
2056
+ sectionNodes = pagebuilderDiv.querySelectorAll('section')
2057
+ }
2058
+
2059
+ // Only use top-level (non-nested) sections as components
2060
+ const topLevelSections = Array.from(sectionNodes).filter(
2061
+ (section) =>
2062
+ !section.parentElement || section.parentElement.tagName.toLowerCase() !== 'section',
2063
+ )
2064
+
2065
+ let components: ComponentObject[] = []
2066
+
2067
+ if (topLevelSections.length > 0) {
2068
+ components = topLevelSections.map((section) => ({
2069
+ id: null,
2070
+ html_code: section.outerHTML.trim(),
2071
+ title:
2072
+ section.getAttribute('data-component-title') ||
2073
+ section.getAttribute('title') ||
2074
+ 'Untitled Component',
2075
+ }))
2076
+ }
2077
+ if (topLevelSections.length === 0) {
2078
+ // No <section> found: treat each first-level child as a component, wrapped in a section
2079
+ const parent = pagebuilderDiv || doc.body
2080
+ const children = Array.from(parent.children)
2081
+ if (children.length > 0) {
2082
+ components = children.map((child) => {
2083
+ // Wrap in a section with data-componentid and data-component-title
2084
+ const section = doc.createElement('section')
2085
+ section.setAttribute('data-component-title', 'Untitled Component')
2086
+ // Optionally: generate a uuid for data-componentid if needed
2087
+ // section.setAttribute('data-componentid', uuidv4())
2088
+ section.innerHTML = child.outerHTML.trim()
2089
+ return {
2090
+ id: null,
2091
+ html_code: section.outerHTML.trim(),
2092
+ title: 'Untitled Component',
2093
+ }
2094
+ })
2095
+ }
2096
+ if (children.length === 0) {
2097
+ // No children: wrap the entire content in a <section> as a single component
2098
+ const section = doc.createElement('section')
2099
+ section.setAttribute('data-component-title', 'Untitled Component')
2100
+ section.innerHTML = parent.innerHTML.trim()
2101
+ components = [
2102
+ {
2103
+ id: null,
2104
+ html_code: section.outerHTML.trim(),
2105
+ title: 'Untitled Component',
2106
+ },
2107
+ ]
2108
+ }
2109
+ }
2110
+
2111
+ return {
2112
+ components,
2113
+ pageSettings,
2114
+ }
2115
+ }
2116
+
1831
2117
  /**
1832
2118
  * Parse and set components from JSON or HTML data
1833
2119
  *
@@ -1840,56 +2126,74 @@ export class PageBuilderService {
1840
2126
  * @param data - JSON string (e.g., '[{"html_code":"...","id":"123","title":"..."}]')
1841
2127
  * OR HTML string (e.g., '<section data-componentid="123">...</section>')
1842
2128
  */
1843
- async #mountComponentsToDOM(htmlString: string, usePassedPageSettings?: boolean): Promise<void> {
1844
- // Auto-detect if input is JSON or HTML
2129
+ private async mountComponentsToDOM(
2130
+ htmlString: string,
2131
+ usePassedPageSettings?: boolean,
2132
+ ): Promise<void> {
2133
+ /**
2134
+ * Mounts builder components to the DOM from either JSON or HTML input.
2135
+ *
2136
+ * Input format detection:
2137
+ * - If the input starts with `[` or `{`, it is treated as JSON (array or object).
2138
+ * - If the input starts with `<`, it is treated as HTML.
2139
+ *
2140
+ * When to use which format:
2141
+ *
2142
+ * 1. JSON input (from localStorage, API, or internal state like pina):
2143
+ * - Use when restoring builder state from localStorage, an API, or a previously saved draft.
2144
+ * - Example: `localStorage.getItem(...)` or API returns a JSON stringified array/object of components.
2145
+ * - This is the most common format for drafts, autosave, and programmatic state management.
2146
+ * - Example usage:
2147
+ * await this.mountComponentsToDOM(JSON.stringify(getComponents))
2148
+ *
2149
+ * 2. HTML input (from HTML snapshot, import, or published output):
2150
+ * - Use when restoring from a published HTML snapshot, importing a static HTML export, or loading the builder from a previously published HTML string.
2151
+ * - Example: output from `getSavedPageHtml()` or a static HTML export.
2152
+ * - This is used for restoring the builder from a published state, importing, or previewing published content.
2153
+ * - Example usage:
2154
+ * await this.mountComponentsToDOM(savedHtmlString)
2155
+ *
2156
+ * Best practice:
2157
+ * - Use JSON for local storage drafts, autosave, and API-driven workflows.
2158
+ * - Use HTML for published/imported content from DB or when restoring from a static HTML snapshot.
2159
+ *
2160
+ * The method auto-detects the format and calls the appropriate parser.
2161
+ */
1845
2162
  const trimmedData = htmlString.trim()
1846
2163
 
1847
2164
  if (trimmedData.startsWith('[') || trimmedData.startsWith('{')) {
1848
- // Looks like JSON - parse as JSON
1849
- await this.#parseJSONComponents(trimmedData, usePassedPageSettings)
2165
+ // JSON input: Use this when restoring from localStorage, API, or internal builder state (drafts, autosave, etc.)
2166
+ await this.parseJSONComponents(trimmedData, usePassedPageSettings)
1850
2167
  return
1851
2168
  }
1852
2169
  if (trimmedData.startsWith('<')) {
1853
- // Looks like HTML - parse as HTML
1854
- await this.#parseHTMLComponents(trimmedData, usePassedPageSettings)
2170
+ // HTML input: Use this when restoring from a published HTML snapshot, import, or static HTML export
2171
+ await this.parseHTMLComponents(trimmedData, usePassedPageSettings)
1855
2172
  return
1856
2173
  }
1857
2174
 
1858
- await this.#parseJSONComponents(trimmedData, usePassedPageSettings)
2175
+ // Fallback: If format is unknown, default to JSON parser (defensive)
2176
+ await this.parseJSONComponents(trimmedData, usePassedPageSettings)
1859
2177
  }
1860
2178
 
1861
2179
  // Private method to parse JSON components and save pageBuilderContentSavedAt to localStorage
1862
- async #parseJSONComponents(jsonData: string, usePassedPageSettings?: boolean): Promise<void> {
1863
- const storedPageSettings =
2180
+ private async parseJSONComponents(
2181
+ jsonData: string,
2182
+ usePassedPageSettings?: boolean,
2183
+ ): Promise<void> {
2184
+ const pageSettings =
1864
2185
  this.pageBuilderStateStore.getPageBuilderConfig &&
1865
2186
  this.pageBuilderStateStore.getPageBuilderConfig.pageSettings
1866
2187
 
2188
+ const userPageSettings = usePassedPageSettings ? pageSettings : null
1867
2189
  try {
1868
2190
  const parsedData = JSON.parse(jsonData)
1869
2191
  let componentsArray: ComponentObject[] = []
1870
2192
 
1871
- // Decide which pageSettings to use
1872
- const pageSettings = usePassedPageSettings
1873
- ? storedPageSettings
1874
- : parsedData && parsedData.pageSettings
1875
- ? parsedData.pageSettings
1876
- : null
1877
-
1878
- // Restore page-level settings like class and style
1879
- if (pageSettings) {
1880
- const pagebuilder = document.querySelector('#pagebuilder') as HTMLElement
1881
- if (pagebuilder) {
1882
- pagebuilder.removeAttribute('class')
1883
- pagebuilder.removeAttribute('style')
1884
- pagebuilder.className = pageSettings.classes || ''
1885
- pagebuilder.setAttribute('style', this.#convertStyleObjectToString(pageSettings.style))
1886
- }
1887
- }
1888
-
1889
- // Support both old and new structure
1890
2193
  if (Array.isArray(parsedData)) {
1891
2194
  componentsArray = parsedData
1892
- } else if (parsedData && Array.isArray(parsedData.components)) {
2195
+ }
2196
+ if (parsedData && Array.isArray(parsedData.components)) {
1893
2197
  componentsArray = parsedData.components
1894
2198
  }
1895
2199
 
@@ -1906,7 +2210,7 @@ export class PageBuilderService {
1906
2210
  section.querySelectorAll('[class]').forEach((el) => {
1907
2211
  el.setAttribute(
1908
2212
  'class',
1909
- this.#addTailwindPrefixToClasses(el.getAttribute('class') || '', 'pbx-'),
2213
+ this.addTailwindPrefixToClasses(el.getAttribute('class') || '', 'pbx-'),
1910
2214
  )
1911
2215
  })
1912
2216
 
@@ -1933,14 +2237,28 @@ export class PageBuilderService {
1933
2237
  this.pageBuilderStateStore.setComponents(savedCurrentDesign)
1934
2238
 
1935
2239
  await nextTick()
1936
- await this.#addListenersToEditableElements()
2240
+ await this.addListenersToEditableElements()
2241
+
2242
+ if (userPageSettings && pageSettings) {
2243
+ const pagebuilder = document.querySelector('#pagebuilder') as HTMLElement
2244
+ if (pagebuilder) {
2245
+ pagebuilder.removeAttribute('class')
2246
+ pagebuilder.removeAttribute('style')
2247
+ pagebuilder.className = pageSettings.classes || ''
2248
+ pagebuilder.setAttribute('style', this.convertStyleObjectToString(pageSettings.style))
2249
+ }
2250
+ }
1937
2251
  } catch (error) {
1938
2252
  console.error('Error parsing JSON components:', error)
1939
2253
  this.deleteAllComponentsFromDOM()
1940
2254
  }
1941
2255
  }
2256
+
1942
2257
  // Private method to parse HTML components
1943
- async #parseHTMLComponents(htmlData: string, usePassedPageSettings?: boolean): Promise<void> {
2258
+ private async parseHTMLComponents(
2259
+ htmlData: string,
2260
+ usePassedPageSettings?: boolean,
2261
+ ): Promise<void> {
1944
2262
  try {
1945
2263
  const parser = new DOMParser()
1946
2264
  const doc = parser.parseFromString(htmlData, 'text/html')
@@ -1971,10 +2289,7 @@ export class PageBuilderService {
1971
2289
  livePageBuilder.removeAttribute('class')
1972
2290
  livePageBuilder.removeAttribute('style')
1973
2291
  livePageBuilder.className = pageSettings.classes || ''
1974
- livePageBuilder.setAttribute(
1975
- 'style',
1976
- this.#convertStyleObjectToString(pageSettings.style),
1977
- )
2292
+ livePageBuilder.setAttribute('style', this.convertStyleObjectToString(pageSettings.style))
1978
2293
  }
1979
2294
  }
1980
2295
 
@@ -1987,7 +2302,7 @@ export class PageBuilderService {
1987
2302
  section.querySelectorAll('[class]').forEach((el) => {
1988
2303
  el.setAttribute(
1989
2304
  'class',
1990
- this.#addTailwindPrefixToClasses(el.getAttribute('class') || '', 'pbx-'),
2305
+ this.addTailwindPrefixToClasses(el.getAttribute('class') || '', 'pbx-'),
1991
2306
  )
1992
2307
  })
1993
2308
 
@@ -2019,67 +2334,192 @@ export class PageBuilderService {
2019
2334
  // Clear selections and re-bind events
2020
2335
  await this.clearHtmlSelection()
2021
2336
  await nextTick()
2022
- await this.#addListenersToEditableElements()
2337
+ await this.addListenersToEditableElements()
2023
2338
  } catch (error) {
2024
2339
  console.error('Error parsing HTML components:', error)
2025
2340
  this.deleteAllComponentsFromDOM()
2026
2341
  }
2027
2342
  }
2028
2343
 
2029
- async initializeElementStyles(): Promise<void> {
2344
+ private updateLocalStorageItemName(): void {
2345
+ const formtype =
2346
+ this.pageBuilderStateStore.getPageBuilderConfig &&
2347
+ this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
2348
+ this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType
2349
+
2350
+ const formname =
2351
+ this.pageBuilderStateStore.getPageBuilderConfig &&
2352
+ this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
2353
+ this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formName
2354
+
2355
+ const resourceData =
2356
+ this.pageBuilderStateStore.getPageBuilderConfig &&
2357
+ this.pageBuilderStateStore.getPageBuilderConfig.resourceData
2358
+
2359
+ // Logic for create resource
2360
+ if (formtype === 'create') {
2361
+ if (formname && formname.length > 0) {
2362
+ this.pageBuilderStateStore.setLocalStorageItemName(
2363
+ `page-builder-create-resource-${this.sanitizeForLocalStorage(formname)}`,
2364
+ )
2365
+ return
2366
+ }
2367
+
2368
+ this.pageBuilderStateStore.setLocalStorageItemName(`page-builder-create-resource`)
2369
+ return
2370
+ }
2371
+
2372
+ // Logic for create
2373
+ // Logic for update and with resource form name
2374
+ if (formtype === 'update') {
2375
+ if (typeof formname === 'string' && formname.length > 0) {
2376
+ //
2377
+ //
2378
+ if (resourceData && resourceData != null && !resourceData.title) {
2379
+ // Check if id is missing, null, undefined, or an empty string (after trimming)
2380
+ if (!resourceData.id || typeof resourceData.id === 'string') {
2381
+ this.pageBuilderStateStore.setLocalStorageItemName(
2382
+ `page-builder-update-resource-${this.sanitizeForLocalStorage(formname)}`,
2383
+ )
2384
+ return
2385
+ }
2386
+ }
2387
+
2388
+ // Runs when resourceData has title but no ID
2389
+ if (resourceData && resourceData != null) {
2390
+ if (
2391
+ resourceData.title &&
2392
+ typeof resourceData.title === 'string' &&
2393
+ resourceData.title.length > 0
2394
+ ) {
2395
+ if (!resourceData.id || typeof resourceData.id === 'string') {
2396
+ this.pageBuilderStateStore.setLocalStorageItemName(
2397
+ `page-builder-update-resource-${this.sanitizeForLocalStorage(formname)}-${this.sanitizeForLocalStorage(resourceData.title)}`,
2398
+ )
2399
+ return
2400
+ }
2401
+ }
2402
+ }
2403
+
2404
+ // Runs when resourceData has ID but no title
2405
+ if (resourceData && resourceData != null) {
2406
+ if (!resourceData.title && typeof resourceData.title !== 'string') {
2407
+ if (resourceData.id || typeof resourceData.id === 'number') {
2408
+ this.pageBuilderStateStore.setLocalStorageItemName(
2409
+ `page-builder-update-resource-${this.sanitizeForLocalStorage(formname)}-${this.sanitizeForLocalStorage(String(resourceData.id))}`,
2410
+ )
2411
+ return
2412
+ }
2413
+ }
2414
+ }
2415
+
2416
+ // Runs when resourceData has both title and ID
2417
+ if (resourceData && resourceData != null) {
2418
+ if (
2419
+ resourceData.title &&
2420
+ typeof resourceData.title === 'string' &&
2421
+ resourceData.title.length > 0
2422
+ ) {
2423
+ if (resourceData.id || typeof resourceData.id === 'number') {
2424
+ this.pageBuilderStateStore.setLocalStorageItemName(
2425
+ `page-builder-update-resource-${this.sanitizeForLocalStorage(formname)}-${this.sanitizeForLocalStorage(resourceData.title)}-${this.sanitizeForLocalStorage(String(resourceData.id))}`,
2426
+ )
2427
+ return
2428
+ }
2429
+ }
2430
+ }
2431
+ }
2432
+
2433
+ // Logic for update without without formname
2434
+ if (!formname || (typeof formname === 'string' && formname.length === 0)) {
2435
+ //
2436
+ //
2437
+ if (resourceData && resourceData != null && !resourceData.title) {
2438
+ // Check if id is missing, null, undefined, or an empty string (after trimming)
2439
+ if (!resourceData.id || typeof resourceData.id === 'string') {
2440
+ this.pageBuilderStateStore.setLocalStorageItemName(`page-builder-update-resource`)
2441
+ return
2442
+ }
2443
+ }
2444
+
2445
+ // Runs when resourceData has title but no ID
2446
+ if (resourceData && resourceData != null) {
2447
+ if (
2448
+ resourceData.title &&
2449
+ typeof resourceData.title === 'string' &&
2450
+ resourceData.title.length > 0
2451
+ ) {
2452
+ if (!resourceData.id || typeof resourceData.id === 'string') {
2453
+ this.pageBuilderStateStore.setLocalStorageItemName(
2454
+ `page-builder-update-resource-${this.sanitizeForLocalStorage(resourceData.title)}`,
2455
+ )
2456
+ return
2457
+ }
2458
+ }
2459
+ }
2460
+
2461
+ // Runs when resourceData has ID but no title
2462
+ if (resourceData && resourceData != null) {
2463
+ if (!resourceData.title && typeof resourceData.title !== 'string') {
2464
+ if (resourceData.id || typeof resourceData.id === 'number') {
2465
+ this.pageBuilderStateStore.setLocalStorageItemName(
2466
+ `page-builder-update-resource-${this.sanitizeForLocalStorage(String(resourceData.id))}`,
2467
+ )
2468
+ return
2469
+ }
2470
+ }
2471
+ }
2472
+
2473
+ // Runs when resourceData has both title and ID
2474
+ if (resourceData && resourceData != null) {
2475
+ if (
2476
+ resourceData.title &&
2477
+ typeof resourceData.title === 'string' &&
2478
+ resourceData.title.length > 0
2479
+ ) {
2480
+ if (resourceData.id || typeof resourceData.id === 'number') {
2481
+ this.pageBuilderStateStore.setLocalStorageItemName(
2482
+ `page-builder-update-resource-${this.sanitizeForLocalStorage(resourceData.title)}-${this.sanitizeForLocalStorage(String(resourceData.id))}`,
2483
+ )
2484
+ return
2485
+ }
2486
+ }
2487
+ }
2488
+ }
2489
+ }
2490
+ }
2491
+
2492
+ public async initializeElementStyles(): Promise<void> {
2030
2493
  // Wait for Vue to finish DOM updates before attaching event listeners.
2031
2494
  // This ensure elements exist in the DOM.
2032
2495
  await nextTick()
2033
2496
 
2034
- // handle custom URL
2035
2497
  this.handleHyperlink(undefined, null, false)
2036
- // handle opacity
2037
2498
  this.handleOpacity(undefined)
2038
- // handle BG opacity
2039
2499
  this.handleBackgroundOpacity(undefined)
2040
- // displayed image
2041
2500
  this.setBasePrimaryImageFromSelectedElement()
2042
- // border style
2043
2501
  this.handleBorderStyle(undefined)
2044
- // border width
2045
2502
  this.handleBorderWidth(undefined)
2046
- // border color
2047
2503
  this.handleBorderColor(undefined)
2048
- // border radius
2049
2504
  this.handleBorderRadiusGlobal(undefined)
2050
- // border radius
2051
2505
  this.handleBorderRadiusTopLeft(undefined)
2052
- // border radius
2053
2506
  this.handleBorderRadiusTopRight(undefined)
2054
- // border radius
2055
2507
  this.handleBorderRadiusBottomleft(undefined)
2056
- // border radius
2057
2508
  this.handleBorderRadiusBottomRight(undefined)
2058
- // handle font size
2059
2509
  this.handleFontSizeBase(undefined)
2060
2510
  this.handleFontSizeDesktop(undefined)
2061
2511
  this.handleFontSizeTablet(undefined)
2062
2512
  this.handleFontSizeMobile(undefined)
2063
- // handle font weight
2064
2513
  this.handleFontWeight(undefined)
2065
- // handle font family
2066
-
2067
2514
  this.handleFontFamily(undefined)
2068
- // handle font style
2069
2515
  this.handleFontStyle(undefined)
2070
- // handle vertical padding
2071
2516
  this.handleVerticalPadding(undefined)
2072
- // handle horizontal padding
2073
2517
  this.handleHorizontalPadding(undefined)
2074
- // handle vertical margin
2075
2518
  this.handleVerticalMargin(undefined)
2076
- // handle horizontal margin
2077
2519
  this.handleHorizontalMargin(undefined)
2078
- // handle color
2079
2520
  this.handleBackgroundColor(undefined)
2080
- // handle text color
2081
2521
  this.handleTextColor(undefined)
2082
- // handle classes
2083
- await this.#syncCurrentClasses()
2522
+ await this.syncCurrentClasses()
2523
+ await this.syncCurrentStyles()
2084
2524
  }
2085
2525
  }