@myissue/vue-website-page-builder 3.4.17 → 3.4.18

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": "3.4.17",
3
+ "version": "3.4.18",
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",
@@ -61,7 +61,7 @@ watch(
61
61
 
62
62
  <ListboxButton
63
63
  v-if="!globalPageLayout"
64
- class="pbx-h-10 pbx-w-10 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
64
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
65
65
  >
66
66
  <div class="pbx-flex pbx-flex-col">
67
67
  <div class="pbx-flex pbx-gap-2 pbx-items-center">
@@ -188,6 +188,8 @@ const handleModalIframeSrc = function () {
188
188
  showModalIframeSrc.value = false
189
189
  }
190
190
  }
191
+
192
+ const openOptionsMoreOpen = ref(false)
191
193
  </script>
192
194
  <template v-if="getElement">
193
195
  <div>
@@ -264,88 +266,148 @@ const handleModalIframeSrc = function () {
264
266
  >
265
267
  </MediaLibraryModal>
266
268
 
267
- <p v-if="false" class="pbx-font-medium pbx-text-[10px] pbx-w-max lg:pbx-block pbx-hidden">
268
- Editing
269
- <span class="pbx-lowercase">&lt;{{ elementTag }}&gt;</span>
270
- </p>
271
- <div class="pbx-flex pbx-items-center pbx-justify-center pbx-gap-2" :class="{ '': getElement }">
272
- <template v-if="pageBuilderService.ElOrFirstChildIsIframe()">
273
- <div class="pbx-flex pbx-items-center pbx-justify-start pbx-gap-2 pbx-w-max">
274
- <button
275
- @click="handleModalIframeSrc"
276
- type="button"
277
- class="pbx-h-10 pbx-w-10 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
278
- >
279
- <span class="material-symbols-outlined"> play_circle </span>
280
- </button>
281
- </div>
282
- </template>
283
-
284
- <template
285
- v-if="
286
- pageBuilderService.isSelectedElementValidText() &&
287
- !pageBuilderService.ElOrFirstChildIsIframe()
288
- "
289
- >
290
- <div class="pbx-flex pbx-items-center pbx-justify-start pbx-gap-2 pbx-w-max">
291
- <button
292
- @click="handleModalPreviewTiptap"
293
- type="button"
294
- class="pbx-h-10 pbx-w-10 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
295
- >
296
- <span class="material-symbols-outlined"> edit </span>
297
- </button>
298
- </div>
299
- <TextColorEditor></TextColorEditor>
300
- </template>
301
-
302
- <template
303
- v-if="
304
- getElement &&
305
- getComponent &&
306
- getBasePrimaryImage &&
307
- !pageBuilderService.ElOrFirstChildIsIframe()
308
- "
269
+ <div class="pbx-select-none">
270
+ <p v-if="false" class="pbx-font-medium pbx-text-[10px] pbx-w-max lg:pbx-block pbx-hidden">
271
+ Editing
272
+ <span class="pbx-lowercase">&lt;{{ elementTag }}&gt;</span>
273
+ </p>
274
+ <div
275
+ class="pbx-flex pbx-items-center pbx-justify-center pbx-gap-2"
276
+ :class="{ '': getElement }"
309
277
  >
310
- <div class="pbx-flex pbx-items-center pbx-justify-start pbx-gap-2 pbx-w-max">
278
+ <template v-if="pageBuilderService.ElOrFirstChildIsIframe()">
279
+ <div class="pbx-flex pbx-items-center pbx-justify-start pbx-gap-2 pbx-w-max">
280
+ <button
281
+ @click="handleModalIframeSrc"
282
+ type="button"
283
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
284
+ >
285
+ <span class="material-symbols-outlined"> play_circle </span>
286
+ </button>
287
+ </div>
288
+ </template>
289
+
290
+ <template
291
+ v-if="
292
+ pageBuilderService.isSelectedElementValidText() &&
293
+ !pageBuilderService.ElOrFirstChildIsIframe()
294
+ "
295
+ >
296
+ <div class="pbx-flex pbx-items-center pbx-justify-start pbx-gap-2 pbx-w-max">
297
+ <button
298
+ @click="handleModalPreviewTiptap"
299
+ type="button"
300
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
301
+ >
302
+ <span class="material-symbols-outlined"> edit </span>
303
+ </button>
304
+ </div>
305
+ <TextColorEditor></TextColorEditor>
306
+ </template>
307
+
308
+ <template
309
+ v-if="
310
+ getElement &&
311
+ getComponent &&
312
+ getBasePrimaryImage &&
313
+ !pageBuilderService.ElOrFirstChildIsIframe()
314
+ "
315
+ >
316
+ <div class="pbx-flex pbx-items-center pbx-justify-start pbx-gap-2 pbx-w-max">
317
+ <button
318
+ @click="handleAddImage"
319
+ type="button"
320
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
321
+ >
322
+ <span class="material-symbols-outlined"> add_photo_alternate </span>
323
+ </button>
324
+ </div>
325
+ </template>
326
+
327
+ <template
328
+ v-if="
329
+ getElement &&
330
+ getElement.nodeType === 1 &&
331
+ !getBasePrimaryImage &&
332
+ !pageBuilderService.ElOrFirstChildIsIframe()
333
+ "
334
+ >
335
+ <BackgroundColorEditor></BackgroundColorEditor>
336
+ </template>
337
+
338
+ <template v-if="getElement">
311
339
  <button
312
- @click="handleAddImage"
340
+ @click="pageBuilderService.deleteElementFromDOM"
313
341
  type="button"
314
- class="pbx-h-10 pbx-w-10 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
342
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
315
343
  >
316
- <span class="material-symbols-outlined"> add_photo_alternate </span>
344
+ <span class="material-symbols-outlined"> delete </span>
317
345
  </button>
346
+ </template>
347
+
348
+ <div
349
+ v-if="getElement && getComponent"
350
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
351
+ @click="openOptionsMoreOpen = !openOptionsMoreOpen"
352
+ >
353
+ <span class="material-symbols-outlined"> more_horiz </span>
318
354
  </div>
319
- </template>
320
-
321
- <template
322
- v-if="
323
- getElement &&
324
- getElement.nodeType === 1 &&
325
- !getBasePrimaryImage &&
326
- !pageBuilderService.ElOrFirstChildIsIframe()
327
- "
328
- >
329
- <BackgroundColorEditor></BackgroundColorEditor>
330
- </template>
331
-
332
- <template v-if="getElement">
333
- <button
334
- @click="pageBuilderService.deleteElementFromDOM"
335
- type="button"
336
- class="pbx-h-10 pbx-w-10 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
355
+ <div
356
+ v-if="getElement && getComponent"
357
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
358
+ @click="pageBuilderService.clearHtmlSelection()"
337
359
  >
338
- <span class="material-symbols-outlined"> delete </span>
339
- </button>
340
- </template>
360
+ <span class="material-symbols-outlined"> close_small</span>
361
+ </div>
362
+ </div>
363
+ </div>
341
364
 
365
+ <transition name="popup-fade">
342
366
  <div
343
- v-if="getElement && getComponent"
344
- class="pbx-h-10 pbx-w-10 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
345
- @click="pageBuilderService.clearHtmlSelection()"
367
+ v-if="openOptionsMoreOpen"
368
+ class="pbx-fixed pbx-z-40 pbx-top-10 pbx-right-0 lg:pbx-w-72 md:pbx-w-64 pbx-w-full pbx-select-none"
346
369
  >
347
- <span class="material-symbols-outlined"> close</span>
370
+ <div
371
+ class="pbx-rounded-3xl pbx-border pbx-border-gray-100 pbx-bg-white pbx-shadow-lg pbx-pt-4 pbx-pb-4 pbx-flex pbx-flex-col pbx-overflow-y-auto pbx-max-h-[80vh] pbx-mx-4 pbx-px-2"
372
+ >
373
+ <div
374
+ class="pbx-flex pbx-gap-2 pbx-items-center pbx-justify-between pbx-border-b pbx-border-gray-200 pbx-pb-4 pbx-pl-2"
375
+ >
376
+ <span class="pbx-text-black pbx-font-medium">Options</span>
377
+
378
+ <!-- Close Modal start -->
379
+ <div
380
+ @click="openOptionsMoreOpen = !openOptionsMoreOpen"
381
+ class="pbx-h-8 pbx-w-8 pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-black pbx-text-white pbx-aspect-square hover:pbx-fill-white focus-visible:pbx-ring-0 hover:pbx-outline-3 hover:pbx-outline-offset-2 hover:pbx-outline-black pbx-transition-all pbx-duration-100"
382
+ >
383
+ <span class="material-symbols-outlined"> close_small </span>
384
+ </div>
385
+ <!-- Close Modal Start -->
386
+ </div>
387
+
388
+ <div class="pbx-flex pbx-flex-col pbx-gap-4 pbx-mt-6">
389
+ <!-- content start -->
390
+ <div
391
+ v-if="getElement && getComponent"
392
+ @click="
393
+ () => {
394
+ openOptionsMoreOpen = !openOptionsMoreOpen
395
+ pageBuilderService.duplicateComponent()
396
+ }
397
+ "
398
+ class="pbx-flex pbx-items-center pbx-justify-start pbx-gap-2 pbx-cursor-pointer"
399
+ >
400
+ <div
401
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
402
+ >
403
+ <span class="material-symbols-outlined"> control_point_duplicate </span>
404
+ </div>
405
+ <div class="pbx-text-sm">Duplicate component</div>
406
+ </div>
407
+ <!-- content end -->
408
+ </div>
409
+ </div>
348
410
  </div>
349
- </div>
411
+ </transition>
350
412
  </div>
351
413
  </template>
@@ -63,7 +63,7 @@ watch(
63
63
 
64
64
  <ListboxButton
65
65
  v-if="!globalPageLayout"
66
- class="pbx-h-10 pbx-w-10 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
66
+ class="pbx-h-8 pbx-w-8 pbx-flex-end pbx-cursor-pointer pbx-rounded-full pbx-flex pbx-items-center pbx-border-none pbx-justify-center pbx-bg-gray-200 pbx-aspect-square hover:pbx-bg-gray-100 hover:pbx-fill-white focus-visible:pbx-ring-0 pbx-text-myPrimaryDarkGrayColor"
67
67
  >
68
68
  <div class="pbx-flex pbx-flex-col">
69
69
  <div class="pbx-flex pbx-gap-2 pbx-items-center">
@@ -869,7 +869,7 @@ onMounted(async () => {
869
869
  >
870
870
  <div
871
871
  id="pbxEditToolbar"
872
- class="pbx-z-30 lg:pbx-mx-20 pbx-flex pbx-gap-2 pbx-justify-center pbx-items-center pbx-rounded pbx-px-4 pbx-bg-red-200 pbx-h-0"
872
+ class="pbx-z-30 lg:pbx-mx-20 pbx-flex pbx-gap-2 pbx-justify-center pbx-items-center pbx-rounded-full pbx-px-4 pbx-h-0"
873
873
  >
874
874
  <template v-if="getElement">
875
875
  <EditGetElement></EditGetElement>
@@ -5,3 +5,20 @@ These styles affect all HTML elements (like input, button, h1, etc.) in the cons
5
5
  .mobile-preview-frame {
6
6
  container-type: inline-size;
7
7
  }
8
+
9
+ /* popover start */
10
+ .popup-fade-enter-active,
11
+ .popup-fade-leave-active {
12
+ transition: all 0.1s cubic-bezier(0.4, 0, 0.2, 1);
13
+ }
14
+ .popup-fade-enter-from,
15
+ .popup-fade-leave-to {
16
+ transform: scale(0.95);
17
+ opacity: 0;
18
+ }
19
+ .popup-fade-enter-to,
20
+ .popup-fade-leave-from {
21
+ transform: scale(1);
22
+ opacity: 1;
23
+ }
24
+ /* popover end */
package/src/css/style.css CHANGED
@@ -367,9 +367,12 @@
367
367
  min-width: 4rem;
368
368
  padding-right: 1rem;
369
369
  padding-left: 1rem;
370
- height: 2rem;
371
- box-shadow: 0 0 0 10px oklch(86.9% 0.005 56.366);
372
- background: oklch(86.9% 0.005 56.366);
370
+ height: 1.5rem;
371
+ background: #fff; /* white background */
372
+ /* white outline first, then black border simulated as shadow */
373
+ box-shadow:
374
+ 0 0 0 10px #fff,
375
+ /* white outline */ 0 0 0 12px #000; /* black border outside */
373
376
  }
374
377
 
375
378
  /* CSS for content inside page builder # start */
@@ -804,18 +804,19 @@ export class PageBuilderService {
804
804
  ) {
805
805
  const heading = element.children[0] as HTMLElement
806
806
 
807
- element.classList.forEach((cls) => {
808
- if (this.fontSizeRegex.test(cls)) {
809
- element.classList.remove(cls)
810
- }
811
- })
807
+ // Only add default font size classes if none exist
808
+ const hasFontSizeClass = Array.from(element.classList).some((cls) =>
809
+ this.fontSizeRegex.test(cls),
810
+ )
812
811
 
813
- // Apply responsive font size classes based on heading type
814
- if (heading.tagName === 'H2') {
815
- element.classList.add('pbx-text-2xl', 'lg:pbx-text-4xl', 'pbx-font-medium')
816
- }
817
- if (heading.tagName === 'H3') {
818
- element.classList.add('pbx-text-1xl', 'lg:pbx-text-3xl', 'pbx-font-medium')
812
+ if (!hasFontSizeClass) {
813
+ // Apply responsive font size classes based on heading type
814
+ if (heading.tagName === 'H2') {
815
+ element.classList.add('pbx-text-2xl', 'lg:pbx-text-4xl', 'pbx-font-medium')
816
+ }
817
+ if (heading.tagName === 'H3') {
818
+ element.classList.add('pbx-text-1xl', 'lg:pbx-text-3xl', 'pbx-font-medium')
819
+ }
819
820
  }
820
821
  }
821
822
  }
@@ -1508,6 +1509,49 @@ export class PageBuilderService {
1508
1509
  return !this.hasVisibleContent(section)
1509
1510
  }
1510
1511
 
1512
+ /**
1513
+ * Duplicate the currently selected component from the DOM and the state.
1514
+ * @returns {Promise<void>}
1515
+ */
1516
+ public async duplicateComponent() {
1517
+ // Sync latest DOM changes to the store
1518
+ this.syncDomToStoreOnly()
1519
+ await nextTick()
1520
+
1521
+ const components = this.pageBuilderStateStore.getComponents
1522
+ const selectedComponent = this.getComponent.value
1523
+
1524
+ if (!components || !selectedComponent) return
1525
+
1526
+ // Find the index of the selected component
1527
+ const index = components.findIndex(
1528
+ (component: ComponentObject) => component.id === selectedComponent.id,
1529
+ )
1530
+ if (index === -1) return
1531
+
1532
+ // Clone the component and generate a new id
1533
+ const clonedComponent = this.cloneCompObjForDOMInsertion(components[index])
1534
+
1535
+ // Insert the cloned component right after the selected one
1536
+ const newComponents = [
1537
+ ...components.slice(0, index + 1),
1538
+ clonedComponent,
1539
+ ...components.slice(index + 1),
1540
+ ]
1541
+
1542
+ this.pageBuilderStateStore.setComponents(newComponents)
1543
+
1544
+ // Wait for DOM update and re-attach listeners
1545
+ await nextTick()
1546
+ await this.addListenersToEditableElements()
1547
+
1548
+ // Optionally, select the new duplicated component
1549
+ this.pageBuilderStateStore.setComponent(clonedComponent)
1550
+ this.pageBuilderStateStore.setElement(null)
1551
+
1552
+ // Auto-save after duplication
1553
+ await this.handleAutoSave()
1554
+ }
1511
1555
  /**
1512
1556
  * Deletes the currently selected component from the DOM and the state.
1513
1557
  * @returns {Promise<void>}
@@ -1521,8 +1565,8 @@ export class PageBuilderService {
1521
1565
  if (!components) return
1522
1566
 
1523
1567
  // Find the index of the component to be deleted.
1524
- const indexToDelete = components.findIndex(
1525
- (component: ComponentObject) => component.id === this.getComponent.value?.id,
1568
+ const indexToDelete = components.findIndex((component: ComponentObject) =>
1569
+ this.getComponent.value ? component.id === this.getComponent.value.id : false,
1526
1570
  )
1527
1571
 
1528
1572
  if (indexToDelete === -1) {
@@ -1538,15 +1582,6 @@ export class PageBuilderService {
1538
1582
 
1539
1583
  this.pageBuilderStateStore.setComponents(newComponents)
1540
1584
 
1541
- // Remove the component's corresponding section from the DOM.
1542
- const pagebuilder = document.querySelector('#pagebuilder')
1543
- if (pagebuilder && this.getComponent.value?.id) {
1544
- const section = pagebuilder.querySelector(
1545
- `section[data-componentid="${this.getComponent.value.id}"]`,
1546
- )
1547
- if (section) section.remove()
1548
- }
1549
-
1550
1585
  // Wait for the DOM to update before re-attaching event listeners.
1551
1586
  await nextTick()
1552
1587
  await this.addListenersToEditableElements()