@myissue/vue-website-page-builder 3.3.11 → 3.3.13
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 +118 -131
- package/dist/vue-website-page-builder.css +1 -1
- package/dist/vue-website-page-builder.js +5382 -5317
- package/dist/vue-website-page-builder.umd.cjs +38 -38
- package/package.json +1 -1
- package/src/Components/Loaders/GlobalLoader.vue +1 -1
- package/src/Components/Modals/ModalBuilder.vue +2 -2
- package/src/Components/PageBuilder/EditorMenu/Editables/ComponentTopMenu.vue +4 -4
- package/src/Components/PageBuilder/EditorMenu/Editables/DeleteElement.vue +2 -2
- package/src/Components/PageBuilder/EditorMenu/Editables/ElementEditor.vue +2 -2
- package/src/Components/PageBuilder/ToolbarOption/ToolbarOption.vue +4 -4
- package/src/DemoComponents/HomeSection.vue +7 -3
- package/src/DemoComponents/html.json +46 -48
- package/src/PageBuilder/PageBuilder.vue +30 -27
- package/src/composables/PageBuilderService.ts +470 -337
- package/src/helpers/isEmptyObject.ts +1 -1
- package/src/stores/page-builder-state.ts +1 -1
- package/src/tailwind-safelist.html +1 -1
- package/src/types/index.ts +10 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// Type definitions
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
BuilderResourceData,
|
|
4
|
+
ComponentObject,
|
|
5
|
+
ImageObject,
|
|
6
|
+
PageBuilderConfig,
|
|
7
|
+
StartBuilderResult,
|
|
8
|
+
} from '../types'
|
|
3
9
|
|
|
4
10
|
import type { usePageBuilderStateStore } from '../stores/page-builder-state'
|
|
5
11
|
|
|
@@ -18,11 +24,7 @@ import { isEmptyObject } from '../helpers/isEmptyObject'
|
|
|
18
24
|
|
|
19
25
|
export class PageBuilderService {
|
|
20
26
|
// Class properties with types
|
|
21
|
-
private nextTick: Promise<void>
|
|
22
|
-
private containsPagebuilder: Element | null
|
|
23
|
-
// private pageBuilder: Element | null
|
|
24
27
|
private pageBuilderStateStore: ReturnType<typeof usePageBuilderStateStore>
|
|
25
|
-
private getTextAreaVueModel: ComputedRef<string | null>
|
|
26
28
|
private getLocalStorageItemName: ComputedRef<string | null>
|
|
27
29
|
private getApplyImageToSelection: ComputedRef<ImageObject>
|
|
28
30
|
private getHyberlinkEnable: ComputedRef<boolean>
|
|
@@ -37,17 +39,14 @@ export class PageBuilderService {
|
|
|
37
39
|
private delay: (ms?: number) => Promise<void>
|
|
38
40
|
private hasStartedEditing: boolean = false
|
|
39
41
|
// Hold data from Database or Backend for updated post
|
|
40
|
-
private originalComponents:
|
|
42
|
+
private originalComponents: BuilderResourceData | undefined = undefined
|
|
41
43
|
// Holds data to be mounted when #pagebuilder is not yet present in the DOM
|
|
42
|
-
private pendingMountData:
|
|
44
|
+
private pendingMountData: BuilderResourceData | null = null
|
|
43
45
|
|
|
44
46
|
constructor(pageBuilderStateStore: ReturnType<typeof usePageBuilderStateStore>) {
|
|
45
|
-
this.nextTick = nextTick()
|
|
46
47
|
this.hasStartedEditing = false
|
|
47
|
-
this.containsPagebuilder = document.querySelector('#contains-pagebuilder')
|
|
48
48
|
this.pageBuilderStateStore = pageBuilderStateStore
|
|
49
49
|
|
|
50
|
-
this.getTextAreaVueModel = computed(() => this.pageBuilderStateStore.getTextAreaVueModel)
|
|
51
50
|
this.getLocalStorageItemName = computed(
|
|
52
51
|
() => this.pageBuilderStateStore.getLocalStorageItemName,
|
|
53
52
|
)
|
|
@@ -176,6 +175,82 @@ export class PageBuilderService {
|
|
|
176
175
|
}
|
|
177
176
|
}
|
|
178
177
|
|
|
178
|
+
#validateUserProvidedComponents(components: unknown) {
|
|
179
|
+
const formType =
|
|
180
|
+
this.pageBuilderStateStore.getPageBuilderConfig &&
|
|
181
|
+
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
|
|
182
|
+
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType
|
|
183
|
+
|
|
184
|
+
if (Array.isArray(components) && components.length === 0) {
|
|
185
|
+
return { error: false as const, message: 'No components provided (empty array).' }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (
|
|
189
|
+
Array.isArray(components) &&
|
|
190
|
+
components.length >= 1 &&
|
|
191
|
+
formType === 'create' &&
|
|
192
|
+
components
|
|
193
|
+
) {
|
|
194
|
+
return {
|
|
195
|
+
error: true as const,
|
|
196
|
+
warning:
|
|
197
|
+
'You cannot set formType to create in your configuration while also passing a components data array to the Page Builder. Please set formType to update.',
|
|
198
|
+
status: 'validation_failed',
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (formType === 'create' && components) {
|
|
202
|
+
return {
|
|
203
|
+
error: true as const,
|
|
204
|
+
warning:
|
|
205
|
+
'You cannot set formType to create in your configuration while also passing a components data array to the Page Builder. Please set formType to update.',
|
|
206
|
+
status: 'validation_failed',
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Must be an array
|
|
211
|
+
if (!Array.isArray(components)) {
|
|
212
|
+
return {
|
|
213
|
+
error: true as const,
|
|
214
|
+
reason: 'Components data must be an array.',
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Check that the first item looks like a component
|
|
219
|
+
const first = components[0]
|
|
220
|
+
|
|
221
|
+
// Check that the first item is not an empty object
|
|
222
|
+
if (isEmptyObject(first)) {
|
|
223
|
+
console.error(
|
|
224
|
+
'The first object in the array is empty. Each component must be a non-empty object and include an html_code key.',
|
|
225
|
+
)
|
|
226
|
+
return {
|
|
227
|
+
error: true as const,
|
|
228
|
+
reason:
|
|
229
|
+
"The first object in the array is empty. Each component must be a non-empty object and include an 'html_code' key.",
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (first && 'html_code' in first && typeof first.html_code !== 'string') {
|
|
234
|
+
console.error("The 'html_code' property in the first object must be a string.")
|
|
235
|
+
return {
|
|
236
|
+
error: true as const,
|
|
237
|
+
reason: "The 'html_code' property in the first object must be a string.",
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check that the first item has an 'html_code' key
|
|
242
|
+
if (!first || !('html_code' in first)) {
|
|
243
|
+
console.error("The first object in the array must include an 'html_code' key.")
|
|
244
|
+
return {
|
|
245
|
+
error: true as const,
|
|
246
|
+
reason: "The first object in the array must include an 'html_code' key.",
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// No errors found
|
|
251
|
+
return { error: false as const }
|
|
252
|
+
}
|
|
253
|
+
|
|
179
254
|
#validateConfig(config: PageBuilderConfig): void {
|
|
180
255
|
const defaultConfigValues = {
|
|
181
256
|
updateOrCreate: {
|
|
@@ -194,6 +269,96 @@ export class PageBuilderService {
|
|
|
194
269
|
}
|
|
195
270
|
}
|
|
196
271
|
|
|
272
|
+
#handlePageBuilderNotPresent(passedDataComponents: BuilderResourceData) {
|
|
273
|
+
this.pendingMountData = passedDataComponents
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async #mountPassedComponentsToDOM(components?: BuilderResourceData): Promise<void> {
|
|
277
|
+
const config = this.pageBuilderStateStore.getPageBuilderConfig
|
|
278
|
+
const formType = config && config.updateOrCreate && config.updateOrCreate.formType
|
|
279
|
+
const localStorageData = this.loadStoredComponentsFromStorage()
|
|
280
|
+
|
|
281
|
+
let dataToPass: string
|
|
282
|
+
if (typeof components === 'string') {
|
|
283
|
+
dataToPass = components
|
|
284
|
+
} else if (components !== undefined) {
|
|
285
|
+
dataToPass = JSON.stringify(components)
|
|
286
|
+
} else {
|
|
287
|
+
dataToPass = ''
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
await this.#updateComponentsFromString(dataToPass)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async tryMountPendingComponents() {
|
|
294
|
+
this.pageBuilderStateStore.setIsLoadingGlobal(true)
|
|
295
|
+
await delay(200)
|
|
296
|
+
const config = this.pageBuilderStateStore.getPageBuilderConfig
|
|
297
|
+
const formType = config && config.updateOrCreate && config.updateOrCreate.formType
|
|
298
|
+
const localStorageData = this.loadStoredComponentsFromStorage()
|
|
299
|
+
//
|
|
300
|
+
if (!config) return
|
|
301
|
+
//
|
|
302
|
+
if (
|
|
303
|
+
config &&
|
|
304
|
+
formType === 'update' &&
|
|
305
|
+
localStorageData &&
|
|
306
|
+
typeof localStorageData === 'string' &&
|
|
307
|
+
this.pendingMountData
|
|
308
|
+
) {
|
|
309
|
+
this.pageBuilderStateStore.setHasLocalDraftForUpdate(true)
|
|
310
|
+
}
|
|
311
|
+
//
|
|
312
|
+
//
|
|
313
|
+
//
|
|
314
|
+
//
|
|
315
|
+
if (config && formType === 'update') {
|
|
316
|
+
if (this.pendingMountData) {
|
|
317
|
+
this.#completeBuilderInitialization(this.pendingMountData)
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Pending data for mount is null at this stage
|
|
322
|
+
if (typeof localStorageData === 'string') {
|
|
323
|
+
await this.#updateComponentsFromString(localStorageData)
|
|
324
|
+
this.#completeBuilderInitialization()
|
|
325
|
+
return
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
//
|
|
329
|
+
//
|
|
330
|
+
//
|
|
331
|
+
//
|
|
332
|
+
// Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
|
|
333
|
+
await nextTick()
|
|
334
|
+
// Attach event listeners to all editable elements in the Builder
|
|
335
|
+
await this.#addListenersToEditableElements()
|
|
336
|
+
|
|
337
|
+
this.pageBuilderStateStore.setIsRestoring(false)
|
|
338
|
+
this.pageBuilderStateStore.setIsLoadingGlobal(false)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (config && formType === 'create') {
|
|
342
|
+
// Pending data for mount is null at this stage
|
|
343
|
+
if (typeof localStorageData === 'string') {
|
|
344
|
+
await this.#updateComponentsFromString(localStorageData)
|
|
345
|
+
this.#completeBuilderInitialization()
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
//
|
|
350
|
+
//
|
|
351
|
+
//
|
|
352
|
+
//
|
|
353
|
+
// Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
|
|
354
|
+
await nextTick()
|
|
355
|
+
// Attach event listeners to all editable elements in the Builder
|
|
356
|
+
await this.#addListenersToEditableElements()
|
|
357
|
+
|
|
358
|
+
this.pageBuilderStateStore.setIsRestoring(false)
|
|
359
|
+
this.pageBuilderStateStore.setIsLoadingGlobal(false)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
197
362
|
/**
|
|
198
363
|
* - Entry point for initializing the Page Builder.
|
|
199
364
|
* - Sets the builder as started in the state store.
|
|
@@ -204,53 +369,103 @@ export class PageBuilderService {
|
|
|
204
369
|
*
|
|
205
370
|
* @param config - The configuration object for the Page Builder.
|
|
206
371
|
*/
|
|
207
|
-
async startBuilder(
|
|
372
|
+
async startBuilder(
|
|
373
|
+
config: PageBuilderConfig,
|
|
374
|
+
passedComponentsArray?: BuilderResourceData,
|
|
375
|
+
): Promise<StartBuilderResult> {
|
|
208
376
|
// Reactive flag signals to the UI that the builder has been successfully initialized
|
|
209
377
|
// Prevents builder actions to prevent errors caused by missing DOM .
|
|
210
378
|
this.pageBuilderStateStore.setBuilderStarted(true)
|
|
379
|
+
const pagebuilder = document.querySelector('#pagebuilder')
|
|
380
|
+
let validation
|
|
211
381
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
// Store the provided config in the builder's state store
|
|
218
|
-
this.pageBuilderStateStore.setPageBuilderConfig(config)
|
|
382
|
+
try {
|
|
383
|
+
this.originalComponents = passedComponentsArray
|
|
384
|
+
this.pageBuilderStateStore.setPageBuilderConfig(config)
|
|
385
|
+
// Validate and normalize the config (ensure required fields are present)
|
|
386
|
+
this.#validateConfig(config)
|
|
219
387
|
|
|
220
|
-
|
|
221
|
-
this.#validateConfig(config)
|
|
388
|
+
validation = this.#validateUserProvidedComponents(passedComponentsArray)
|
|
222
389
|
|
|
223
|
-
|
|
224
|
-
|
|
390
|
+
// Update the localStorage key name based on the config/resource
|
|
391
|
+
this.#updateLocalStorageItemName()
|
|
225
392
|
|
|
226
|
-
|
|
393
|
+
// Page Builder is not Present in the DOM but Components have been passed to the Builder
|
|
394
|
+
if (passedComponentsArray && !pagebuilder) {
|
|
395
|
+
this.#handlePageBuilderNotPresent(passedComponentsArray)
|
|
396
|
+
}
|
|
397
|
+
// Page Builder is Present in the DOM & Components have been passed to the Builder
|
|
398
|
+
if (pagebuilder) {
|
|
399
|
+
this.#completeBuilderInitialization(passedComponentsArray)
|
|
400
|
+
}
|
|
227
401
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
402
|
+
// Return both the success message and validation info if present
|
|
403
|
+
return {
|
|
404
|
+
message: 'Page builder started successfully.',
|
|
405
|
+
...(validation || {}),
|
|
406
|
+
}
|
|
407
|
+
} catch (err) {
|
|
408
|
+
console.error('Not able to start the Page Builder', err)
|
|
409
|
+
this.pageBuilderStateStore.setIsLoadingGlobal(false)
|
|
410
|
+
return {
|
|
411
|
+
error: true as const,
|
|
412
|
+
reason: 'Failed to start the Page Builder due to an unexpected error.',
|
|
413
|
+
}
|
|
231
414
|
}
|
|
232
415
|
}
|
|
233
416
|
|
|
234
|
-
async completeBuilderInitialization() {
|
|
235
|
-
|
|
236
|
-
|
|
417
|
+
async #completeBuilderInitialization(passedComponentsArray?: BuilderResourceData): Promise<void> {
|
|
418
|
+
this.pageBuilderStateStore.setIsLoadingGlobal(true)
|
|
419
|
+
const localStorageData = this.loadStoredComponentsFromStorage()
|
|
420
|
+
|
|
421
|
+
await this.delay(300)
|
|
237
422
|
|
|
238
423
|
// Deselect any selected or hovered elements in the builder UI
|
|
239
424
|
await this.clearHtmlSelection()
|
|
240
|
-
this.pageBuilderStateStore.setIsLoadingGlobal(true)
|
|
241
|
-
await this.delay(300)
|
|
242
425
|
|
|
243
|
-
|
|
244
|
-
|
|
426
|
+
if (passedComponentsArray) {
|
|
427
|
+
// Prefer components from local storage if available for this resource
|
|
428
|
+
if (!this.pendingMountData && localStorageData && typeof localStorageData === 'string') {
|
|
429
|
+
await this.#updateComponentsFromString(localStorageData)
|
|
430
|
+
} else {
|
|
431
|
+
// If no local storage is found, use the components array provided by the user
|
|
432
|
+
await this.#mountPassedComponentsToDOM(passedComponentsArray)
|
|
433
|
+
this.pendingMountData = null
|
|
434
|
+
}
|
|
435
|
+
}
|
|
245
436
|
|
|
246
|
-
|
|
247
|
-
|
|
437
|
+
//
|
|
438
|
+
//
|
|
439
|
+
//
|
|
440
|
+
if (!passedComponentsArray) {
|
|
441
|
+
// Prefer components from local storage if available for this resource
|
|
442
|
+
if (localStorageData && typeof localStorageData === 'string') {
|
|
443
|
+
await this.#updateComponentsFromString(localStorageData)
|
|
444
|
+
} else {
|
|
445
|
+
// If no local storage is found, use the components array provided by the user
|
|
446
|
+
await this.#mountPassedComponentsToDOM([])
|
|
447
|
+
}
|
|
248
448
|
}
|
|
449
|
+
//
|
|
450
|
+
//
|
|
451
|
+
//
|
|
452
|
+
//
|
|
453
|
+
//
|
|
454
|
+
//
|
|
455
|
+
//
|
|
456
|
+
//
|
|
457
|
+
//
|
|
458
|
+
//
|
|
459
|
+
//
|
|
460
|
+
//
|
|
461
|
+
//
|
|
249
462
|
|
|
250
463
|
// Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
|
|
251
464
|
await nextTick()
|
|
252
465
|
// Attach event listeners to all editable elements in the Builder
|
|
253
466
|
await this.#addListenersToEditableElements()
|
|
467
|
+
// Show a global loading indicator while initializing
|
|
468
|
+
this.pageBuilderStateStore.setIsLoadingGlobal(false)
|
|
254
469
|
|
|
255
470
|
// Clean up any old localStorage items related to previous builder sessions
|
|
256
471
|
this.deleteOldPageBuilderLocalStorage()
|
|
@@ -437,8 +652,8 @@ export class PageBuilderService {
|
|
|
437
652
|
this.pageBuilderStateStore.setIsSaving(true)
|
|
438
653
|
// Deselect any selected or hovered elements in the builder UI
|
|
439
654
|
//
|
|
440
|
-
|
|
441
|
-
await this.delay(
|
|
655
|
+
this.#saveDomComponentsToLocalStorage()
|
|
656
|
+
await this.delay(300)
|
|
442
657
|
} catch (err) {
|
|
443
658
|
console.error('Error trying auto save.', err)
|
|
444
659
|
} finally {
|
|
@@ -449,7 +664,7 @@ export class PageBuilderService {
|
|
|
449
664
|
if (passedConfig && !passedConfig.userSettings) {
|
|
450
665
|
try {
|
|
451
666
|
this.pageBuilderStateStore.setIsSaving(true)
|
|
452
|
-
|
|
667
|
+
this.#saveDomComponentsToLocalStorage()
|
|
453
668
|
await this.delay(300)
|
|
454
669
|
} catch (err) {
|
|
455
670
|
console.error('Error trying saving.', err)
|
|
@@ -475,7 +690,7 @@ export class PageBuilderService {
|
|
|
475
690
|
passedConfig.userSettings.autoSave)
|
|
476
691
|
) {
|
|
477
692
|
this.pageBuilderStateStore.setIsSaving(true)
|
|
478
|
-
|
|
693
|
+
this.#saveDomComponentsToLocalStorage()
|
|
479
694
|
await this.delay(300)
|
|
480
695
|
|
|
481
696
|
this.pageBuilderStateStore.setIsSaving(false)
|
|
@@ -483,7 +698,7 @@ export class PageBuilderService {
|
|
|
483
698
|
}
|
|
484
699
|
if (passedConfig && !passedConfig.userSettings) {
|
|
485
700
|
this.pageBuilderStateStore.setIsSaving(true)
|
|
486
|
-
|
|
701
|
+
this.#saveDomComponentsToLocalStorage()
|
|
487
702
|
await this.delay(300)
|
|
488
703
|
|
|
489
704
|
this.pageBuilderStateStore.setIsSaving(false)
|
|
@@ -494,15 +709,16 @@ export class PageBuilderService {
|
|
|
494
709
|
// Deep clone clone component
|
|
495
710
|
const clonedComponent = { ...componentObject }
|
|
496
711
|
|
|
712
|
+
const pageBuilder = document.querySelector('#contains-pagebuilder')
|
|
497
713
|
// scoll to top or bottom # end
|
|
498
|
-
if (
|
|
714
|
+
if (pageBuilder) {
|
|
499
715
|
if (
|
|
500
716
|
this.getComponentArrayAddMethod.value === 'unshift' ||
|
|
501
717
|
this.getComponentArrayAddMethod.value === 'push'
|
|
502
718
|
) {
|
|
503
719
|
// push to top
|
|
504
720
|
if (this.getComponentArrayAddMethod.value === 'unshift') {
|
|
505
|
-
|
|
721
|
+
pageBuilder.scrollTo({
|
|
506
722
|
top: 0,
|
|
507
723
|
behavior: 'smooth',
|
|
508
724
|
})
|
|
@@ -510,9 +726,8 @@ export class PageBuilderService {
|
|
|
510
726
|
|
|
511
727
|
// push to bottom
|
|
512
728
|
if (this.getComponentArrayAddMethod.value === 'push') {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
top: maxHeight,
|
|
729
|
+
pageBuilder.scrollTo({
|
|
730
|
+
top: 0,
|
|
516
731
|
behavior: 'smooth',
|
|
517
732
|
})
|
|
518
733
|
}
|
|
@@ -606,80 +821,6 @@ export class PageBuilderService {
|
|
|
606
821
|
}
|
|
607
822
|
}
|
|
608
823
|
|
|
609
|
-
handleRemoveClasses(userSelectedClass: string): void {
|
|
610
|
-
// remove selected class from element
|
|
611
|
-
if (this.getElement.value?.classList.contains(userSelectedClass)) {
|
|
612
|
-
this.getElement.value?.classList.remove(userSelectedClass)
|
|
613
|
-
|
|
614
|
-
this.pageBuilderStateStore.setElement(this.getElement.value)
|
|
615
|
-
this.pageBuilderStateStore.removeClass(userSelectedClass)
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
handleDeleteElement() {
|
|
620
|
-
// Get the element to be deleted
|
|
621
|
-
const element = this.getElement.value
|
|
622
|
-
|
|
623
|
-
if (!element) return
|
|
624
|
-
|
|
625
|
-
if (!element?.parentNode) {
|
|
626
|
-
this.pageBuilderStateStore.setComponent(null)
|
|
627
|
-
this.pageBuilderStateStore.setElement(null)
|
|
628
|
-
return
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// Store the parent of the deleted element
|
|
632
|
-
// if parent element tag is section remove the hole component
|
|
633
|
-
if (element.parentElement?.tagName !== 'SECTION') {
|
|
634
|
-
this.pageBuilderStateStore.setParentElement(element.parentNode as HTMLElement)
|
|
635
|
-
|
|
636
|
-
// Store the outerHTML of the deleted element
|
|
637
|
-
this.pageBuilderStateStore.setRestoredElement(element.outerHTML)
|
|
638
|
-
|
|
639
|
-
// Store the next sibling of the deleted element
|
|
640
|
-
this.pageBuilderStateStore.setNextSibling(element.nextSibling as HTMLElement | null)
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// if parent element tag is section remove the hole component
|
|
644
|
-
if (element.parentElement?.tagName === 'SECTION') {
|
|
645
|
-
this.deleteSelectedComponent()
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// Remove the element from the DOM
|
|
649
|
-
element.remove()
|
|
650
|
-
this.pageBuilderStateStore.setComponent(null)
|
|
651
|
-
this.pageBuilderStateStore.setElement(null)
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
async handleRestoreElement() {
|
|
655
|
-
// Get the stored deleted element and its parent
|
|
656
|
-
if (this.getRestoredElement.value && this.getParentElement.value) {
|
|
657
|
-
// Create a new element from the stored outerHTML
|
|
658
|
-
const newElement = document.createElement('div')
|
|
659
|
-
// Fixed type conversion issue
|
|
660
|
-
newElement.innerHTML = this.getRestoredElement.value
|
|
661
|
-
|
|
662
|
-
// Append the restored element to its parent
|
|
663
|
-
// Insert the restored element before its next sibling in its parent
|
|
664
|
-
if (newElement.firstChild && this.getParentElement.value) {
|
|
665
|
-
// insertBefore can accept null as second parameter (will append to end)
|
|
666
|
-
this.getParentElement.value.insertBefore(newElement.firstChild, this.getNextSibling.value)
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// Clear
|
|
671
|
-
this.pageBuilderStateStore.setParentElement(null)
|
|
672
|
-
this.pageBuilderStateStore.setRestoredElement(null)
|
|
673
|
-
this.pageBuilderStateStore.setNextSibling(null)
|
|
674
|
-
this.pageBuilderStateStore.setComponent(null)
|
|
675
|
-
this.pageBuilderStateStore.setElement(null)
|
|
676
|
-
|
|
677
|
-
// Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
|
|
678
|
-
await nextTick()
|
|
679
|
-
// Attach event listeners to all editable elements in the Builder
|
|
680
|
-
await this.#addListenersToEditableElements()
|
|
681
|
-
}
|
|
682
|
-
|
|
683
824
|
handleFontWeight(userSelectedFontWeight?: string): void {
|
|
684
825
|
this.#applyElementClassChanges(
|
|
685
826
|
userSelectedFontWeight,
|
|
@@ -840,15 +981,40 @@ export class PageBuilderService {
|
|
|
840
981
|
this.#applyElementClassChanges(opacity, tailwindOpacities.opacities, 'setOpacity')
|
|
841
982
|
}
|
|
842
983
|
|
|
843
|
-
|
|
984
|
+
/**
|
|
985
|
+
* Removes all components from both the builder state and the DOM.
|
|
986
|
+
*
|
|
987
|
+
* - First clears the components array in the store.
|
|
988
|
+
* - Then, as a defensive measure, also manually removes all sections elements from the DOM.
|
|
989
|
+
*
|
|
990
|
+
* This manual DOM clearing is only optional
|
|
991
|
+
*
|
|
992
|
+
*/
|
|
993
|
+
|
|
994
|
+
deleteAllComponentsFromDOM() {
|
|
995
|
+
// Clear the store
|
|
844
996
|
this.pageBuilderStateStore.setComponents([])
|
|
997
|
+
|
|
998
|
+
// Also clear the DOM
|
|
999
|
+
const pagebuilder = document.querySelector('#pagebuilder')
|
|
1000
|
+
if (pagebuilder) {
|
|
1001
|
+
// Remove all section elements (assuming each component is a <section>)
|
|
1002
|
+
pagebuilder
|
|
1003
|
+
.querySelectorAll('section[data-componentid]')
|
|
1004
|
+
.forEach((section) => section.remove())
|
|
1005
|
+
}
|
|
845
1006
|
}
|
|
846
1007
|
|
|
847
|
-
async
|
|
848
|
-
|
|
1008
|
+
async deleteComponentFromDOM() {
|
|
1009
|
+
this.#syncDomToStoreOnly()
|
|
1010
|
+
await nextTick()
|
|
1011
|
+
|
|
1012
|
+
const components = this.getComponents.value
|
|
1013
|
+
|
|
1014
|
+
if (!components) return
|
|
849
1015
|
|
|
850
1016
|
// Find the index of the component to delete
|
|
851
|
-
const indexToDelete =
|
|
1017
|
+
const indexToDelete = components.findIndex(
|
|
852
1018
|
(component) => component.id === this.getComponent.value?.id,
|
|
853
1019
|
)
|
|
854
1020
|
|
|
@@ -857,17 +1023,99 @@ export class PageBuilderService {
|
|
|
857
1023
|
return
|
|
858
1024
|
}
|
|
859
1025
|
|
|
860
|
-
//
|
|
861
|
-
|
|
862
|
-
|
|
1026
|
+
// Create a new array without the deleted component (avoid mutating original array)
|
|
1027
|
+
const newComponents = [
|
|
1028
|
+
...components.slice(0, indexToDelete),
|
|
1029
|
+
...components.slice(indexToDelete + 1),
|
|
1030
|
+
]
|
|
863
1031
|
|
|
864
|
-
|
|
1032
|
+
this.pageBuilderStateStore.setComponents(newComponents)
|
|
1033
|
+
|
|
1034
|
+
// Remove the section from the DOM as well
|
|
1035
|
+
const pagebuilder = document.querySelector('#pagebuilder')
|
|
1036
|
+
if (pagebuilder && this.getComponent.value?.id) {
|
|
1037
|
+
const section = pagebuilder.querySelector(
|
|
1038
|
+
`section[data-componentid="${this.getComponent.value.id}"]`,
|
|
1039
|
+
)
|
|
1040
|
+
if (section) section.remove()
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// Wait for Vue to finish DOM updates before attaching event listeners.
|
|
865
1044
|
await nextTick()
|
|
866
|
-
// Attach event listeners to all editable elements in the Builder
|
|
867
1045
|
await this.#addListenersToEditableElements()
|
|
868
1046
|
|
|
869
1047
|
this.pageBuilderStateStore.setComponent(null)
|
|
870
1048
|
this.pageBuilderStateStore.setElement(null)
|
|
1049
|
+
|
|
1050
|
+
// Optional: auto-save after deletion
|
|
1051
|
+
await this.handleAutoSave()
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
async deleteElementFromDOM() {
|
|
1055
|
+
const element = this.getElement.value
|
|
1056
|
+
if (!element) return
|
|
1057
|
+
|
|
1058
|
+
if (!element.parentNode) {
|
|
1059
|
+
this.pageBuilderStateStore.setComponent(null)
|
|
1060
|
+
this.pageBuilderStateStore.setElement(null)
|
|
1061
|
+
return
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// If the element is inside a section, but not the section itself, store info for undo/restore
|
|
1065
|
+
if (element.parentElement?.tagName !== 'SECTION') {
|
|
1066
|
+
this.pageBuilderStateStore.setParentElement(element.parentNode as HTMLElement)
|
|
1067
|
+
this.pageBuilderStateStore.setRestoredElement(element.outerHTML)
|
|
1068
|
+
this.pageBuilderStateStore.setNextSibling(element.nextSibling as HTMLElement | null)
|
|
1069
|
+
// Remove only the element itself from the DOM
|
|
1070
|
+
element.remove()
|
|
1071
|
+
} else {
|
|
1072
|
+
// If the element's parent is a section, remove the whole component (section)
|
|
1073
|
+
await this.deleteComponentFromDOM()
|
|
1074
|
+
// No need to call element.remove() here, as the section is already removed
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// Clear selection state
|
|
1078
|
+
this.pageBuilderStateStore.setComponent(null)
|
|
1079
|
+
this.pageBuilderStateStore.setElement(null)
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
async restoreDeletedElementToDOM() {
|
|
1083
|
+
// Restore the previously deleted element to the DOM
|
|
1084
|
+
const restoredHTML = this.getRestoredElement.value
|
|
1085
|
+
const parent = this.getParentElement.value
|
|
1086
|
+
const nextSibling = this.getNextSibling.value
|
|
1087
|
+
|
|
1088
|
+
if (restoredHTML && parent) {
|
|
1089
|
+
// Create a container and parse the HTML
|
|
1090
|
+
const container = document.createElement('div')
|
|
1091
|
+
container.innerHTML = restoredHTML
|
|
1092
|
+
|
|
1093
|
+
// Insert the restored element before its next sibling (or append if null)
|
|
1094
|
+
if (container.firstChild) {
|
|
1095
|
+
parent.insertBefore(container.firstChild, nextSibling)
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// Clear restore-related state
|
|
1100
|
+
this.pageBuilderStateStore.setParentElement(null)
|
|
1101
|
+
this.pageBuilderStateStore.setRestoredElement(null)
|
|
1102
|
+
this.pageBuilderStateStore.setNextSibling(null)
|
|
1103
|
+
this.pageBuilderStateStore.setComponent(null)
|
|
1104
|
+
this.pageBuilderStateStore.setElement(null)
|
|
1105
|
+
|
|
1106
|
+
// Wait for Vue to finish DOM updates before attaching event listeners
|
|
1107
|
+
await nextTick()
|
|
1108
|
+
await this.#addListenersToEditableElements()
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
handleRemoveClasses(userSelectedClass: string): void {
|
|
1112
|
+
// remove selected class from element
|
|
1113
|
+
if (this.getElement.value?.classList.contains(userSelectedClass)) {
|
|
1114
|
+
this.getElement.value?.classList.remove(userSelectedClass)
|
|
1115
|
+
|
|
1116
|
+
this.pageBuilderStateStore.setElement(this.getElement.value)
|
|
1117
|
+
this.pageBuilderStateStore.removeClass(userSelectedClass)
|
|
1118
|
+
}
|
|
871
1119
|
}
|
|
872
1120
|
|
|
873
1121
|
// move component
|
|
@@ -932,7 +1180,7 @@ export class PageBuilderService {
|
|
|
932
1180
|
}
|
|
933
1181
|
|
|
934
1182
|
if (typeof this.getElement.value.innerHTML === 'string') {
|
|
935
|
-
await
|
|
1183
|
+
await nextTick()
|
|
936
1184
|
|
|
937
1185
|
// Update text content
|
|
938
1186
|
this.getElement.value.textContent = textContentVueModel
|
|
@@ -1038,21 +1286,25 @@ export class PageBuilderService {
|
|
|
1038
1286
|
}
|
|
1039
1287
|
|
|
1040
1288
|
#updateLocalStorageItemName(): void {
|
|
1041
|
-
const
|
|
1289
|
+
const formtype =
|
|
1042
1290
|
this.pageBuilderStateStore.getPageBuilderConfig &&
|
|
1043
1291
|
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
|
|
1044
1292
|
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType
|
|
1045
1293
|
|
|
1046
|
-
const
|
|
1294
|
+
const formname =
|
|
1295
|
+
this.pageBuilderStateStore.getPageBuilderConfig &&
|
|
1296
|
+
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
|
|
1297
|
+
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formName
|
|
1047
1298
|
|
|
1048
|
-
const
|
|
1049
|
-
this.pageBuilderStateStore.getPageBuilderConfig
|
|
1299
|
+
const resourceData =
|
|
1300
|
+
this.pageBuilderStateStore.getPageBuilderConfig &&
|
|
1301
|
+
this.pageBuilderStateStore.getPageBuilderConfig.resourceData
|
|
1050
1302
|
|
|
1051
1303
|
// Logic for create resource
|
|
1052
|
-
if (
|
|
1053
|
-
if (
|
|
1304
|
+
if (formtype === 'create') {
|
|
1305
|
+
if (formname && formname.length > 0) {
|
|
1054
1306
|
this.pageBuilderStateStore.setLocalStorageItemName(
|
|
1055
|
-
`page-builder-create-resource-${this.sanitizeForLocalStorage(
|
|
1307
|
+
`page-builder-create-resource-${this.sanitizeForLocalStorage(formname)}`,
|
|
1056
1308
|
)
|
|
1057
1309
|
return
|
|
1058
1310
|
}
|
|
@@ -1063,15 +1315,15 @@ export class PageBuilderService {
|
|
|
1063
1315
|
|
|
1064
1316
|
// Logic for create
|
|
1065
1317
|
// Logic for update and with resource form name
|
|
1066
|
-
if (
|
|
1067
|
-
if (typeof
|
|
1318
|
+
if (formtype === 'update') {
|
|
1319
|
+
if (typeof formname === 'string' && formname.length > 0) {
|
|
1068
1320
|
//
|
|
1069
1321
|
//
|
|
1070
1322
|
if (resourceData && resourceData != null && !resourceData.title) {
|
|
1071
1323
|
// Check if id is missing, null, undefined, or an empty string (after trimming)
|
|
1072
1324
|
if (!resourceData.id || typeof resourceData.id === 'string') {
|
|
1073
1325
|
this.pageBuilderStateStore.setLocalStorageItemName(
|
|
1074
|
-
`page-builder-update-resource-${this.sanitizeForLocalStorage(
|
|
1326
|
+
`page-builder-update-resource-${this.sanitizeForLocalStorage(formname)}`,
|
|
1075
1327
|
)
|
|
1076
1328
|
return
|
|
1077
1329
|
}
|
|
@@ -1086,7 +1338,7 @@ export class PageBuilderService {
|
|
|
1086
1338
|
) {
|
|
1087
1339
|
if (!resourceData.id || typeof resourceData.id === 'string') {
|
|
1088
1340
|
this.pageBuilderStateStore.setLocalStorageItemName(
|
|
1089
|
-
`page-builder-update-resource-${this.sanitizeForLocalStorage(
|
|
1341
|
+
`page-builder-update-resource-${this.sanitizeForLocalStorage(formname)}-${this.sanitizeForLocalStorage(resourceData.title)}`,
|
|
1090
1342
|
)
|
|
1091
1343
|
return
|
|
1092
1344
|
}
|
|
@@ -1098,7 +1350,7 @@ export class PageBuilderService {
|
|
|
1098
1350
|
if (!resourceData.title && typeof resourceData.title !== 'string') {
|
|
1099
1351
|
if (resourceData.id || typeof resourceData.id === 'number') {
|
|
1100
1352
|
this.pageBuilderStateStore.setLocalStorageItemName(
|
|
1101
|
-
`page-builder-update-resource-${this.sanitizeForLocalStorage(
|
|
1353
|
+
`page-builder-update-resource-${this.sanitizeForLocalStorage(formname)}-${this.sanitizeForLocalStorage(String(resourceData.id))}`,
|
|
1102
1354
|
)
|
|
1103
1355
|
return
|
|
1104
1356
|
}
|
|
@@ -1114,7 +1366,7 @@ export class PageBuilderService {
|
|
|
1114
1366
|
) {
|
|
1115
1367
|
if (resourceData.id || typeof resourceData.id === 'number') {
|
|
1116
1368
|
this.pageBuilderStateStore.setLocalStorageItemName(
|
|
1117
|
-
`page-builder-update-resource-${this.sanitizeForLocalStorage(
|
|
1369
|
+
`page-builder-update-resource-${this.sanitizeForLocalStorage(formname)}-${this.sanitizeForLocalStorage(resourceData.title)}-${this.sanitizeForLocalStorage(String(resourceData.id))}`,
|
|
1118
1370
|
)
|
|
1119
1371
|
return
|
|
1120
1372
|
}
|
|
@@ -1122,11 +1374,8 @@ export class PageBuilderService {
|
|
|
1122
1374
|
}
|
|
1123
1375
|
}
|
|
1124
1376
|
|
|
1125
|
-
// Logic for update without without
|
|
1126
|
-
if (
|
|
1127
|
-
!resourceFormName ||
|
|
1128
|
-
(typeof resourceFormName === 'string' && resourceFormName.length === 0)
|
|
1129
|
-
) {
|
|
1377
|
+
// Logic for update without without formname
|
|
1378
|
+
if (!formname || (typeof formname === 'string' && formname.length === 0)) {
|
|
1130
1379
|
//
|
|
1131
1380
|
//
|
|
1132
1381
|
if (resourceData && resourceData != null && !resourceData.title) {
|
|
@@ -1204,11 +1453,31 @@ export class PageBuilderService {
|
|
|
1204
1453
|
}
|
|
1205
1454
|
|
|
1206
1455
|
/**
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
1209
|
-
|
|
1456
|
+
* Syncs the current DOM state into the in-memory store (getComponents),
|
|
1457
|
+
* but does NOT save to localStorage.
|
|
1458
|
+
*/
|
|
1459
|
+
#syncDomToStoreOnly() {
|
|
1460
|
+
const pagebuilder = document.querySelector('#pagebuilder')
|
|
1461
|
+
if (!pagebuilder) return
|
|
1462
|
+
|
|
1463
|
+
const componentsToSave: { html_code: string; id: string | null; title: string }[] = []
|
|
1464
|
+
|
|
1465
|
+
pagebuilder.querySelectorAll('section[data-componentid]').forEach((section) => {
|
|
1466
|
+
const sanitizedSection = this.#cloneAndRemoveSelectionAttributes(section as HTMLElement)
|
|
1467
|
+
componentsToSave.push({
|
|
1468
|
+
html_code: sanitizedSection.outerHTML,
|
|
1469
|
+
id: sanitizedSection.getAttribute('data-componentid'),
|
|
1470
|
+
title: sanitizedSection.getAttribute('data-component-title') || 'Untitled Component',
|
|
1471
|
+
})
|
|
1472
|
+
})
|
|
1473
|
+
|
|
1474
|
+
this.pageBuilderStateStore.setComponents(componentsToSave)
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* Saves the current DOM state (components) to localStorage.
|
|
1210
1479
|
*/
|
|
1211
|
-
#
|
|
1480
|
+
#saveDomComponentsToLocalStorage() {
|
|
1212
1481
|
const pagebuilder = document.querySelector('#pagebuilder')
|
|
1213
1482
|
if (!pagebuilder) return
|
|
1214
1483
|
|
|
@@ -1240,34 +1509,23 @@ export class PageBuilderService {
|
|
|
1240
1509
|
if (keyForSavingFromDomToLocal && typeof keyForSavingFromDomToLocal === 'string') {
|
|
1241
1510
|
localStorage.setItem(keyForSavingFromDomToLocal, JSON.stringify(dataToSave))
|
|
1242
1511
|
}
|
|
1243
|
-
|
|
1244
|
-
// No DOM mutation here!
|
|
1245
|
-
await new Promise<void>((resolve) => resolve())
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
// save to local storage
|
|
1249
|
-
async saveComponentsLocalStorage() {
|
|
1250
|
-
await this.nextTick
|
|
1251
|
-
|
|
1252
|
-
await this.#domToComponentsSync()
|
|
1253
1512
|
}
|
|
1254
1513
|
|
|
1255
|
-
async
|
|
1256
|
-
await
|
|
1514
|
+
async removeCurrentComponentsFromLocalStorage() {
|
|
1515
|
+
await nextTick()
|
|
1257
1516
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1517
|
+
const key = this.getLocalStorageItemName.value
|
|
1518
|
+
if (key) {
|
|
1519
|
+
localStorage.removeItem(key)
|
|
1260
1520
|
}
|
|
1261
1521
|
}
|
|
1262
1522
|
|
|
1263
1523
|
//
|
|
1264
1524
|
deleteOldPageBuilderLocalStorage(): void {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'update'
|
|
1270
|
-
) {
|
|
1525
|
+
const config = this.pageBuilderStateStore.getPageBuilderConfig
|
|
1526
|
+
const formType = config && config.updateOrCreate && config.updateOrCreate.formType
|
|
1527
|
+
|
|
1528
|
+
if (formType === 'update') {
|
|
1271
1529
|
let oldCountLocalStorages = 0
|
|
1272
1530
|
const deletedItemsLog: { Number: number; Key: string; SavedAt: string }[] = []
|
|
1273
1531
|
|
|
@@ -1315,38 +1573,6 @@ export class PageBuilderService {
|
|
|
1315
1573
|
}
|
|
1316
1574
|
}
|
|
1317
1575
|
|
|
1318
|
-
async hasLocalDraftForUpdate(): Promise<boolean> {
|
|
1319
|
-
const pagebuilder = document.querySelector('#pagebuilder')
|
|
1320
|
-
if (!pagebuilder) {
|
|
1321
|
-
return true
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
if (this.hasStartedEditing) return false
|
|
1325
|
-
|
|
1326
|
-
if (
|
|
1327
|
-
this.pageBuilderStateStore.getPageBuilderConfig &&
|
|
1328
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
|
|
1329
|
-
typeof this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'string' &&
|
|
1330
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'update'
|
|
1331
|
-
) {
|
|
1332
|
-
const key = this.getLocalStorageItemName.value
|
|
1333
|
-
if (typeof key === 'string') {
|
|
1334
|
-
const draft = localStorage.getItem(key)
|
|
1335
|
-
if (draft) {
|
|
1336
|
-
try {
|
|
1337
|
-
await this.delay(500)
|
|
1338
|
-
this.pageBuilderStateStore.setHasLocalDraftForUpdate(false)
|
|
1339
|
-
return true
|
|
1340
|
-
} catch (err) {
|
|
1341
|
-
console.error('Unable to mount components to DOM.', err)
|
|
1342
|
-
return false
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
return false
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
1576
|
// Call this when the user starts editing (e.g., on first change or when resuming a draft)
|
|
1351
1577
|
startEditing() {
|
|
1352
1578
|
this.hasStartedEditing = true
|
|
@@ -1354,40 +1580,48 @@ export class PageBuilderService {
|
|
|
1354
1580
|
|
|
1355
1581
|
//
|
|
1356
1582
|
async resumeEditingForUpdate() {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
|
|
1360
|
-
typeof this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'string' &&
|
|
1361
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'update'
|
|
1362
|
-
) {
|
|
1363
|
-
const key = this.getLocalStorageItemName.value
|
|
1583
|
+
const config = this.pageBuilderStateStore.getPageBuilderConfig
|
|
1584
|
+
const formType = config && config.updateOrCreate && config.updateOrCreate.formType
|
|
1364
1585
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1586
|
+
if (formType !== 'update') return
|
|
1587
|
+
//
|
|
1588
|
+
//
|
|
1589
|
+
//
|
|
1367
1590
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1591
|
+
const key = this.getLocalStorageItemName.value
|
|
1592
|
+
|
|
1593
|
+
if (typeof key === 'string') {
|
|
1594
|
+
const updateDraftFromLocalStorage = localStorage.getItem(key)
|
|
1595
|
+
|
|
1596
|
+
if (typeof updateDraftFromLocalStorage === 'string') {
|
|
1597
|
+
this.pageBuilderStateStore.setIsLoadingResumeEditing(true)
|
|
1598
|
+
localStorage.removeItem(key)
|
|
1599
|
+
await delay(300)
|
|
1600
|
+
await this.#updateComponentsFromString(updateDraftFromLocalStorage)
|
|
1601
|
+
this.pageBuilderStateStore.setIsLoadingResumeEditing(false)
|
|
1374
1602
|
}
|
|
1375
1603
|
}
|
|
1604
|
+
|
|
1605
|
+
// Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
|
|
1606
|
+
await nextTick()
|
|
1607
|
+
// Attach event listeners to all editable elements in the Builder
|
|
1608
|
+
await this.#addListenersToEditableElements()
|
|
1609
|
+
// set loading to false
|
|
1610
|
+
this.pageBuilderStateStore.setIsLoadingResumeEditing(false)
|
|
1376
1611
|
}
|
|
1377
1612
|
|
|
1378
1613
|
async restoreOriginalContent() {
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'update'
|
|
1384
|
-
) {
|
|
1614
|
+
const config = this.pageBuilderStateStore.getPageBuilderConfig
|
|
1615
|
+
const formType = config && config.updateOrCreate && config.updateOrCreate.formType
|
|
1616
|
+
|
|
1617
|
+
if (formType === 'update') {
|
|
1385
1618
|
this.pageBuilderStateStore.setIsRestoring(true)
|
|
1386
1619
|
await this.delay(300)
|
|
1387
1620
|
|
|
1388
1621
|
// Restore the original content if available
|
|
1389
|
-
if (this.originalComponents) {
|
|
1390
|
-
this
|
|
1622
|
+
if (Array.isArray(this.originalComponents)) {
|
|
1623
|
+
await this.#mountPassedComponentsToDOM(this.originalComponents)
|
|
1624
|
+
this.removeCurrentComponentsFromLocalStorage()
|
|
1391
1625
|
}
|
|
1392
1626
|
|
|
1393
1627
|
// Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
|
|
@@ -1441,7 +1675,7 @@ export class PageBuilderService {
|
|
|
1441
1675
|
|
|
1442
1676
|
// Only apply if an image is staged
|
|
1443
1677
|
if (this.getApplyImageToSelection.value && this.getApplyImageToSelection.value.src) {
|
|
1444
|
-
await
|
|
1678
|
+
await nextTick()
|
|
1445
1679
|
this.pageBuilderStateStore.setBasePrimaryImage(`${this.getApplyImageToSelection.value.src}`)
|
|
1446
1680
|
|
|
1447
1681
|
await this.handleAutoSave()
|
|
@@ -1698,19 +1932,22 @@ export class PageBuilderService {
|
|
|
1698
1932
|
* @param data - JSON string (e.g., '[{"html_code":"...","id":"123","title":"..."}]')
|
|
1699
1933
|
* OR HTML string (e.g., '<section data-componentid="123">...</section>')
|
|
1700
1934
|
*/
|
|
1701
|
-
async #
|
|
1935
|
+
async #updateComponentsFromString(htmlString: string): Promise<void> {
|
|
1702
1936
|
// Auto-detect if input is JSON or HTML
|
|
1703
1937
|
const trimmedData = htmlString.trim()
|
|
1704
1938
|
|
|
1705
1939
|
if (trimmedData.startsWith('[') || trimmedData.startsWith('{')) {
|
|
1706
1940
|
// Looks like JSON - parse as JSON
|
|
1707
1941
|
await this.#parseJSONComponents(trimmedData)
|
|
1708
|
-
|
|
1942
|
+
return
|
|
1943
|
+
}
|
|
1944
|
+
if (trimmedData.startsWith('<')) {
|
|
1709
1945
|
// Looks like HTML - parse as HTML
|
|
1710
1946
|
await this.#parseHTMLComponents(trimmedData)
|
|
1711
|
-
|
|
1712
|
-
await this.#parseJSONComponents(trimmedData)
|
|
1947
|
+
return
|
|
1713
1948
|
}
|
|
1949
|
+
|
|
1950
|
+
await this.#parseJSONComponents(trimmedData)
|
|
1714
1951
|
}
|
|
1715
1952
|
|
|
1716
1953
|
// Private method to parse JSON components and save pageBuilderContentSavedAt to localStorage
|
|
@@ -1772,7 +2009,7 @@ export class PageBuilderService {
|
|
|
1772
2009
|
await this.#addListenersToEditableElements()
|
|
1773
2010
|
} catch (error) {
|
|
1774
2011
|
console.error('Error parsing JSON components:', error)
|
|
1775
|
-
this.
|
|
2012
|
+
this.deleteAllComponentsFromDOM()
|
|
1776
2013
|
}
|
|
1777
2014
|
}
|
|
1778
2015
|
// Private method to parse HTML components
|
|
@@ -1827,111 +2064,7 @@ export class PageBuilderService {
|
|
|
1827
2064
|
await this.#addListenersToEditableElements()
|
|
1828
2065
|
} catch (error) {
|
|
1829
2066
|
console.error('Error parsing HTML components:', error)
|
|
1830
|
-
this.
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
|
|
1834
|
-
/**
|
|
1835
|
-
* Mount Components to DOM
|
|
1836
|
-
* @param passedData - HTML/JSON string to inject (optional)
|
|
1837
|
-
* @param preferLocalStorage - if true, always try localStorage first
|
|
1838
|
-
*/
|
|
1839
|
-
async mountComponentsToDOM(passedData: string): Promise<void> {
|
|
1840
|
-
const pagebuilder = document.querySelector('#pagebuilder')
|
|
1841
|
-
|
|
1842
|
-
// If #pagebuilder is not present, cache the data and exit
|
|
1843
|
-
if (!pagebuilder) {
|
|
1844
|
-
// For 'create', set pendingMountData to '' (empty string)
|
|
1845
|
-
const config = this.pageBuilderStateStore.getPageBuilderConfig
|
|
1846
|
-
const formType = config && config.updateOrCreate && config.updateOrCreate.formType
|
|
1847
|
-
if (formType === 'create') {
|
|
1848
|
-
this.pendingMountData = ''
|
|
1849
|
-
} else {
|
|
1850
|
-
this.pendingMountData = passedData
|
|
1851
|
-
}
|
|
1852
|
-
return
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
// Clear the cache if we are mounting now
|
|
1856
|
-
this.pendingMountData = null
|
|
1857
|
-
|
|
1858
|
-
this.pageBuilderStateStore.setComponents([])
|
|
1859
|
-
|
|
1860
|
-
// On from type update Save Original Post
|
|
1861
|
-
if (
|
|
1862
|
-
this.pageBuilderStateStore.getPageBuilderConfig &&
|
|
1863
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
|
|
1864
|
-
typeof this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'string' &&
|
|
1865
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'update' &&
|
|
1866
|
-
passedData &&
|
|
1867
|
-
!this.originalComponents
|
|
1868
|
-
) {
|
|
1869
|
-
this.originalComponents = passedData
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
|
-
// Form type Update
|
|
1873
|
-
if (
|
|
1874
|
-
this.pageBuilderStateStore.getPageBuilderConfig &&
|
|
1875
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
|
|
1876
|
-
typeof this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'string' &&
|
|
1877
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'update'
|
|
1878
|
-
) {
|
|
1879
|
-
if (passedData) {
|
|
1880
|
-
await this.#setComponentsFromData(passedData)
|
|
1881
|
-
return
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
|
|
1885
|
-
// Form type Create
|
|
1886
|
-
const localStorageData = this.loadStoredComponentsFromStorage()
|
|
1887
|
-
|
|
1888
|
-
if (
|
|
1889
|
-
this.pageBuilderStateStore.getPageBuilderConfig &&
|
|
1890
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate &&
|
|
1891
|
-
typeof this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'string' &&
|
|
1892
|
-
this.pageBuilderStateStore.getPageBuilderConfig.updateOrCreate.formType === 'create'
|
|
1893
|
-
) {
|
|
1894
|
-
if (localStorageData) {
|
|
1895
|
-
await this.#setComponentsFromData(localStorageData)
|
|
1896
|
-
return
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
// If no localStorage, but passedData exists (for demo), use it
|
|
1900
|
-
if (passedData) {
|
|
1901
|
-
await this.#setComponentsFromData(passedData)
|
|
1902
|
-
return
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
|
|
1907
|
-
async tryMountPendingData() {
|
|
1908
|
-
const pagebuilder = document.querySelector('#pagebuilder')
|
|
1909
|
-
|
|
1910
|
-
// Only run if #pagebuilder exists
|
|
1911
|
-
if (!pagebuilder) return
|
|
1912
|
-
|
|
1913
|
-
// If pendingMountData is a non-empty string (update or demo), always mount
|
|
1914
|
-
if (this.pendingMountData && typeof this.pendingMountData === 'string') {
|
|
1915
|
-
await this.mountComponentsToDOM(this.pendingMountData)
|
|
1916
|
-
this.pendingMountData = null
|
|
1917
|
-
this.completeBuilderInitialization()
|
|
1918
|
-
return
|
|
1919
|
-
}
|
|
1920
|
-
|
|
1921
|
-
// If pendingMountData is exactly '', and formType is 'create', and no components are mounted, mount for create
|
|
1922
|
-
const config = this.pageBuilderStateStore.getPageBuilderConfig
|
|
1923
|
-
const formType = config && config.updateOrCreate && config.updateOrCreate.formType
|
|
1924
|
-
const components = this.pageBuilderStateStore.getComponents
|
|
1925
|
-
|
|
1926
|
-
if (
|
|
1927
|
-
this.pendingMountData === '' &&
|
|
1928
|
-
formType === 'create' &&
|
|
1929
|
-
(!components || (Array.isArray(components) && components.length === 0))
|
|
1930
|
-
) {
|
|
1931
|
-
await this.mountComponentsToDOM('')
|
|
1932
|
-
this.pendingMountData = null
|
|
1933
|
-
this.completeBuilderInitialization()
|
|
1934
|
-
return
|
|
2067
|
+
this.deleteAllComponentsFromDOM()
|
|
1935
2068
|
}
|
|
1936
2069
|
}
|
|
1937
2070
|
|