@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.
- package/README.md +216 -129
- package/dist/logo/mybuilder_new_lowercase.svg +17558 -0
- package/dist/vue-website-page-builder.css +1 -1
- package/dist/vue-website-page-builder.js +16345 -13089
- package/dist/vue-website-page-builder.umd.cjs +78 -54
- package/package.json +3 -2
- package/src/Components/DemoUnsplash.vue +1 -4
- package/src/Components/PageBuilder/EditorMenu/Editables/BackgroundColorEditor.vue +4 -3
- package/src/Components/PageBuilder/EditorMenu/Editables/BorderRadius.vue +32 -13
- package/src/Components/PageBuilder/EditorMenu/Editables/Borders.vue +12 -8
- package/src/Components/PageBuilder/EditorMenu/Editables/ClassEditor.vue +10 -8
- package/src/Components/PageBuilder/EditorMenu/Editables/EditGetElement.vue +5 -5
- package/src/Components/PageBuilder/EditorMenu/Editables/ManageBackgroundOpacity.vue +12 -9
- package/src/Components/PageBuilder/EditorMenu/Editables/ManageOpacity.vue +6 -4
- package/src/Components/PageBuilder/EditorMenu/Editables/Margin.vue +11 -5
- package/src/Components/PageBuilder/EditorMenu/Editables/OpacityEditor.vue +1 -1
- package/src/Components/PageBuilder/EditorMenu/Editables/Padding.vue +11 -5
- package/src/Components/PageBuilder/EditorMenu/Editables/StyleEditor.vue +116 -0
- package/src/Components/PageBuilder/EditorMenu/Editables/TextColorEditor.vue +2 -1
- package/src/Components/PageBuilder/EditorMenu/Editables/Typography.vue +32 -9
- package/src/Components/PageBuilder/EditorMenu/RightSidebarEditor.vue +63 -74
- package/src/Components/PageBuilder/Settings/PageBuilderSettings.vue +3 -3
- package/src/Components/PageBuilder/ToolbarOption/ToolbarOption.vue +14 -11
- package/src/PageBuilder/PageBuilder.vue +169 -65
- package/src/PageBuilder/Preview.vue +25 -9
- package/src/composables/builderInstance.ts +3 -2
- package/src/composables/extractCleanHTMLFromPageBuilder.ts +4 -3
- package/src/css/app.css +10 -70
- package/src/i18n.ts +28 -0
- package/src/locales/ar.json +136 -0
- package/src/locales/de.json +136 -0
- package/src/locales/en.json +136 -0
- package/src/locales/es.json +136 -0
- package/src/locales/fr.json +136 -0
- package/src/locales/hi.json +136 -0
- package/src/locales/ja.json +136 -0
- package/src/locales/pt.json +136 -0
- package/src/locales/ru.json +136 -0
- package/src/locales/zh-Hans.json +136 -0
- package/src/main.ts +10 -5
- package/src/services/LocalStorageManager.ts +1 -162
- package/src/services/PageBuilderService.ts +730 -290
- package/src/stores/page-builder-state.ts +8 -0
- package/src/tests/PageBuilderTest.vue +41 -70
- package/src/tests/componentsArray.test.json +3 -3
- package/src/tests/pageBuilderService.test.ts +7 -1
- package/src/types/index.ts +17 -3
- 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
|
|
48
|
-
private
|
|
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.
|
|
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
|
|
141
|
+
await this.removeHoveredAndSelected()
|
|
108
142
|
}
|
|
109
143
|
|
|
110
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
409
|
+
this.validateConfig(config)
|
|
281
410
|
|
|
282
|
-
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
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(
|
|
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.
|
|
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.
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
await this
|
|
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
|
-
|
|
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
|
-
|
|
363
|
-
await this
|
|
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
|
-
|
|
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
|
-
|
|
527
|
+
console.log('7777:', internalPageBuilderCall)
|
|
528
|
+
await this.completeMountProcess(JSON.stringify([]))
|
|
374
529
|
return
|
|
375
530
|
}
|
|
376
531
|
}
|
|
377
532
|
|
|
378
|
-
// FOCUS ON:
|
|
379
|
-
if (this.
|
|
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
|
-
|
|
383
|
-
this.pendingMountData = null
|
|
541
|
+
this.pendingMountComponents = null
|
|
384
542
|
return
|
|
385
543
|
}
|
|
386
544
|
if (!localStorageData && passedComponentsArray && this.isPageBuilderMissingOnStart) {
|
|
387
|
-
|
|
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
|
-
|
|
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
|
|
401
|
-
await this
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
567
|
-
this
|
|
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
|
|
762
|
+
await this.addListenersToEditableElements()
|
|
604
763
|
|
|
605
764
|
if (!status) {
|
|
606
765
|
await this.handleAutoSave()
|
|
607
766
|
}
|
|
608
767
|
}
|
|
609
768
|
|
|
610
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
706
|
-
htmlElement.addEventListener('mouseover', (e) => this
|
|
707
|
-
htmlElement.addEventListener('mouseleave', (e) => this
|
|
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
|
|
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
|
|
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
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
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('#
|
|
791
|
-
// scoll to top or bottom
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
890
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1367
|
-
this.
|
|
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;
|
|
1540
|
+
const componentsToSave: { html_code: string; title: string }[] = []
|
|
1377
1541
|
|
|
1378
1542
|
pagebuilder.querySelectorAll('section[data-componentid]').forEach((section) => {
|
|
1379
|
-
const sanitizedSection = this
|
|
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.
|
|
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.
|
|
1656
|
+
public async resumeEditingFromDraft() {
|
|
1657
|
+
this.updateLocalStorageItemName()
|
|
1476
1658
|
|
|
1477
|
-
const localStorageData = this.
|
|
1659
|
+
const localStorageData = this.getSavedPageHtml()
|
|
1478
1660
|
|
|
1479
1661
|
if (localStorageData) {
|
|
1480
|
-
this.pageBuilderStateStore.setIsLoadingResumeEditing(true)
|
|
1481
1662
|
await delay(400)
|
|
1482
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
1521
|
-
this.localStorageManager.updateLocalStorageItemName()
|
|
1702
|
+
public getSavedPageHtml() {
|
|
1522
1703
|
if (!this.getLocalStorageItemName.value) return false
|
|
1523
1704
|
|
|
1524
|
-
|
|
1525
|
-
|
|
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
|
-
|
|
1531
|
-
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1708
|
+
const raw = localStorage.getItem(key)
|
|
1709
|
+
if (!raw) return false
|
|
1534
1710
|
|
|
1535
|
-
|
|
1536
|
-
}
|
|
1711
|
+
const parsed = JSON.parse(raw)
|
|
1537
1712
|
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1930
|
+
this.checkForHyperlink()
|
|
1753
1931
|
return
|
|
1754
1932
|
}
|
|
1755
1933
|
|
|
1756
|
-
this
|
|
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('#
|
|
1776
|
-
// scoll to top or bottom
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1844
|
-
|
|
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
|
-
//
|
|
1849
|
-
await this
|
|
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
|
-
//
|
|
1854
|
-
await this
|
|
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
|
-
|
|
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
|
|
1863
|
-
|
|
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
|
-
}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
2083
|
-
await this
|
|
2522
|
+
await this.syncCurrentClasses()
|
|
2523
|
+
await this.syncCurrentStyles()
|
|
2084
2524
|
}
|
|
2085
2525
|
}
|