@myissue/vue-website-page-builder 3.2.76 → 3.2.78

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myissue/vue-website-page-builder",
3
- "version": "v3.2.76",
3
+ "version": "v3.2.78",
4
4
  "description": "Vue 3 page builder component with drag & drop functionality.",
5
5
  "type": "module",
6
6
  "main": "./dist/vue-website-page-builder.umd.cjs",
@@ -17,6 +17,11 @@ defineProps({
17
17
  default: false,
18
18
  required: false,
19
19
  },
20
+ isLoading: {
21
+ type: Boolean,
22
+ default: false,
23
+ required: false,
24
+ },
20
25
  disabledWhichButton: {
21
26
  type: String,
22
27
  default: '',
@@ -97,7 +102,7 @@ const thirdButtonBuilder = function () {
97
102
  </div>
98
103
  </div>
99
104
 
100
- <template v-if="simpleModal !== true">
105
+ <template v-if="simpleModal !== true && !isLoading">
101
106
  <div class="py-4 flex sm:justify-end justify-center border-t border-gray-200 mt-4">
102
107
  <slot name="footer" />
103
108
  <div
@@ -362,6 +367,18 @@ const thirdButtonBuilder = function () {
362
367
  </div>
363
368
  </div>
364
369
  </template>
370
+ <template v-if="isLoading">
371
+ <div class="flex items-center my-2 py-4 px-2 justify-end">
372
+ <div
373
+ class="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]"
374
+ >
375
+ <span
376
+ class="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]"
377
+ >Loading...</span
378
+ >
379
+ </div>
380
+ </div>
381
+ </template>
365
382
  </ModalBuilder>
366
383
  </template>
367
384
 
@@ -71,7 +71,7 @@ const configPageBuilder = {
71
71
  image: '/jane_doe.jpg',
72
72
  },
73
73
  updateOrCreate: {
74
- formType: 'create' as 'create',
74
+ formType: 'update' as 'update',
75
75
  formName: 'news',
76
76
  },
77
77
  pageBuilderLogo: {
@@ -95,10 +95,10 @@ pageBuilderClass.setConfigPageBuilder(configPageBuilder)
95
95
 
96
96
  onMounted(async () => {
97
97
  if (typeof getLocalStorageItemName.value === 'string' && getLocalStorageItemName.value) {
98
- if (localStorage.getItem(getLocalStorageItemName.value)) {
99
- pageBuilderClass.loadExistingContent(
100
- JSON.stringify(localStorage.getItem(getLocalStorageItemName.value)),
101
- )
98
+ const value = localStorage.getItem(getLocalStorageItemName.value)
99
+
100
+ if (value) {
101
+ pageBuilderClass.loadExistingContent(JSON.stringify(value))
102
102
  } else {
103
103
  pageBuilderClass.loadExistingContent(JSON.stringify(html), true)
104
104
  // pageBuilderClass.loadExistingContent(rawHTML, true)
@@ -1,5 +1,4 @@
1
1
  {
2
- "savedAt": "2016-06-19T12:34:56.000Z",
3
2
  "components": [
4
3
  {
5
4
  "html_code": "<section><div class=\"relative py-4\"><div class=\"mx-auto max-w-7xl lg:px-4 px-2\"><div class=\"break-words\"><h2><strong>Demo Content</strong></h2></div></div></div></section>",
@@ -12,12 +12,13 @@ import { updateOrCreateIsFalsy } from '../helpers/passedPageBuilderConfig'
12
12
  import ToolbarOption from '../Components/PageBuilder/ToolbarOption/ToolbarOption.vue'
13
13
  import { delay } from '../composables/delay'
14
14
  import { useDebounce } from '../composables/useDebounce.ts'
15
+ import DynamicModalBuilder from '../Components/Modals/DynamicModalBuilder.vue'
15
16
 
16
17
  /**
17
18
  * Props for PageBuilder component
18
19
  * @typedef {Object} Props
19
20
  * @property {Object|null} CustomMediaLibraryComponent - Custom media component
20
- * @property {Object|null} CustomBuilderComponents - Custom search component
21
+ * @property {Object|null} CustomBuilderComponents - Custom component
21
22
  * @property {Object} configPageBuilder - Configuration object containing:
22
23
  */
23
24
  const props = defineProps({
@@ -192,6 +193,47 @@ watch(
192
193
  },
193
194
  { immediate: true },
194
195
  )
196
+ const gridColumnModalResumeEditing = ref(Number(1))
197
+ const typeModal = ref('')
198
+ const showModalResumeEditing = ref(false)
199
+ const titleModalResumeEditing = ref('')
200
+ const descriptionModalResumeEditing = ref('')
201
+ const firstButtonResumeEditing = ref('')
202
+ const secondButtonResumeEditing = ref(null)
203
+ const thirdButtonResumeEditing = ref(null)
204
+ const firstModalButtonResumeEditingFunction = ref(null)
205
+ const secondModalButtonResumeEditingFunction = ref(null)
206
+ const thirdModalButtonResumeEditingFunction = ref(null)
207
+
208
+ const isLoadingResumeEditing = ref(null)
209
+
210
+ const handlerRumeEditingForUpdate = async function () {
211
+ await pageBuilderClass.clearHtmlSelection()
212
+
213
+ typeModal.value = 'default'
214
+ titleModalResumeEditing.value = 'Continue Your Work?'
215
+ descriptionModalResumeEditing.value =
216
+ 'We noticed you have some changes that weren’t saved last time. Would you like to pick up where you left off, or use the version that’s currently saved?'
217
+ firstButtonResumeEditing.value = 'Use Saved Version'
218
+ secondButtonResumeEditing.value = null
219
+ thirdButtonResumeEditing.value = 'Continue Where I Left Off'
220
+ showModalResumeEditing.value = true
221
+
222
+ firstModalButtonResumeEditingFunction.value = function () {
223
+ showModalResumeEditing.value = false
224
+ }
225
+
226
+ secondModalButtonResumeEditingFunction.value = function () {}
227
+ thirdModalButtonResumeEditingFunction.value = async function () {
228
+ isLoadingResumeEditing.value = true
229
+ await delay(1000)
230
+ await pageBuilderClass.resumeEditingForUpdate()
231
+ isLoadingResumeEditing.value = false
232
+ showModalResumeEditing.value = false
233
+ }
234
+
235
+ // end modal
236
+ }
195
237
 
196
238
  onMounted(async () => {
197
239
  const config = getConfigPageBuilder.value
@@ -199,9 +241,15 @@ onMounted(async () => {
199
241
 
200
242
  pageBuilderClass.updateLocalStorageItemName()
201
243
 
244
+ pageBuilderClass.deleteOldPageBuilderLocalStorage()
245
+
202
246
  await pageBuilderClass.clearHtmlSelection()
203
247
 
204
248
  await pageBuilderClass.setEventListenersForElements()
249
+
250
+ if (await pageBuilderClass.hasLocalDraftForUpdate()) {
251
+ handlerRumeEditingForUpdate()
252
+ }
205
253
  })
206
254
  </script>
207
255
 
@@ -264,6 +312,24 @@ onMounted(async () => {
264
312
  <Preview></Preview>
265
313
  </ModalBuilder>
266
314
 
315
+ <DynamicModalBuilder
316
+ :showDynamicModalBuilder="showModalResumeEditing"
317
+ :isLoading="isLoadingResumeEditing"
318
+ :type="typeModal"
319
+ :gridColumnAmount="gridColumnModalResumeEditing"
320
+ :title="titleModalResumeEditing"
321
+ :description="descriptionModalResumeEditing"
322
+ :firstButtonText="firstButtonResumeEditing"
323
+ :secondButtonText="secondButtonResumeEditing"
324
+ :thirdButtonText="thirdButtonResumeEditing"
325
+ @firstModalButtonFunctionDynamicModalBuilder="firstModalButtonResumeEditingFunction"
326
+ @secondModalButtonFunctionDynamicModalBuilder="secondModalButtonResumeEditingFunction"
327
+ @thirdModalButtonFunctionDynamicModalBuilder="thirdModalButtonResumeEditingFunction"
328
+ >
329
+ <header></header>
330
+ <main></main>
331
+ </DynamicModalBuilder>
332
+
267
333
  <div>
268
334
  <div class="relative h-full flex pb-2 gap-2">
269
335
  <div
@@ -304,7 +370,7 @@ onMounted(async () => {
304
370
  class="flex myPrimaryGap items-center pt-4 pb-2 pl-2 h-24 w-full min-w-36"
305
371
  >
306
372
  <button
307
- class="myPrimaryButton h-6 flex gap-2"
373
+ class="mySecondaryButton h-6 flex gap-2"
308
374
  @click.stop="
309
375
  async () => {
310
376
  await pageBuilderClass.clearHtmlSelection()
@@ -333,6 +399,8 @@ onMounted(async () => {
333
399
  </div>
334
400
  <div>Save</div>
335
401
  </button>
402
+
403
+ <!-- Continue editing # end -->
336
404
  </div>
337
405
 
338
406
  <div
@@ -50,6 +50,8 @@ class PageBuilderClass {
50
50
  constructor(pageBuilderStateStore: ReturnType<typeof usePageBuilderStateStore>) {
51
51
  this.nextTick = nextTick()
52
52
 
53
+ this.hasStartedEditing = false
54
+
53
55
  this.containsPagebuilder = document.querySelector('#contains-pagebuilder')
54
56
 
55
57
  this.pageBuilderStateStore = pageBuilderStateStore
@@ -275,6 +277,7 @@ class PageBuilderClass {
275
277
  }
276
278
 
277
279
  handleAutoSave = async () => {
280
+ this.startEditing()
278
281
  const passedConfig = this.pageBuilderStateStore.getConfigPageBuilder
279
282
 
280
283
  // Check if config is set
@@ -312,6 +315,7 @@ class PageBuilderClass {
312
315
  }
313
316
 
314
317
  handleManualSave = async () => {
318
+ this.startEditing()
315
319
  const passedConfig = this.pageBuilderStateStore.getConfigPageBuilder
316
320
 
317
321
  // Check if config is set
@@ -958,7 +962,10 @@ class PageBuilderClass {
958
962
  }
959
963
 
960
964
  updateLocalStorageItemName(): void {
961
- const updateOrCreate = this.pageBuilderStateStore.getConfigPageBuilder?.updateOrCreate?.formType
965
+ const updateOrCreate =
966
+ this.pageBuilderStateStore.getConfigPageBuilder &&
967
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate &&
968
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType
962
969
 
963
970
  const resourceData = this.pageBuilderStateStore.getConfigPageBuilder?.resourceData
964
971
 
@@ -1132,7 +1139,7 @@ class PageBuilderClass {
1132
1139
  localStorage.setItem(
1133
1140
  this.getLocalStorageItemName.value,
1134
1141
  JSON.stringify({
1135
- savedAt: new Date().toISOString(),
1142
+ pageBuilderContentSavedAt: new Date().toISOString(),
1136
1143
  components: componentsToSave,
1137
1144
  }),
1138
1145
  )
@@ -1157,6 +1164,121 @@ class PageBuilderClass {
1157
1164
  }
1158
1165
  }
1159
1166
 
1167
+ //
1168
+ deleteOldPageBuilderLocalStorage(): void {
1169
+ if (
1170
+ this.pageBuilderStateStore.getConfigPageBuilder &&
1171
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate &&
1172
+ typeof this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'string' &&
1173
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'update'
1174
+ ) {
1175
+ let oldCountLocalStorages = 0
1176
+ const deletedItemsLog: { Number: number; Key: string; SavedAt: string }[] = []
1177
+
1178
+ // const pastTime = new Date(Date.now() - 1 * 60 * 1000) // 1 minute
1179
+ const pastTime = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000) // 2 weeks
1180
+
1181
+ for (let i = 0; i < localStorage.length; i++) {
1182
+ const key = localStorage.key(i)
1183
+
1184
+ if (!key) continue
1185
+ if (!key.startsWith('page-builder-update-resource-')) continue
1186
+
1187
+ try {
1188
+ const storeComponents = localStorage.getItem(key)
1189
+ if (!storeComponents) continue
1190
+
1191
+ const storeComponentsParsed = JSON.parse(storeComponents)
1192
+ const savedAt = storeComponentsParsed.pageBuilderContentSavedAt
1193
+ if (savedAt) {
1194
+ const savedAtDate = new Date(savedAt)
1195
+
1196
+ if (savedAtDate < pastTime) {
1197
+ oldCountLocalStorages++
1198
+ deletedItemsLog.push({
1199
+ Number: oldCountLocalStorages,
1200
+ Key: key,
1201
+ SavedAt: savedAt,
1202
+ })
1203
+
1204
+ // Delete old items
1205
+ localStorage.removeItem(key)
1206
+ }
1207
+ }
1208
+ } catch (e) {
1209
+ // Ignore parse errors for unrelated keys
1210
+ }
1211
+ }
1212
+
1213
+ if (deletedItemsLog.length > 0) {
1214
+ console.info(
1215
+ `Deleted ${deletedItemsLog.length} localStorage item(s) older than ${pastTime} days:`,
1216
+ )
1217
+ console.table(deletedItemsLog)
1218
+ }
1219
+ }
1220
+ }
1221
+
1222
+ async hasLocalDraftForUpdate(): boolean {
1223
+ if (this.hasStartedEditing) return false
1224
+
1225
+ if (
1226
+ this.pageBuilderStateStore.getConfigPageBuilder &&
1227
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate &&
1228
+ typeof this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'string' &&
1229
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'update'
1230
+ ) {
1231
+ const key = this.getLocalStorageItemName.value
1232
+ if (typeof key === 'string') {
1233
+ const draft = localStorage.getItem(key)
1234
+ if (draft) {
1235
+ try {
1236
+ await this.delay(1000)
1237
+ const draftParsed = JSON.parse(draft)
1238
+ const dbComponents = this.getComponents.value
1239
+ return JSON.stringify(draftParsed.components) !== JSON.stringify(dbComponents)
1240
+ } catch (e) {
1241
+ return false
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+ return false
1247
+ }
1248
+
1249
+ // Call this when the user starts editing (e.g., on first change or when resuming a draft)
1250
+ startEditing() {
1251
+ this.hasStartedEditing = true
1252
+ }
1253
+
1254
+ async resumeEditingForUpdate() {
1255
+ if (
1256
+ this.pageBuilderStateStore.getConfigPageBuilder &&
1257
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate &&
1258
+ typeof this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'string' &&
1259
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'update'
1260
+ ) {
1261
+ const key = this.getLocalStorageItemName.value
1262
+ if (typeof key === 'string') {
1263
+ const savedCurrentDesign = localStorage.getItem(key)
1264
+ if (savedCurrentDesign) {
1265
+ try {
1266
+ const parsed = JSON.parse(savedCurrentDesign)
1267
+ if (parsed && Array.isArray(parsed.components)) {
1268
+ this.pageBuilderStateStore.setComponents(parsed.components)
1269
+ localStorage.removeItem(key)
1270
+ await nextTick()
1271
+ await this.setEventListenersForElements()
1272
+ await this.handleAutoSave()
1273
+ }
1274
+ } catch (e) {
1275
+ console.error('Failed to parse local draft:', e)
1276
+ }
1277
+ }
1278
+ }
1279
+ }
1280
+ }
1281
+
1160
1282
  getStorageItemNameForResource(): string | null {
1161
1283
  return this.getLocalStorageItemName.value
1162
1284
  }
@@ -1165,6 +1287,7 @@ class PageBuilderClass {
1165
1287
 
1166
1288
  if (
1167
1289
  this.getLocalStorageItemName.value &&
1290
+ typeof this.getLocalStorageItemName.value === 'string' &&
1168
1291
  localStorage.getItem(this.getLocalStorageItemName.value)
1169
1292
  ) {
1170
1293
  const savedCurrentDesign = localStorage.getItem(this.getLocalStorageItemName.value)
@@ -1431,19 +1554,20 @@ class PageBuilderClass {
1431
1554
  }
1432
1555
  }
1433
1556
 
1434
- // Private method to parse JSON components and save savedAt to localStorage
1557
+ // Private method to parse JSON components and save pageBuilderContentSavedAt to localStorage
1435
1558
  #parseJSONComponents(jsonData: string): void {
1436
1559
  try {
1437
1560
  const parsedData = JSON.parse(jsonData)
1438
1561
  let componentsArray: ComponentObject[] = []
1439
- let savedAt: string | undefined = undefined
1562
+ let pageBuilderContentSavedAt: string | undefined = undefined
1440
1563
 
1441
1564
  // Support both old and new structure
1442
1565
  if (Array.isArray(parsedData)) {
1443
1566
  componentsArray = parsedData
1444
1567
  } else if (parsedData && Array.isArray(parsedData.components)) {
1445
1568
  componentsArray = parsedData.components
1446
- savedAt = parsedData.savedAt
1569
+
1570
+ pageBuilderContentSavedAt = parsedData.pageBuilderContentSavedAt
1447
1571
  }
1448
1572
 
1449
1573
  let savedCurrentDesign: ComponentObject[] = []
@@ -1479,9 +1603,9 @@ class PageBuilderClass {
1479
1603
 
1480
1604
  this.pageBuilderStateStore.setComponents(savedCurrentDesign)
1481
1605
 
1482
- // Save to localStorage with savedAt using the correct key
1606
+ // Save to localStorage with pageBuilderContentSavedAt using the correct key
1483
1607
  const dataToSave = {
1484
- savedAt: savedAt || new Date().toISOString(),
1608
+ pageBuilderContentSavedAt: parsedData.pageBuilderContentSavedAt || new Date().toISOString(),
1485
1609
  components: savedCurrentDesign,
1486
1610
  }
1487
1611
 
@@ -1549,13 +1673,23 @@ class PageBuilderClass {
1549
1673
 
1550
1674
  const storedData = this.areComponentsStoredInLocalStorage()
1551
1675
 
1552
- if (this.pageBuilderStateStore.getConfigPageBuilder?.updateOrCreate?.formType === 'create') {
1676
+ if (
1677
+ this.pageBuilderStateStore.getConfigPageBuilder &&
1678
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate &&
1679
+ typeof this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'string' &&
1680
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'create'
1681
+ ) {
1553
1682
  if (storedData) {
1554
1683
  this.setComponentsFromData(storedData)
1555
1684
  }
1556
1685
  }
1557
1686
 
1558
- if (this.pageBuilderStateStore.getConfigPageBuilder?.updateOrCreate?.formType === 'update') {
1687
+ if (
1688
+ this.pageBuilderStateStore.getConfigPageBuilder &&
1689
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate &&
1690
+ typeof this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'string' &&
1691
+ this.pageBuilderStateStore.getConfigPageBuilder.updateOrCreate.formType === 'update'
1692
+ ) {
1559
1693
  if (data) {
1560
1694
  this.setComponentsFromData(data)
1561
1695
  }